From 5477f609de6d32b82b096e5611e8379937383f9b Mon Sep 17 00:00:00 2001 From: Shahzad Date: Tue, 19 Oct 2021 21:49:34 +0200 Subject: [PATCH 01/13] [Uptime] Added trends chart and deep link to exp view on waterfall step metric markers (#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 * 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 --- .../configurations/lens_attributes.ts | 50 ++++++++- .../synthetics/kpi_over_time_config.ts | 20 ++-- .../embeddable/embeddable.tsx | 23 +++- .../columns/operation_type_select.tsx | 6 + .../common/runtime_types/ping/synthetics.ts | 4 + .../step_detail/step_detail_container.tsx | 6 +- .../waterfall/waterfall_chart_container.tsx | 5 +- .../waterfall/waterfall_chart_wrapper.tsx | 10 +- .../components/waterfall_marker_icon.tsx | 38 +++++++ .../components/waterfall_marker_trend.tsx | 106 ++++++++++++++++++ .../components/waterfall_markers.tsx | 44 ++++++-- .../waterfall/context/waterfall_chart.tsx | 5 + .../uptime_startup_plugins_context.tsx | 4 +- 13 files changed, 293 insertions(+), 28 deletions(-) create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_marker_icon.tsx create mode 100644 x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.tsx diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts index a31bef7c9c214..5e769882a2793 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/lens_attributes.ts @@ -14,6 +14,7 @@ import { AvgIndexPatternColumn, MedianIndexPatternColumn, PercentileIndexPatternColumn, + LastValueIndexPatternColumn, OperationType, PersistedIndexPatternLayer, RangeIndexPatternColumn, @@ -219,6 +220,15 @@ 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!); } @@ -226,6 +236,36 @@ export class LensAttributes { 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, @@ -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}`, @@ -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 } : {}), diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts index 8951ffcda63d8..e548ec2714e14 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/configurations/synthetics/kpi_over_time_config.ts @@ -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 = { diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx index 10ec4075a8155..35ce2fc6c1a47 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/embeddable.tsx @@ -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'; @@ -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 { @@ -37,6 +39,8 @@ export default function Embeddable({ appendTitle, indexPatterns, lens, + axisTitlesVisibility, + legendIsVisible, showCalculationMethod = false, }: ExploratoryEmbeddableComponentProps) { const LensComponent = lens?.EmbeddableComponent; @@ -57,11 +61,20 @@ export default function Embeddable({ return No lens component; } + const attributesJSON = lensAttributes.getJSON(); + + (attributesJSON.state.visualization as XYState).axisTitlesVisibilitySettings = + axisTitlesVisibility; + + if (typeof legendIsVisible !== 'undefined') { + (attributesJSON.state.visualization as XYState).legend.isVisible = legendIsVisible; + } + return ( - + - +

{title}

@@ -81,7 +94,7 @@ export default function Embeddable({ id="exploratoryView" style={{ height: '100%' }} timeRange={series?.time} - attributes={lensAttributes.getJSON()} + attributes={attributesJSON} onBrushEnd={({ range }) => {}} />
@@ -92,7 +105,7 @@ const Wrapper = styled.div` height: 100%; &&& { > :nth-child(2) { - height: calc(100% - 56px); + height: calc(100% - 32px); } } `; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx index 6d83e25cc96e3..a223a74d74aea 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/series_editor/columns/operation_type_select.tsx @@ -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', { diff --git a/x-pack/plugins/uptime/common/runtime_types/ping/synthetics.ts b/x-pack/plugins/uptime/common/runtime_types/ping/synthetics.ts index e7948f4ad532c..7b181ac2cf50c 100644 --- a/x-pack/plugins/uptime/common/runtime_types/ping/synthetics.ts +++ b/x-pack/plugins/uptime/common/runtime_types/ping/synthetics.ts @@ -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({ diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx index 777491c503fd9..1e493ad21b4d3 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/step_detail_container.tsx @@ -45,7 +45,11 @@ export const StepDetailContainer: React.FC = ({ checkGroup, stepIndex }) )} {journey && activeStep && !journey.loading && ( - + )} ); diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx index d249c23c44d75..95da0ea0a45f6 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_container.tsx @@ -15,6 +15,7 @@ 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', @@ -22,10 +23,11 @@ export const NO_DATA_TEXT = i18n.translate('xpack.uptime.synthetics.stepDetail.w interface Props { checkGroup: string; + activeStep?: JourneyStep; stepIndex: number; } -export const WaterfallChartContainer: React.FC = ({ checkGroup, stepIndex }) => { +export const WaterfallChartContainer: React.FC = ({ checkGroup, stepIndex, activeStep }) => { const dispatch = useDispatch(); useEffect(() => { @@ -79,6 +81,7 @@ export const WaterfallChartContainer: React.FC = ({ checkGroup, stepIndex data={extractItems(networkEvents.events)} markerItems={metrics} total={networkEvents.total} + activeStep={activeStep} /> )} {waterfallLoaded && hasEvents && !isWaterfallSupported && ( diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx index 8071fd1e3c4d3..aaa3d5c1cc23c 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/step_detail/waterfall/waterfall_chart_wrapper.tsx @@ -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 = (item) => { return ( @@ -26,11 +27,17 @@ export const renderLegendItem: RenderItem = (item) => { interface Props { total: number; + activeStep?: JourneyStep; data: NetworkItems; markerItems?: MarkerItems; } -export const WaterfallChartWrapper: React.FC = ({ data, total, markerItems }) => { +export const WaterfallChartWrapper: React.FC = ({ + data, + total, + markerItems, + activeStep, +}) => { const [query, setQuery] = useState(''); const [activeFilters, setActiveFilters] = useState([]); const [onlyHighlighted, setOnlyHighlighted] = useState(false); @@ -109,6 +116,7 @@ export const WaterfallChartWrapper: React.FC = ({ data, total, markerItem return ( ; + } + + return ( + setIsOpen(false)} + anchorPosition="downLeft" + panelStyle={{ paddingBottom: 0, paddingLeft: 4 }} + zIndex={100} + button={ + setIsOpen((prevState) => !prevState)} + /> + } + > + + + ); +} diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.tsx new file mode 100644 index 0000000000000..6ff7835633914 --- /dev/null +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_marker_trend.tsx @@ -0,0 +1,106 @@ +/* + * 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 { EuiButton } from '@elastic/eui'; +import { i18n } from '@kbn/i18n'; +import moment from 'moment'; +import { useUptimeStartPlugins } from '../../../../../contexts/uptime_startup_plugins_context'; +import { useUptimeSettingsContext } from '../../../../../contexts/uptime_settings_context'; +import { AllSeries, createExploratoryViewUrl } from '../../../../../../../observability/public'; +import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; +import { useWaterfallContext } from '../context/waterfall_chart'; +import { JourneyStep } from '../../../../../../common/runtime_types'; + +const getLast48Intervals = (activeStep: JourneyStep) => { + const { lt, gte } = activeStep.monitor.timespan!; + const inDays = moment(lt).diff(moment(gte), 'days'); + if (inDays > 0) { + return { to: 'now', from: `now-${inDays * 48}d` }; + } + + const inHours = moment(lt).diff(moment(gte), 'hours'); + if (inHours > 0) { + return { to: 'now', from: `now-${inHours * 48}h` }; + } + + const inMinutes = moment(lt).diff(moment(gte), 'minutes'); + if (inMinutes > 0) { + return { to: 'now', from: `now-${inMinutes * 48}m` }; + } + + const inSeconds = moment(lt).diff(moment(gte), 'seconds'); + return { to: 'now', from: `now-${inSeconds * 48}s` }; +}; + +export function WaterfallMarkerTrend({ title, field }: { title: string; field: string }) { + const { observability } = useUptimeStartPlugins(); + + const EmbeddableExpVIew = observability!.ExploratoryViewEmbeddable; + + const { basePath } = useUptimeSettingsContext(); + + const { activeStep } = useWaterfallContext(); + + if (!activeStep) { + return null; + } + + const allSeries: AllSeries = [ + { + name: `${title}(${activeStep.synthetics.step?.name!})`, + selectedMetricField: field, + time: getLast48Intervals(activeStep), + seriesType: 'area', + dataType: 'synthetics', + reportDefinitions: { + 'monitor.name': [activeStep.monitor.name!], + 'synthetics.step.name.keyword': [activeStep.synthetics.step?.name!], + }, + operationType: 'last_value', + }, + ]; + + const href = createExploratoryViewUrl( + { + reportType: 'kpi-over-time', + allSeries, + }, + basePath + ); + + return ( + + + {EXPLORE_LABEL} + + } + reportType={'kpi-over-time'} + attributes={allSeries} + axisTitlesVisibility={{ x: false, yLeft: false, yRight: false }} + legendIsVisible={false} + /> + + ); +} + +export const EXPLORE_LABEL = i18n.translate('xpack.uptime.synthetics.markers.explore', { + defaultMessage: 'Explore', +}); + +const Wrapper = euiStyled.div` + height: 200px; + width: 400px; + &&& { + .expExpressionRenderer__expression { + padding-bottom: 0 !important; + } + } +`; diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_markers.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_markers.tsx index b341b052e0102..d8f6468015ede 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_markers.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/components/waterfall_markers.tsx @@ -7,11 +7,11 @@ import React from 'react'; import { AnnotationDomainType, LineAnnotation } from '@elastic/charts'; -import { EuiIcon } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useWaterfallContext } from '..'; import { useTheme } from '../../../../../../../observability/public'; import { euiStyled } from '../../../../../../../../../src/plugins/kibana_react/common'; +import { WaterfallMarkerIcon } from './waterfall_marker_icon'; export const FCP_LABEL = i18n.translate('xpack.uptime.synthetics.waterfall.fcpLabel', { defaultMessage: 'First contentful paint', @@ -39,6 +39,12 @@ export const DOCUMENT_CONTENT_LOADED_LABEL = i18n.translate( } ); +export const SYNTHETICS_CLS = 'browser.experience.cls'; +export const SYNTHETICS_LCP = 'browser.experience.lcp.us'; +export const SYNTHETICS_FCP = 'browser.experience.fcp.us'; +export const SYNTHETICS_DOCUMENT_ONLOAD = 'browser.experience.load.us'; +export const SYNTHETICS_DCL = 'browser.experience.dcl.us'; + export function WaterfallChartMarkers() { const { markerItems } = useWaterfallContext(); @@ -48,12 +54,32 @@ export function WaterfallChartMarkers() { return null; } - const markersInfo: Record = { - domContentLoaded: { label: DOCUMENT_CONTENT_LOADED_LABEL, color: theme.eui.euiColorVis0 }, - firstContentfulPaint: { label: FCP_LABEL, color: theme.eui.euiColorVis1 }, - largestContentfulPaint: { label: LCP_LABEL, color: theme.eui.euiColorVis2 }, - layoutShift: { label: LAYOUT_SHIFT_LABEL, color: theme.eui.euiColorVis3 }, - loadEvent: { label: LOAD_EVENT_LABEL, color: theme.eui.euiColorVis9 }, + const markersInfo: Record = { + domContentLoaded: { + label: DOCUMENT_CONTENT_LOADED_LABEL, + color: theme.eui.euiColorVis0, + field: SYNTHETICS_DCL, + }, + firstContentfulPaint: { + label: FCP_LABEL, + color: theme.eui.euiColorVis1, + field: SYNTHETICS_FCP, + }, + largestContentfulPaint: { + label: LCP_LABEL, + color: theme.eui.euiColorVis2, + field: SYNTHETICS_LCP, + }, + layoutShift: { + label: LAYOUT_SHIFT_LABEL, + color: theme.eui.euiColorVis3, + field: SYNTHETICS_CLS, + }, + loadEvent: { + label: LOAD_EVENT_LABEL, + color: theme.eui.euiColorVis9, + field: SYNTHETICS_DOCUMENT_ONLOAD, + }, }; return ( @@ -73,7 +99,9 @@ export function WaterfallChartMarkers() { }), }, ]} - marker={} + marker={ + + } style={{ line: { strokeWidth: 2, diff --git a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx index cce0533293e07..d495b7432bce7 100644 --- a/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx +++ b/x-pack/plugins/uptime/public/components/monitor/synthetics/waterfall/context/waterfall_chart.tsx @@ -9,6 +9,7 @@ import React, { createContext, useContext, Context } from 'react'; import { WaterfallData, WaterfallDataEntry, WaterfallMetadata } from '../types'; import { OnSidebarClick, OnElementClick, OnProjectionClick } from '../components/use_flyout'; import { SidebarItems } from '../../step_detail/waterfall/types'; +import { JourneyStep } from '../../../../../../common/runtime_types'; export type MarkerItems = Array<{ id: @@ -38,6 +39,7 @@ export interface IWaterfallContext { index?: number ) => JSX.Element; markerItems?: MarkerItems; + activeStep?: JourneyStep; } export const WaterfallContext = createContext>({}); @@ -56,6 +58,7 @@ interface ProviderProps { metadata: IWaterfallContext['metadata']; renderTooltipItem: IWaterfallContext['renderTooltipItem']; markerItems?: MarkerItems; + activeStep?: JourneyStep; } export const WaterfallProvider: React.FC = ({ @@ -73,11 +76,13 @@ export const WaterfallProvider: React.FC = ({ totalNetworkRequests, highlightedNetworkRequests, fetchedNetworkRequests, + activeStep, }) => { return ( >({}); @@ -14,3 +14,5 @@ export const UptimeStartupPluginsContextProvider: React.FC ; + +export const useUptimeStartPlugins = () => useContext(UptimeStartupPluginsContext); From a22066ef41a7d58cc23899a3b9b4126a83b53fca Mon Sep 17 00:00:00 2001 From: Michael Olorunnisola Date: Tue, 19 Oct 2021 16:18:17 -0400 Subject: [PATCH 02/13] [Security Solution] Fix height issue (#114718) Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../public/components/t_grid/body/height_hack.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/x-pack/plugins/timelines/public/components/t_grid/body/height_hack.ts b/x-pack/plugins/timelines/public/components/t_grid/body/height_hack.ts index 5371d7004a864..26d32b13eede7 100644 --- a/x-pack/plugins/timelines/public/components/t_grid/body/height_hack.ts +++ b/x-pack/plugins/timelines/public/components/t_grid/body/height_hack.ts @@ -35,6 +35,11 @@ const DATA_GRID_HEIGHT_BY_PAGE_SIZE: { [key: number]: number } = { * * Please delete me and allow DataGrid to calculate its height when the bug is fixed. */ + +const dataGridRowHeight = 36; +const headerSectionHeight = 32; +const additionalFiltersHeight = 44; + export const useDataGridHeightHack = (pageSize: number, rowCount: number) => { const [height, setHeight] = useState(DATA_GRID_HEIGHT_BY_PAGE_SIZE[pageSize]); @@ -44,7 +49,11 @@ export const useDataGridHeightHack = (pageSize: number, rowCount: number) => { if (rowCount === pageSize) { setHeight(DATA_GRID_HEIGHT_BY_PAGE_SIZE[pageSize]); + } else if (rowCount <= pageSize) { + // This is unnecessary if we add rowCount > pageSize below + setHeight(dataGridRowHeight * rowCount + (headerSectionHeight + additionalFiltersHeight)); } else if ( + // rowCount > pageSize && // This will fix the issue but is always full height so has a lot of empty state gridVirtualized && gridVirtualized.children[0].clientHeight !== gridVirtualized.clientHeight // check if it has vertical scroll ) { From 0b0d49ef5125f0551d4c6ddfc52f6bccc76df153 Mon Sep 17 00:00:00 2001 From: Zacqary Adam Xeper Date: Tue, 19 Oct 2021 15:51:39 -0500 Subject: [PATCH 03/13] [Logs/Metrics UI] Remove configurable fields in settings for 8.0.0 (#61302) * [Logs/Metrics UI] Remove configurable fields in settings for 8.0.0 * Fix logs fields * Fix i18n * Fix typecheck and i18n * Fix i18n Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../settings/fields_configuration_panel.tsx | 190 ------------ .../indices_configuration_form_state.ts | 43 +-- .../indices_configuration_panel.stories.tsx | 24 +- .../settings/indices_configuration_panel.tsx | 71 ++--- .../source_configuration_form_state.tsx | 18 +- .../source_configuration_settings.tsx | 4 - .../settings/fields_configuration_panel.tsx | 274 ------------------ .../indices_configuration_form_state.ts | 80 +---- .../source_configuration_form_state.tsx | 19 -- .../source_configuration_settings.tsx | 12 - .../translations/translations/ja-JP.json | 20 -- .../translations/translations/zh-CN.json | 20 -- 12 files changed, 33 insertions(+), 742 deletions(-) delete mode 100644 x-pack/plugins/infra/public/pages/logs/settings/fields_configuration_panel.tsx delete mode 100644 x-pack/plugins/infra/public/pages/metrics/settings/fields_configuration_panel.tsx diff --git a/x-pack/plugins/infra/public/pages/logs/settings/fields_configuration_panel.tsx b/x-pack/plugins/infra/public/pages/logs/settings/fields_configuration_panel.tsx deleted file mode 100644 index d21b0b0588dea..0000000000000 --- a/x-pack/plugins/infra/public/pages/logs/settings/fields_configuration_panel.tsx +++ /dev/null @@ -1,190 +0,0 @@ -/* - * 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 { - EuiCallOut, - EuiCode, - EuiDescribedFormGroup, - EuiFieldText, - EuiFormRow, - EuiLink, - EuiSpacer, - EuiTitle, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import React, { useMemo } from 'react'; -import { FormElement } from './form_elements'; -import { getFormRowProps, getStringInputFieldProps } from './form_field_props'; -import { FormValidationError } from './validation_errors'; - -interface FieldsConfigurationPanelProps { - isLoading: boolean; - isReadOnly: boolean; - tiebreakerFieldFormElement: FormElement; - timestampFieldFormElement: FormElement; -} - -export const FieldsConfigurationPanel = ({ - isLoading, - isReadOnly, - tiebreakerFieldFormElement, - timestampFieldFormElement, -}: FieldsConfigurationPanelProps) => { - const isTimestampValueDefault = timestampFieldFormElement.value === '@timestamp'; - const isTiebreakerValueDefault = tiebreakerFieldFormElement.value === '_doc'; - - return ( - <> - -

- -

-
- - -

- - - - ), - ecsLink: ( - - ECS - - ), - }} - /> -

-
- - - - - } - description={ - - } - > - @timestamp, - }} - /> - } - label={ - - } - {...useMemo( - () => getFormRowProps(timestampFieldFormElement), - [timestampFieldFormElement] - )} - > - getStringInputFieldProps(timestampFieldFormElement), - [timestampFieldFormElement] - )} - /> - - - - - - } - description={ - - } - > - _doc, - }} - /> - } - label={ - - } - {...useMemo( - () => getFormRowProps(tiebreakerFieldFormElement), - [tiebreakerFieldFormElement] - )} - > - getStringInputFieldProps(tiebreakerFieldFormElement), - [tiebreakerFieldFormElement] - )} - /> - - - - ); -}; diff --git a/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_form_state.ts b/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_form_state.ts index 1a70aaff6636c..31f4b96c92379 100644 --- a/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_form_state.ts +++ b/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_form_state.ts @@ -14,7 +14,7 @@ import { LogIndexPatternReference, } from '../../../../common/log_sources'; import { useKibanaIndexPatternService } from '../../../hooks/use_kibana_index_patterns'; -import { useCompositeFormElement, useFormElement } from './form_elements'; +import { useFormElement } from './form_elements'; import { FormValidationError, validateIndexPattern, @@ -80,44 +80,3 @@ export const useLogIndicesFormElement = (initialValue: LogIndicesFormState) => { return logIndicesFormElement; }; - -export interface FieldsFormState { - tiebreakerField: string; - timestampField: string; -} - -export const useFieldsFormElement = (initialValues: FieldsFormState) => { - const tiebreakerFieldFormElement = useFormElement({ - initialValue: initialValues.tiebreakerField, - validate: useMemo( - () => async (tiebreakerField) => validateStringNotEmpty('tiebreaker', tiebreakerField), - [] - ), - }); - - const timestampFieldFormElement = useFormElement({ - initialValue: initialValues.timestampField, - validate: useMemo( - () => async (timestampField) => validateStringNotEmpty('timestamp', timestampField), - [] - ), - }); - - const fieldsFormElement = useCompositeFormElement( - useMemo( - () => ({ - childFormElements: { - tiebreaker: tiebreakerFieldFormElement, - timestamp: timestampFieldFormElement, - }, - }), - [tiebreakerFieldFormElement, timestampFieldFormElement] - ) - ); - - return { - fieldsFormElement, - tiebreakerFieldFormElement, - timestampFieldFormElement, - }; -}; diff --git a/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_panel.stories.tsx b/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_panel.stories.tsx index 546bb9aab0f33..cf0f302136fbf 100644 --- a/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_panel.stories.tsx +++ b/x-pack/plugins/infra/public/pages/logs/settings/indices_configuration_panel.stories.tsx @@ -15,12 +15,7 @@ import { MockIndexPatternsKibanaContextProvider, MockIndexPatternSpec, } from '../../../hooks/use_kibana_index_patterns.mock'; -import { - FieldsFormState, - LogIndicesFormState, - useFieldsFormElement, - useLogIndicesFormElement, -} from './indices_configuration_form_state'; +import { LogIndicesFormState, useLogIndicesFormElement } from './indices_configuration_form_state'; import { IndicesConfigurationPanel } from './indices_configuration_panel'; export default { @@ -69,17 +64,14 @@ type IndicesConfigurationPanelStoryArgs = Pick< > & { availableIndexPatterns: MockIndexPatternSpec[]; logIndices: LogIndicesFormState; - fields: FieldsFormState; }; const IndicesConfigurationPanelTemplate: Story = ({ isLoading, isReadOnly, logIndices, - fields, }) => { const logIndicesFormElement = useLogIndicesFormElement(logIndices); - const { tiebreakerFieldFormElement, timestampFieldFormElement } = useFieldsFormElement(fields); return ( <> @@ -87,8 +79,6 @@ const IndicesConfigurationPanelTemplate: Story // field states{'\n'} @@ -98,14 +88,6 @@ const IndicesConfigurationPanelTemplate: Story; - tiebreakerFieldFormElement: FormElement; - timestampFieldFormElement: FormElement; -}>( - ({ - isLoading, - isReadOnly, - indicesFormElement, - tiebreakerFieldFormElement, - timestampFieldFormElement, - }) => { - const trackSwitchToIndexPatternReference = useUiTracker({ app: 'infra_logs' }); +}>(({ isLoading, isReadOnly, indicesFormElement }) => { + const trackSwitchToIndexPatternReference = useUiTracker({ app: 'infra_logs' }); - const switchToIndexPatternReference = useCallback(() => { - indicesFormElement.updateValue(() => undefined); - trackSwitchToIndexPatternReference({ - metric: 'configuration_switch_to_index_pattern_reference', - }); - }, [indicesFormElement, trackSwitchToIndexPatternReference]); + const switchToIndexPatternReference = useCallback(() => { + indicesFormElement.updateValue(() => undefined); + trackSwitchToIndexPatternReference({ + metric: 'configuration_switch_to_index_pattern_reference', + }); + }, [indicesFormElement, trackSwitchToIndexPatternReference]); - if (isIndexPatternFormElement(indicesFormElement)) { - return ( - + ); + } else if (isIndexNamesFormElement(indicesFormElement)) { + return ( + <> + - ); - } else if (isIndexNamesFormElement(indicesFormElement)) { - return ( - <> - - - - ); - } else { - return null; - } + + ); + } else { + return null; } -); +}); const isIndexPatternFormElement = isFormElementForType( (value): value is LogIndexPatternReference | undefined => diff --git a/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_form_state.tsx b/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_form_state.tsx index ac390a5bcfb8b..6523708d18ee0 100644 --- a/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_form_state.tsx +++ b/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_form_state.tsx @@ -8,7 +8,7 @@ import { useMemo } from 'react'; import { LogSourceConfigurationProperties } from '../../../containers/logs/log_source'; import { useCompositeFormElement } from './form_elements'; -import { useFieldsFormElement, useLogIndicesFormElement } from './indices_configuration_form_state'; +import { useLogIndicesFormElement } from './indices_configuration_form_state'; import { useLogColumnsFormElement } from './log_columns_configuration_form_state'; import { useNameFormElement } from './name_configuration_form_state'; @@ -28,17 +28,6 @@ export const useLogSourceConfigurationFormState = ( ) ); - const { fieldsFormElement, tiebreakerFieldFormElement, timestampFieldFormElement } = - useFieldsFormElement( - useMemo( - () => ({ - tiebreakerField: configuration?.fields?.tiebreaker ?? '_doc', - timestampField: configuration?.fields?.timestamp ?? '@timestamp', - }), - [configuration] - ) - ); - const logColumnsFormElement = useLogColumnsFormElement( useMemo(() => configuration?.logColumns ?? [], [configuration]) ); @@ -49,12 +38,11 @@ export const useLogSourceConfigurationFormState = ( childFormElements: { name: nameFormElement, logIndices: logIndicesFormElement, - fields: fieldsFormElement, logColumns: logColumnsFormElement, }, validate: async () => [], }), - [nameFormElement, logIndicesFormElement, fieldsFormElement, logColumnsFormElement] + [nameFormElement, logIndicesFormElement, logColumnsFormElement] ) ); @@ -64,7 +52,5 @@ export const useLogSourceConfigurationFormState = ( logColumnsFormElement, nameFormElement, sourceConfigurationFormElement, - tiebreakerFieldFormElement, - timestampFieldFormElement, }; }; diff --git a/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx b/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx index 883c321db9ae6..b7fbef74781fc 100644 --- a/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx +++ b/x-pack/plugins/infra/public/pages/logs/settings/source_configuration_settings.tsx @@ -67,8 +67,6 @@ export const LogsSettingsPage = () => { logIndicesFormElement, logColumnsFormElement, nameFormElement, - tiebreakerFieldFormElement, - timestampFieldFormElement, } = useLogSourceConfigurationFormState(source?.configuration); const persistUpdates = useCallback(async () => { @@ -113,8 +111,6 @@ export const LogsSettingsPage = () => { isLoading={isLoading} isReadOnly={!isWriteable} indicesFormElement={logIndicesFormElement} - tiebreakerFieldFormElement={tiebreakerFieldFormElement} - timestampFieldFormElement={timestampFieldFormElement} /> diff --git a/x-pack/plugins/infra/public/pages/metrics/settings/fields_configuration_panel.tsx b/x-pack/plugins/infra/public/pages/metrics/settings/fields_configuration_panel.tsx deleted file mode 100644 index 1c6c627c08d0b..0000000000000 --- a/x-pack/plugins/infra/public/pages/metrics/settings/fields_configuration_panel.tsx +++ /dev/null @@ -1,274 +0,0 @@ -/* - * 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 { - EuiDescribedFormGroup, - EuiCode, - EuiFieldText, - EuiForm, - EuiFormRow, - EuiSpacer, - EuiTitle, - EuiCallOut, - EuiLink, -} from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import { FormattedMessage } from '@kbn/i18n/react'; -import React from 'react'; -import { InputFieldProps } from './input_fields'; - -interface FieldsConfigurationPanelProps { - containerFieldProps: InputFieldProps; - hostFieldProps: InputFieldProps; - isLoading: boolean; - readOnly: boolean; - podFieldProps: InputFieldProps; - timestampFieldProps: InputFieldProps; -} - -export const FieldsConfigurationPanel = ({ - containerFieldProps, - hostFieldProps, - isLoading, - readOnly, - podFieldProps, - timestampFieldProps, -}: FieldsConfigurationPanelProps) => { - const isHostValueDefault = hostFieldProps.value === 'host.name'; - const isContainerValueDefault = containerFieldProps.value === 'container.id'; - const isPodValueDefault = podFieldProps.value === 'kubernetes.pod.uid'; - const isTimestampValueDefault = timestampFieldProps.value === '@timestamp'; - return ( - - -

- -

-
- - -

- - - - ), - ecsLink: ( - - ECS - - ), - }} - /> -

-
- - - - - } - description={ - - } - > - @timestamp, - }} - /> - } - isInvalid={timestampFieldProps.isInvalid} - label={ - - } - > - - - - - - - } - description={ - - } - > - container.id, - }} - /> - } - isInvalid={containerFieldProps.isInvalid} - label={ - - } - > - - - - - - - } - description={ - - } - > - host.name, - }} - /> - } - isInvalid={hostFieldProps.isInvalid} - label={ - - } - > - - - - - - - } - description={ - - } - > - kubernetes.pod.uid, - }} - /> - } - isInvalid={podFieldProps.isInvalid} - label={ - - } - > - - - -
- ); -}; diff --git a/x-pack/plugins/infra/public/pages/metrics/settings/indices_configuration_form_state.ts b/x-pack/plugins/infra/public/pages/metrics/settings/indices_configuration_form_state.ts index ced87112e6e9a..359ada83b2ffa 100644 --- a/x-pack/plugins/infra/public/pages/metrics/settings/indices_configuration_form_state.ts +++ b/x-pack/plugins/infra/public/pages/metrics/settings/indices_configuration_form_state.ts @@ -16,11 +16,6 @@ interface FormState { name: string; description: string; metricAlias: string; - containerField: string; - hostField: string; - podField: string; - tiebreakerField: string; - timestampField: string; anomalyThreshold: number; } @@ -63,59 +58,7 @@ export const useIndicesConfigurationFormState = ({ }), [formState.metricAlias] ); - const containerFieldFieldProps = useMemo( - () => - createInputFieldProps({ - errors: validateInputFieldNotEmpty(formState.containerField), - name: `containerField`, - onChange: (containerField) => - setFormStateChanges((changes) => ({ ...changes, containerField })), - value: formState.containerField, - }), - [formState.containerField] - ); - const hostFieldFieldProps = useMemo( - () => - createInputFieldProps({ - errors: validateInputFieldNotEmpty(formState.hostField), - name: `hostField`, - onChange: (hostField) => setFormStateChanges((changes) => ({ ...changes, hostField })), - value: formState.hostField, - }), - [formState.hostField] - ); - const podFieldFieldProps = useMemo( - () => - createInputFieldProps({ - errors: validateInputFieldNotEmpty(formState.podField), - name: `podField`, - onChange: (podField) => setFormStateChanges((changes) => ({ ...changes, podField })), - value: formState.podField, - }), - [formState.podField] - ); - const tiebreakerFieldFieldProps = useMemo( - () => - createInputFieldProps({ - errors: validateInputFieldNotEmpty(formState.tiebreakerField), - name: `tiebreakerField`, - onChange: (tiebreakerField) => - setFormStateChanges((changes) => ({ ...changes, tiebreakerField })), - value: formState.tiebreakerField, - }), - [formState.tiebreakerField] - ); - const timestampFieldFieldProps = useMemo( - () => - createInputFieldProps({ - errors: validateInputFieldNotEmpty(formState.timestampField), - name: `timestampField`, - onChange: (timestampField) => - setFormStateChanges((changes) => ({ ...changes, timestampField })), - value: formState.timestampField, - }), - [formState.timestampField] - ); + const anomalyThresholdFieldProps = useMemo( () => createInputRangeFieldProps({ @@ -132,23 +75,9 @@ export const useIndicesConfigurationFormState = ({ () => ({ name: nameFieldProps, metricAlias: metricAliasFieldProps, - containerField: containerFieldFieldProps, - hostField: hostFieldFieldProps, - podField: podFieldFieldProps, - tiebreakerField: tiebreakerFieldFieldProps, - timestampField: timestampFieldFieldProps, anomalyThreshold: anomalyThresholdFieldProps, }), - [ - nameFieldProps, - metricAliasFieldProps, - containerFieldFieldProps, - hostFieldFieldProps, - podFieldFieldProps, - tiebreakerFieldFieldProps, - timestampFieldFieldProps, - anomalyThresholdFieldProps, - ] + [nameFieldProps, metricAliasFieldProps, anomalyThresholdFieldProps] ); const errors = useMemo( @@ -179,10 +108,5 @@ const defaultFormState: FormState = { name: '', description: '', metricAlias: '', - containerField: '', - hostField: '', - podField: '', - tiebreakerField: '', - timestampField: '', anomalyThreshold: 0, }; diff --git a/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_form_state.tsx b/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_form_state.tsx index 909bf294e4098..d97d66cd5c05d 100644 --- a/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_form_state.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_form_state.tsx @@ -20,11 +20,6 @@ export const useSourceConfigurationFormState = ( name: configuration.name, description: configuration.description, metricAlias: configuration.metricAlias, - containerField: configuration.fields.container, - hostField: configuration.fields.host, - podField: configuration.fields.pod, - tiebreakerField: configuration.fields.tiebreaker, - timestampField: configuration.fields.timestamp, anomalyThreshold: configuration.anomalyThreshold, } : undefined, @@ -56,13 +51,6 @@ export const useSourceConfigurationFormState = ( name: indicesConfigurationFormState.formState.name, description: indicesConfigurationFormState.formState.description, metricAlias: indicesConfigurationFormState.formState.metricAlias, - fields: { - container: indicesConfigurationFormState.formState.containerField, - host: indicesConfigurationFormState.formState.hostField, - pod: indicesConfigurationFormState.formState.podField, - tiebreaker: indicesConfigurationFormState.formState.tiebreakerField, - timestamp: indicesConfigurationFormState.formState.timestampField, - }, anomalyThreshold: indicesConfigurationFormState.formState.anomalyThreshold, }), [indicesConfigurationFormState.formState] @@ -73,13 +61,6 @@ export const useSourceConfigurationFormState = ( name: indicesConfigurationFormState.formStateChanges.name, description: indicesConfigurationFormState.formStateChanges.description, metricAlias: indicesConfigurationFormState.formStateChanges.metricAlias, - fields: { - container: indicesConfigurationFormState.formStateChanges.containerField, - host: indicesConfigurationFormState.formStateChanges.hostField, - pod: indicesConfigurationFormState.formStateChanges.podField, - tiebreaker: indicesConfigurationFormState.formStateChanges.tiebreakerField, - timestamp: indicesConfigurationFormState.formStateChanges.timestampField, - }, anomalyThreshold: indicesConfigurationFormState.formStateChanges.anomalyThreshold, }), [indicesConfigurationFormState.formStateChanges] diff --git a/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx b/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx index 0adf4ed6b5b45..f10bd7cf9e38a 100644 --- a/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx +++ b/x-pack/plugins/infra/public/pages/metrics/settings/source_configuration_settings.tsx @@ -20,7 +20,6 @@ import { SourceLoadingPage } from '../../../components/source_loading_page'; import { Source } from '../../../containers/metrics_source'; import { useInfraMLCapabilitiesContext } from '../../../containers/ml/infra_ml_capabilities'; import { Prompt } from '../../../utils/navigation_warning_prompt'; -import { FieldsConfigurationPanel } from './fields_configuration_panel'; import { IndicesConfigurationPanel } from './indices_configuration_panel'; import { MLConfigurationPanel } from './ml_configuration_panel'; import { NameConfigurationPanel } from './name_configuration_panel'; @@ -123,17 +122,6 @@ export const SourceConfigurationSettings = ({ /> - - - - {hasInfraMLCapabilities && ( <> diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index b9313ccfa46a4..781ef74a872eb 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -13694,20 +13694,9 @@ "xpack.infra.sourceConfiguration.anomalyThresholdLabel": "最低重要度スコア", "xpack.infra.sourceConfiguration.anomalyThresholdTitle": "異常重要度しきい値", "xpack.infra.sourceConfiguration.applySettingsButtonLabel": "適用", - "xpack.infra.sourceConfiguration.containerFieldDescription": "Docker コンテナーの識別に使用されるフィールドです", - "xpack.infra.sourceConfiguration.containerFieldLabel": "コンテナー ID", - "xpack.infra.sourceConfiguration.containerFieldRecommendedValue": "推奨値は {defaultValue} です", - "xpack.infra.sourceConfiguration.deprecationMessage": "これらのフィールドの構成は廃止予定です。8.0.0で削除されます。このアプリケーションは{ecsLink}で動作するように設計されています。{documentationLink}を使用するには、インデックスを調整してください。", - "xpack.infra.sourceConfiguration.deprecationNotice": "廃止通知", "xpack.infra.sourceConfiguration.discardSettingsButtonLabel": "破棄", - "xpack.infra.sourceConfiguration.documentedFields": "文書化されたフィールド", "xpack.infra.sourceConfiguration.fieldEmptyErrorMessage": "このフィールドは未入力のままにできません。", "xpack.infra.sourceConfiguration.fieldLogColumnTitle": "フィールド", - "xpack.infra.sourceConfiguration.fieldsSectionTitle": "フィールド", - "xpack.infra.sourceConfiguration.hostFieldDescription": "推奨値は {defaultValue} です", - "xpack.infra.sourceConfiguration.hostFieldLabel": "ホスト名", - "xpack.infra.sourceConfiguration.hostNameFieldDescription": "ホストの識別に使用されるフィールドです", - "xpack.infra.sourceConfiguration.hostNameFieldLabel": "ホスト名", "xpack.infra.sourceConfiguration.indicesSectionTitle": "インデックス", "xpack.infra.sourceConfiguration.logColumnsSectionTitle": "ログ列", "xpack.infra.sourceConfiguration.logIndicesDescription": "ログデータを含む一致するインデックスのインデックスパターンです", @@ -13725,17 +13714,8 @@ "xpack.infra.sourceConfiguration.nameSectionTitle": "名前", "xpack.infra.sourceConfiguration.noLogColumnsDescription": "上のボタンでこのリストに列を追加します。", "xpack.infra.sourceConfiguration.noLogColumnsTitle": "列がありません", - "xpack.infra.sourceConfiguration.podFieldDescription": "Kubernetes ポッドの識別に使用されるフィールドです", - "xpack.infra.sourceConfiguration.podFieldLabel": "ポッド ID", - "xpack.infra.sourceConfiguration.podFieldRecommendedValue": "推奨値は {defaultValue} です", "xpack.infra.sourceConfiguration.removeLogColumnButtonLabel": "{columnDescription} 列を削除", "xpack.infra.sourceConfiguration.systemColumnBadgeLabel": "システム", - "xpack.infra.sourceConfiguration.tiebreakerFieldDescription": "同じタイムスタンプの 2 つのエントリーを識別するのに使用されるフィールドです", - "xpack.infra.sourceConfiguration.tiebreakerFieldLabel": "タイブレーカー", - "xpack.infra.sourceConfiguration.tiebreakerFieldRecommendedValue": "推奨値は {defaultValue} です", - "xpack.infra.sourceConfiguration.timestampFieldDescription": "ログエントリーの並べ替えに使用されるタイムスタンプです", - "xpack.infra.sourceConfiguration.timestampFieldLabel": "タイムスタンプ", - "xpack.infra.sourceConfiguration.timestampFieldRecommendedValue": "推奨値は {defaultValue} です", "xpack.infra.sourceConfiguration.timestampLogColumnDescription": "このシステムフィールドは、{timestampSetting} フィールド設定から判断されたログエントリーの時刻を表示します。", "xpack.infra.sourceConfiguration.unsavedFormPrompt": "終了してよろしいですか?変更内容は失われます", "xpack.infra.sourceErrorPage.failedToLoadDataSourcesMessage": "データソースの読み込みに失敗しました。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index b9739d4bd991e..92646cc1f9cd9 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -13882,20 +13882,9 @@ "xpack.infra.sourceConfiguration.anomalyThresholdLabel": "最低严重性分数", "xpack.infra.sourceConfiguration.anomalyThresholdTitle": "异常严重性阈值", "xpack.infra.sourceConfiguration.applySettingsButtonLabel": "应用", - "xpack.infra.sourceConfiguration.containerFieldDescription": "用于标识 Docker 容器的字段", - "xpack.infra.sourceConfiguration.containerFieldLabel": "容器 ID", - "xpack.infra.sourceConfiguration.containerFieldRecommendedValue": "推荐值为 {defaultValue}", - "xpack.infra.sourceConfiguration.deprecationMessage": "有关这些字段的配置已过时,将在 8.0.0 中移除。此应用程序专用于 {ecsLink},您应调整索引以使用{documentationLink}。", - "xpack.infra.sourceConfiguration.deprecationNotice": "过时通知", "xpack.infra.sourceConfiguration.discardSettingsButtonLabel": "丢弃", - "xpack.infra.sourceConfiguration.documentedFields": "已记录字段", "xpack.infra.sourceConfiguration.fieldEmptyErrorMessage": "字段不得为空。", "xpack.infra.sourceConfiguration.fieldLogColumnTitle": "字段", - "xpack.infra.sourceConfiguration.fieldsSectionTitle": "字段", - "xpack.infra.sourceConfiguration.hostFieldDescription": "推荐值为 {defaultValue}", - "xpack.infra.sourceConfiguration.hostFieldLabel": "主机名", - "xpack.infra.sourceConfiguration.hostNameFieldDescription": "用于标识主机的字段", - "xpack.infra.sourceConfiguration.hostNameFieldLabel": "主机名", "xpack.infra.sourceConfiguration.indicesSectionTitle": "索引", "xpack.infra.sourceConfiguration.logColumnsSectionTitle": "日志列", "xpack.infra.sourceConfiguration.logIndicesDescription": "用于匹配包含日志数据的索引的索引模式", @@ -13913,17 +13902,8 @@ "xpack.infra.sourceConfiguration.nameSectionTitle": "名称", "xpack.infra.sourceConfiguration.noLogColumnsDescription": "使用上面的按钮将列添加到此列表。", "xpack.infra.sourceConfiguration.noLogColumnsTitle": "无列", - "xpack.infra.sourceConfiguration.podFieldDescription": "用于标识 Kubernetes Pod 的字段", - "xpack.infra.sourceConfiguration.podFieldLabel": "Pod ID", - "xpack.infra.sourceConfiguration.podFieldRecommendedValue": "推荐值为 {defaultValue}", "xpack.infra.sourceConfiguration.removeLogColumnButtonLabel": "删除“{columnDescription}”列", "xpack.infra.sourceConfiguration.systemColumnBadgeLabel": "系统", - "xpack.infra.sourceConfiguration.tiebreakerFieldDescription": "用于时间戳相同的两个条目间决胜的字段", - "xpack.infra.sourceConfiguration.tiebreakerFieldLabel": "决胜属性", - "xpack.infra.sourceConfiguration.tiebreakerFieldRecommendedValue": "推荐值为 {defaultValue}", - "xpack.infra.sourceConfiguration.timestampFieldDescription": "用于排序日志条目的时间戳", - "xpack.infra.sourceConfiguration.timestampFieldLabel": "时间戳", - "xpack.infra.sourceConfiguration.timestampFieldRecommendedValue": "推荐值为 {defaultValue}", "xpack.infra.sourceConfiguration.timestampLogColumnDescription": "此系统字段显示 {timestampSetting} 字段设置所确定的日志条目时间。", "xpack.infra.sourceConfiguration.unsavedFormPrompt": "是否确定要离开?更改将丢失", "xpack.infra.sourceErrorPage.failedToLoadDataSourcesMessage": "无法加载数据源。", From 26d42523c57dfb695f8de1820e96c3e6084fa955 Mon Sep 17 00:00:00 2001 From: Gloria Hornero Date: Tue, 19 Oct 2021 22:53:18 +0200 Subject: [PATCH 04/13] fixes style bug (#114976) --- .../public/timelines/components/timeline/tabs_content/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx index 1041d5fbc7d7d..e38e380292260 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/tabs_content/index.tsx @@ -201,7 +201,7 @@ const CountBadge = styled(EuiBadge)` `; const StyledEuiTab = styled(EuiTab)` - > span { + .euiTab__content { display: flex; flex-direction: row; white-space: pre; From db53a79cc4de33b85fbdae1d2bfa51c655477b4a Mon Sep 17 00:00:00 2001 From: Nathan L Smith Date: Tue, 19 Oct 2021 16:04:02 -0500 Subject: [PATCH 05/13] Waterfall layout and expansion fixes (#114889) Fix according toggling behavior on trace waterfall. Previously clicking on any item would collapse the whole waterfall, now it just collapses the one you clicked on. Truncate spans so ones with very long names don't overflow. Make the left margin relative to the max level of depth so waterfalls with deep trees don't overflow. --- .../Waterfall/accordion_waterfall.tsx | 10 +- .../waterfall_container/Waterfall/index.tsx | 25 +- .../Waterfall/waterfall_item.tsx | 8 +- .../WaterfallContainer.stories.tsx | 56 - .../waterfallContainer.stories.data.ts | 2269 ------- .../waterfall_container.stories.data.ts | 5865 +++++++++++++++++ .../waterfall_container.stories.tsx | 88 + .../waterfall_container.test.tsx | 42 + 8 files changed, 6019 insertions(+), 2344 deletions(-) delete mode 100644 x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/WaterfallContainer.stories.tsx delete mode 100644 x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfallContainer.stories.data.ts create mode 100644 x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.data.ts create mode 100644 x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.tsx create mode 100644 x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.test.tsx diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/accordion_waterfall.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/accordion_waterfall.tsx index e4a851b890a7c..15883e7905142 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/accordion_waterfall.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/accordion_waterfall.tsx @@ -7,7 +7,7 @@ import { EuiAccordion, EuiAccordionProps } from '@elastic/eui'; import { isEmpty } from 'lodash'; -import React, { useState } from 'react'; +import React, { Dispatch, SetStateAction, useState } from 'react'; import { euiStyled } from '../../../../../../../../../../src/plugins/kibana_react/common'; import { Margins } from '../../../../../shared/charts/Timeline'; import { WaterfallItem } from './waterfall_item'; @@ -22,8 +22,8 @@ interface AccordionWaterfallProps { level: number; duration: IWaterfall['duration']; waterfallItemId?: string; + setMaxLevel: Dispatch>; waterfall: IWaterfall; - onToggleEntryTransaction?: () => void; timelineMargins: Margins; onClickWaterfallItem: (item: IWaterfallSpanOrTransaction) => void; } @@ -97,12 +97,13 @@ export function AccordionWaterfall(props: AccordionWaterfallProps) { duration, waterfall, waterfallItemId, + setMaxLevel, timelineMargins, onClickWaterfallItem, - onToggleEntryTransaction, } = props; const nextLevel = level + 1; + setMaxLevel(nextLevel); const children = waterfall.childrenByParentId[item.id] || []; const errorCount = waterfall.getErrorCount(item.id); @@ -139,9 +140,6 @@ export function AccordionWaterfall(props: AccordionWaterfallProps) { forceState={isOpen ? 'open' : 'closed'} onToggle={() => { setIsOpen((isCurrentOpen) => !isCurrentOpen); - if (onToggleEntryTransaction) { - onToggleEntryTransaction(); - } }} > {children.map((child) => ( diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/index.tsx index 3932a02c9d974..5b4bf99f7dae6 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/index.tsx @@ -29,13 +29,6 @@ const Container = euiStyled.div` overflow: hidden; `; -const TIMELINE_MARGINS = { - top: 40, - left: 100, - right: 50, - bottom: 0, -}; - const toggleFlyout = ({ history, item, @@ -72,6 +65,16 @@ export function Waterfall({ waterfall, waterfallItemId }: Props) { const agentMarks = getAgentMarks(waterfall.entryWaterfallTransaction?.doc); const errorMarks = getErrorMarks(waterfall.errorItems); + // Calculate the left margin relative to the deepest level, or 100px, whichever + // is more. + const [maxLevel, setMaxLevel] = useState(0); + const timelineMargins = { + top: 40, + left: Math.max(100, maxLevel * 10), + right: 50, + bottom: 0, + }; + return ( @@ -99,7 +102,7 @@ export function Waterfall({ waterfall, waterfallItemId }: Props) { marks={[...agentMarks, ...errorMarks]} xMax={duration} height={waterfallHeight} - margins={TIMELINE_MARGINS} + margins={timelineMargins} /> @@ -110,16 +113,14 @@ export function Waterfall({ waterfall, waterfallItemId }: Props) { isOpen={isAccordionOpen} item={waterfall.entryWaterfallTransaction} level={0} + setMaxLevel={setMaxLevel} waterfallItemId={waterfallItemId} duration={duration} waterfall={waterfall} - timelineMargins={TIMELINE_MARGINS} + timelineMargins={timelineMargins} onClickWaterfallItem={(item: IWaterfallItem) => toggleFlyout({ history, item }) } - onToggleEntryTransaction={() => - setIsAccordionOpen((isOpen) => !isOpen) - } /> )} diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_item.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_item.tsx index 4001a0624a809..caa0cac3acef8 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_item.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/Waterfall/waterfall_item.tsx @@ -17,6 +17,7 @@ import { } from '../../../../../../../common/elasticsearch_fieldnames'; import { asDuration } from '../../../../../../../common/utils/formatters'; import { Margins } from '../../../../../shared/charts/Timeline'; +import { TruncateWithTooltip } from '../../../../../shared/truncate_with_tooltip'; import { SyncBadge } from './sync_badge'; import { IWaterfallSpanOrTransaction } from './waterfall_helpers/waterfall_helpers'; import { FailureBadge } from './failure_badge'; @@ -67,6 +68,7 @@ const ItemText = euiStyled.span` display: flex; align-items: center; height: ${({ theme }) => theme.eui.euiSizeL}; + max-width: 100%; /* add margin to all direct descendants */ & > * { @@ -160,7 +162,11 @@ function NameLabel({ item }: { item: IWaterfallSpanOrTransaction }) { : ''; name = `${item.doc.span.composite.count}${compositePrefix} ${name}`; } - return {name}; + return ( + + + + ); case 'transaction': return ( diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/WaterfallContainer.stories.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/WaterfallContainer.stories.tsx deleted file mode 100644 index a03b7b29f9666..0000000000000 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/WaterfallContainer.stories.tsx +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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, { ComponentType } from 'react'; -import { MemoryRouter } from 'react-router-dom'; -import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context'; -import { WaterfallContainer } from './index'; -import { getWaterfall } from './Waterfall/waterfall_helpers/waterfall_helpers'; -import { - inferredSpans, - simpleTrace, - traceChildStartBeforeParent, - traceWithErrors, - urlParams, -} from './waterfallContainer.stories.data'; - -export default { - title: 'app/TransactionDetails/Waterfall', - component: WaterfallContainer, - decorators: [ - (Story: ComponentType) => ( - - - - - - ), - ], -}; - -export function Example() { - const waterfall = getWaterfall(simpleTrace, '975c8d5bfd1dd20b'); - return ; -} - -export function WithErrors() { - const waterfall = getWaterfall(traceWithErrors, '975c8d5bfd1dd20b'); - return ; -} - -export function ChildStartsBeforeParent() { - const waterfall = getWaterfall( - traceChildStartBeforeParent, - '975c8d5bfd1dd20b' - ); - return ; -} - -export function InferredSpans() { - const waterfall = getWaterfall(inferredSpans, 'f2387d37260d00bd'); - return ; -} diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfallContainer.stories.data.ts b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfallContainer.stories.data.ts deleted file mode 100644 index 60285c835bbf3..0000000000000 --- a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfallContainer.stories.data.ts +++ /dev/null @@ -1,2269 +0,0 @@ -/* - * 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 type { Location } from 'history'; -import type { ApmUrlParams } from '../../../../../context/url_params_context/types'; -import { APIReturnType } from '../../../../../services/rest/createCallApmApi'; - -export const location = { - pathname: '/services/opbeans-go/transactions/view', - search: - '?rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0&kuery=service.name%253A%2520%2522opbeans-java%2522%2520or%2520service.name%2520%253A%2520%2522opbeans-go%2522&traceId=513d33fafe99bbe6134749310c9b5322&transactionId=975c8d5bfd1dd20b&transactionName=GET%20%2Fapi%2Forders&transactionType=request', - hash: '', -} as Location; - -type TraceAPIResponse = APIReturnType<'GET /internal/apm/traces/{traceId}'>; - -export const urlParams = { - start: '2020-03-22T15:16:38.742Z', - end: '2020-03-23T15:16:38.742Z', - rangeFrom: 'now-24h', - rangeTo: 'now', - refreshPaused: true, - refreshInterval: 0, - page: 0, - transactionId: '975c8d5bfd1dd20b', - traceId: '513d33fafe99bbe6134749310c9b5322', - transactionName: 'GET /api/orders', - transactionType: 'request', - processorEvent: 'transaction', - serviceName: 'opbeans-go', -} as ApmUrlParams; - -export const simpleTrace = { - traceDocs: [ - { - container: { - id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - process: { - pid: 6, - title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', - version: '1.14.1-SNAPSHOT', - }, - internal: { - sampler: { - value: 46, - }, - }, - source: { - ip: '172.19.0.13', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: '172.19.0.9', - full: 'http://172.19.0.9:3000/api/orders', - }, - observer: { - hostname: 'f37f48d8b60b', - id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', - ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.785Z', - ecs: { - version: '1.4.0', - }, - service: { - node: { - name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '10.0.2', - }, - language: { - name: 'Java', - version: '10.0.2', - }, - version: 'None', - }, - host: { - hostname: '4cf84d094553', - os: { - platform: 'Linux', - }, - ip: '172.19.0.9', - name: '4cf84d094553', - architecture: 'amd64', - }, - http: { - request: { - headers: { - Accept: ['*/*'], - 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], - Host: ['172.19.0.9:3000'], - 'Accept-Encoding': ['gzip, deflate'], - }, - method: 'get', - socket: { - encrypted: false, - remote_address: '172.19.0.13', - }, - body: { - original: '[REDACTED]', - }, - }, - response: { - headers: { - 'Transfer-Encoding': ['chunked'], - Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], - 'Content-Type': ['application/json;charset=ISO-8859-1'], - }, - status_code: 200, - finished: true, - headers_sent: true, - }, - version: '1.1', - }, - client: { - ip: '172.19.0.13', - }, - transaction: { - duration: { - us: 18842, - }, - result: 'HTTP 2xx', - name: 'DispatcherServlet#doGet', - id: '49809ad3c26adf74', - span_count: { - dropped: 0, - started: 1, - }, - type: 'request', - sampled: true, - }, - user_agent: { - original: 'Python/3.7 aiohttp/3.3.2', - name: 'Other', - device: { - name: 'Other', - }, - }, - timestamp: { - us: 1584975868785000, - }, - }, - { - parent: { - id: 'fc107f7b556eb49b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: 'opbeans-go', - full: 'http://opbeans-go:3000/api/orders', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - framework: { - name: 'gin', - version: 'v1.4.0', - }, - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - duration: { - us: 16597, - }, - result: 'HTTP 2xx', - name: 'GET /api/orders', - id: '975c8d5bfd1dd20b', - span_count: { - dropped: 0, - started: 1, - }, - type: 'request', - sampled: true, - }, - timestamp: { - us: 1584975868787052, - }, - }, - { - parent: { - id: 'daae24d83c269918', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - timestamp: { - us: 1584975868788603, - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: 'opbeans-go', - full: 'http://opbeans-go:3000/api/orders', - }, - '@timestamp': '2020-03-23T15:04:28.788Z', - service: { - node: { - name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - environment: 'production', - framework: { - name: 'django', - version: '2.1.13', - }, - name: 'opbeans-python', - runtime: { - name: 'CPython', - version: '3.6.10', - }, - language: { - name: 'python', - version: '3.6.10', - }, - version: 'None', - }, - transaction: { - result: 'HTTP 2xx', - duration: { - us: 14648, - }, - name: 'GET opbeans.views.orders', - span_count: { - dropped: 0, - started: 1, - }, - id: '6fb0ff7365b87298', - type: 'request', - sampled: true, - }, - }, - { - container: { - id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - parent: { - id: '49809ad3c26adf74', - }, - process: { - pid: 6, - title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', - version: '1.14.1-SNAPSHOT', - }, - internal: { - sampler: { - value: 44, - }, - }, - destination: { - address: 'opbeans-go', - port: 3000, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: 'f37f48d8b60b', - id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', - type: 'apm-server', - ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.785Z', - ecs: { - version: '1.4.0', - }, - service: { - node: { - name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '10.0.2', - }, - language: { - name: 'Java', - version: '10.0.2', - }, - version: 'None', - }, - host: { - hostname: '4cf84d094553', - os: { - platform: 'Linux', - }, - ip: '172.19.0.9', - name: '4cf84d094553', - architecture: 'amd64', - }, - connection: { - hash: "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}", - }, - transaction: { - id: '49809ad3c26adf74', - }, - timestamp: { - us: 1584975868785273, - }, - span: { - duration: { - us: 17530, - }, - subtype: 'http', - name: 'GET opbeans-go', - destination: { - service: { - resource: 'opbeans-go:3000', - name: 'http://opbeans-go:3000', - type: 'external', - }, - }, - http: { - response: { - status_code: 200, - }, - url: { - original: 'http://opbeans-go:3000/api/orders', - }, - }, - id: 'fc107f7b556eb49b', - type: 'external', - }, - }, - { - parent: { - id: '975c8d5bfd1dd20b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - processor: { - name: 'transaction', - event: 'span', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - id: '975c8d5bfd1dd20b', - }, - timestamp: { - us: 1584975868787174, - }, - span: { - duration: { - us: 16250, - }, - subtype: 'http', - destination: { - service: { - resource: 'opbeans-python:3000', - name: 'http://opbeans-python:3000', - type: 'external', - }, - }, - name: 'GET opbeans-python:3000', - http: { - response: { - status_code: 200, - }, - url: { - original: 'http://opbeans-python:3000/api/orders', - }, - }, - id: 'daae24d83c269918', - type: 'external', - }, - }, - { - container: { - id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - parent: { - id: '6fb0ff7365b87298', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - processor: { - name: 'transaction', - event: 'span', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.790Z', - service: { - node: { - name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - environment: 'production', - framework: { - name: 'django', - version: '2.1.13', - }, - name: 'opbeans-python', - runtime: { - name: 'CPython', - version: '3.6.10', - }, - language: { - name: 'python', - version: '3.6.10', - }, - version: 'None', - }, - transaction: { - id: '6fb0ff7365b87298', - }, - timestamp: { - us: 1584975868790080, - }, - span: { - duration: { - us: 2519, - }, - subtype: 'postgresql', - name: 'SELECT FROM opbeans_order', - destination: { - service: { - resource: 'postgresql', - name: 'postgresql', - type: 'db', - }, - }, - action: 'query', - id: 'c9407abb4d08ead1', - type: 'db', - sync: true, - db: { - statement: - 'SELECT "opbeans_order"."id", "opbeans_order"."customer_id", "opbeans_customer"."full_name", "opbeans_order"."created_at" FROM "opbeans_order" INNER JOIN "opbeans_customer" ON ("opbeans_order"."customer_id" = "opbeans_customer"."id") LIMIT 1000', - type: 'sql', - }, - }, - }, - ], - exceedsMax: false, - errorDocs: [], -} as TraceAPIResponse; - -export const traceWithErrors = { - traceDocs: [ - { - container: { - id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - process: { - pid: 6, - title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', - version: '1.14.1-SNAPSHOT', - }, - internal: { - sampler: { - value: 46, - }, - }, - source: { - ip: '172.19.0.13', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: '172.19.0.9', - full: 'http://172.19.0.9:3000/api/orders', - }, - observer: { - hostname: 'f37f48d8b60b', - id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', - ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.785Z', - ecs: { - version: '1.4.0', - }, - service: { - node: { - name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '10.0.2', - }, - language: { - name: 'Java', - version: '10.0.2', - }, - version: 'None', - }, - host: { - hostname: '4cf84d094553', - os: { - platform: 'Linux', - }, - ip: '172.19.0.9', - name: '4cf84d094553', - architecture: 'amd64', - }, - http: { - request: { - headers: { - Accept: ['*/*'], - 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], - Host: ['172.19.0.9:3000'], - 'Accept-Encoding': ['gzip, deflate'], - }, - method: 'get', - socket: { - encrypted: false, - remote_address: '172.19.0.13', - }, - body: { - original: '[REDACTED]', - }, - }, - response: { - headers: { - 'Transfer-Encoding': ['chunked'], - Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], - 'Content-Type': ['application/json;charset=ISO-8859-1'], - }, - status_code: 200, - finished: true, - headers_sent: true, - }, - version: '1.1', - }, - client: { - ip: '172.19.0.13', - }, - transaction: { - duration: { - us: 18842, - }, - result: 'HTTP 2xx', - name: 'DispatcherServlet#doGet', - id: '49809ad3c26adf74', - span_count: { - dropped: 0, - started: 1, - }, - type: 'request', - sampled: true, - }, - user_agent: { - original: 'Python/3.7 aiohttp/3.3.2', - name: 'Other', - device: { - name: 'Other', - }, - }, - timestamp: { - us: 1584975868785000, - }, - }, - { - parent: { - id: 'fc107f7b556eb49b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: 'opbeans-go', - full: 'http://opbeans-go:3000/api/orders', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - framework: { - name: 'gin', - version: 'v1.4.0', - }, - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - duration: { - us: 16597, - }, - result: 'HTTP 2xx', - name: 'GET /api/orders', - id: '975c8d5bfd1dd20b', - span_count: { - dropped: 0, - started: 1, - }, - type: 'request', - sampled: true, - }, - timestamp: { - us: 1584975868787052, - }, - }, - { - parent: { - id: 'daae24d83c269918', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - timestamp: { - us: 1584975868788603, - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: 'opbeans-go', - full: 'http://opbeans-go:3000/api/orders', - }, - '@timestamp': '2020-03-23T15:04:28.788Z', - service: { - node: { - name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - environment: 'production', - framework: { - name: 'django', - version: '2.1.13', - }, - name: 'opbeans-python', - runtime: { - name: 'CPython', - version: '3.6.10', - }, - language: { - name: 'python', - version: '3.6.10', - }, - version: 'None', - }, - transaction: { - result: 'HTTP 2xx', - duration: { - us: 14648, - }, - name: 'GET opbeans.views.orders', - span_count: { - dropped: 0, - started: 1, - }, - id: '6fb0ff7365b87298', - type: 'request', - sampled: true, - }, - }, - { - container: { - id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - parent: { - id: '49809ad3c26adf74', - }, - process: { - pid: 6, - title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', - version: '1.14.1-SNAPSHOT', - }, - internal: { - sampler: { - value: 44, - }, - }, - destination: { - address: 'opbeans-go', - port: 3000, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: 'f37f48d8b60b', - id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', - type: 'apm-server', - ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.785Z', - ecs: { - version: '1.4.0', - }, - service: { - node: { - name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '10.0.2', - }, - language: { - name: 'Java', - version: '10.0.2', - }, - version: 'None', - }, - host: { - hostname: '4cf84d094553', - os: { - platform: 'Linux', - }, - ip: '172.19.0.9', - name: '4cf84d094553', - architecture: 'amd64', - }, - connection: { - hash: "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}", - }, - transaction: { - id: '49809ad3c26adf74', - }, - timestamp: { - us: 1584975868785273, - }, - span: { - duration: { - us: 17530, - }, - subtype: 'http', - name: 'GET opbeans-go', - destination: { - service: { - resource: 'opbeans-go:3000', - name: 'http://opbeans-go:3000', - type: 'external', - }, - }, - http: { - response: { - status_code: 200, - }, - url: { - original: 'http://opbeans-go:3000/api/orders', - }, - }, - id: 'fc107f7b556eb49b', - type: 'external', - }, - }, - { - parent: { - id: '975c8d5bfd1dd20b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - processor: { - name: 'transaction', - event: 'span', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - id: '975c8d5bfd1dd20b', - }, - timestamp: { - us: 1584975868787174, - }, - span: { - duration: { - us: 16250, - }, - subtype: 'http', - destination: { - service: { - resource: 'opbeans-python:3000', - name: 'http://opbeans-python:3000', - type: 'external', - }, - }, - name: 'GET opbeans-python:3000', - http: { - response: { - status_code: 200, - }, - url: { - original: 'http://opbeans-python:3000/api/orders', - }, - }, - id: 'daae24d83c269918', - type: 'external', - }, - }, - { - container: { - id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - parent: { - id: '6fb0ff7365b87298', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - processor: { - name: 'transaction', - event: 'span', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.790Z', - service: { - node: { - name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - environment: 'production', - framework: { - name: 'django', - version: '2.1.13', - }, - name: 'opbeans-python', - runtime: { - name: 'CPython', - version: '3.6.10', - }, - language: { - name: 'python', - version: '3.6.10', - }, - version: 'None', - }, - transaction: { - id: '6fb0ff7365b87298', - }, - timestamp: { - us: 1584975868790080, - }, - span: { - duration: { - us: 2519, - }, - subtype: 'postgresql', - name: 'SELECT FROM opbeans_order', - destination: { - service: { - resource: 'postgresql', - name: 'postgresql', - type: 'db', - }, - }, - action: 'query', - id: 'c9407abb4d08ead1', - type: 'db', - sync: true, - db: { - statement: - 'SELECT "opbeans_order"."id", "opbeans_order"."customer_id", "opbeans_customer"."full_name", "opbeans_order"."created_at" FROM "opbeans_order" INNER JOIN "opbeans_customer" ON ("opbeans_order"."customer_id" = "opbeans_customer"."id") LIMIT 1000', - type: 'sql', - }, - }, - }, - ], - exceedsMax: false, - errorDocs: [ - { - parent: { - id: '975c8d5bfd1dd20b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - error: { - culprit: 'logrusMiddleware', - log: { - level: 'error', - message: 'GET //api/products (502)', - }, - id: '1f3cb98206b5c54225cb7c8908a658da', - grouping_key: '4dba2ff58fe6c036a5dee2ce411e512a', - }, - processor: { - name: 'error', - event: 'error', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T16:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - id: '975c8d5bfd1dd20b', - sampled: false, - }, - timestamp: { - us: 1584975868787052, - }, - }, - { - parent: { - id: '6fb0ff7365b87298', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - error: { - culprit: 'logrusMiddleware', - log: { - level: 'error', - message: 'GET //api/products (502)', - }, - id: '1f3cb98206b5c54225cb7c8908a658d2', - grouping_key: '4dba2ff58fe6c036a5dee2ce411e512a', - }, - processor: { - name: 'error', - event: 'error', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T16:04:28.790Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - name: 'opbeans-python', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - id: '6fb0ff7365b87298', - sampled: false, - }, - timestamp: { - us: 1584975868790000, - }, - }, - ], -} as unknown as TraceAPIResponse; - -export const traceChildStartBeforeParent = { - traceDocs: [ - { - container: { - id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - process: { - pid: 6, - title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', - version: '1.14.1-SNAPSHOT', - }, - internal: { - sampler: { - value: 46, - }, - }, - source: { - ip: '172.19.0.13', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: '172.19.0.9', - full: 'http://172.19.0.9:3000/api/orders', - }, - observer: { - hostname: 'f37f48d8b60b', - id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', - ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.785Z', - ecs: { - version: '1.4.0', - }, - service: { - node: { - name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '10.0.2', - }, - language: { - name: 'Java', - version: '10.0.2', - }, - version: 'None', - }, - host: { - hostname: '4cf84d094553', - os: { - platform: 'Linux', - }, - ip: '172.19.0.9', - name: '4cf84d094553', - architecture: 'amd64', - }, - http: { - request: { - headers: { - Accept: ['*/*'], - 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], - Host: ['172.19.0.9:3000'], - 'Accept-Encoding': ['gzip, deflate'], - }, - method: 'get', - socket: { - encrypted: false, - remote_address: '172.19.0.13', - }, - body: { - original: '[REDACTED]', - }, - }, - response: { - headers: { - 'Transfer-Encoding': ['chunked'], - Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], - 'Content-Type': ['application/json;charset=ISO-8859-1'], - }, - status_code: 200, - finished: true, - headers_sent: true, - }, - version: '1.1', - }, - client: { - ip: '172.19.0.13', - }, - transaction: { - duration: { - us: 18842, - }, - result: 'HTTP 2xx', - name: 'DispatcherServlet#doGet', - id: '49809ad3c26adf74', - span_count: { - dropped: 0, - started: 1, - }, - type: 'request', - sampled: true, - }, - user_agent: { - original: 'Python/3.7 aiohttp/3.3.2', - name: 'Other', - device: { - name: 'Other', - }, - }, - timestamp: { - us: 1584975868785000, - }, - }, - { - parent: { - id: 'fc107f7b556eb49b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: 'opbeans-go', - full: 'http://opbeans-go:3000/api/orders', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - framework: { - name: 'gin', - version: 'v1.4.0', - }, - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - duration: { - us: 16597, - }, - result: 'HTTP 2xx', - name: 'GET /api/orders', - id: '975c8d5bfd1dd20b', - span_count: { - dropped: 0, - started: 1, - }, - type: 'request', - sampled: true, - }, - timestamp: { - us: 1584975868787052, - }, - }, - { - parent: { - id: 'daae24d83c269918', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - timestamp: { - us: 1584975868780000, - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/orders', - scheme: 'http', - port: 3000, - domain: 'opbeans-go', - full: 'http://opbeans-go:3000/api/orders', - }, - '@timestamp': '2020-03-23T15:04:28.788Z', - service: { - node: { - name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - environment: 'production', - framework: { - name: 'django', - version: '2.1.13', - }, - name: 'opbeans-python', - runtime: { - name: 'CPython', - version: '3.6.10', - }, - language: { - name: 'python', - version: '3.6.10', - }, - version: 'None', - }, - transaction: { - result: 'HTTP 2xx', - duration: { - us: 1464, - }, - name: 'I started before my parent 😰', - span_count: { - dropped: 0, - started: 1, - }, - id: '6fb0ff7365b87298', - type: 'request', - sampled: true, - }, - }, - { - container: { - id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - parent: { - id: '49809ad3c26adf74', - }, - process: { - pid: 6, - title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', - version: '1.14.1-SNAPSHOT', - }, - internal: { - sampler: { - value: 44, - }, - }, - destination: { - address: 'opbeans-go', - port: 3000, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: 'f37f48d8b60b', - id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', - type: 'apm-server', - ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.785Z', - ecs: { - version: '1.4.0', - }, - service: { - node: { - name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '10.0.2', - }, - language: { - name: 'Java', - version: '10.0.2', - }, - version: 'None', - }, - host: { - hostname: '4cf84d094553', - os: { - platform: 'Linux', - }, - ip: '172.19.0.9', - name: '4cf84d094553', - architecture: 'amd64', - }, - connection: { - hash: "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}", - }, - transaction: { - id: '49809ad3c26adf74', - }, - timestamp: { - us: 1584975868785273, - }, - span: { - duration: { - us: 17530, - }, - subtype: 'http', - name: 'GET opbeans-go', - destination: { - service: { - resource: 'opbeans-go:3000', - name: 'http://opbeans-go:3000', - type: 'external', - }, - }, - http: { - response: { - status_code: 200, - }, - url: { - original: 'http://opbeans-go:3000/api/orders', - }, - }, - id: 'fc107f7b556eb49b', - type: 'external', - }, - }, - { - parent: { - id: '975c8d5bfd1dd20b', - }, - agent: { - name: 'go', - version: '1.7.2', - }, - processor: { - name: 'transaction', - event: 'span', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.787Z', - service: { - node: { - name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', - }, - environment: 'production', - name: 'opbeans-go', - runtime: { - name: 'gc', - version: 'go1.14.1', - }, - language: { - name: 'go', - version: 'go1.14.1', - }, - version: 'None', - }, - transaction: { - id: '975c8d5bfd1dd20b', - }, - timestamp: { - us: 1584975868787174, - }, - span: { - duration: { - us: 16250, - }, - subtype: 'http', - destination: { - service: { - resource: 'opbeans-python:3000', - name: 'http://opbeans-python:3000', - type: 'external', - }, - }, - name: 'I am his 👇🏻 parent 😡', - http: { - response: { - status_code: 200, - }, - url: { - original: 'http://opbeans-python:3000/api/orders', - }, - }, - id: 'daae24d83c269918', - type: 'external', - }, - }, - { - container: { - id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - parent: { - id: '6fb0ff7365b87298', - }, - agent: { - name: 'python', - version: '5.5.2', - }, - processor: { - name: 'transaction', - event: 'span', - }, - trace: { - id: '513d33fafe99bbe6134749310c9b5322', - }, - '@timestamp': '2020-03-23T15:04:28.790Z', - service: { - node: { - name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', - }, - environment: 'production', - framework: { - name: 'django', - version: '2.1.13', - }, - name: 'opbeans-python', - runtime: { - name: 'CPython', - version: '3.6.10', - }, - language: { - name: 'python', - version: '3.6.10', - }, - version: 'None', - }, - transaction: { - id: '6fb0ff7365b87298', - }, - timestamp: { - us: 1584975868781000, - }, - span: { - duration: { - us: 2519, - }, - subtype: 'postgresql', - name: 'I am using my parents skew 😇', - destination: { - service: { - resource: 'postgresql', - name: 'postgresql', - type: 'db', - }, - }, - action: 'query', - id: 'c9407abb4d08ead1', - type: 'db', - sync: true, - db: { - statement: - 'SELECT "opbeans_order"."id", "opbeans_order"."customer_id", "opbeans_customer"."full_name", "opbeans_order"."created_at" FROM "opbeans_order" INNER JOIN "opbeans_customer" ON ("opbeans_order"."customer_id" = "opbeans_customer"."id") LIMIT 1000', - type: 'sql', - }, - }, - }, - ], - exceedsMax: false, - errorDocs: [], -} as TraceAPIResponse; - -export const inferredSpans = { - traceDocs: [ - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - source: { - ip: '172.18.0.8', - }, - processor: { - name: 'transaction', - event: 'transaction', - }, - url: { - path: '/api/products/2', - scheme: 'http', - port: 3000, - domain: '172.18.0.7', - full: 'http://172.18.0.7:3000/api/products/2', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.786Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - client: { - ip: '172.18.0.8', - }, - http: { - request: { - headers: { - Accept: ['*/*'], - 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], - Host: ['172.18.0.7:3000'], - 'Accept-Encoding': ['gzip, deflate'], - }, - method: 'get', - socket: { - encrypted: false, - remote_address: '172.18.0.8', - }, - }, - response: { - headers: { - 'Transfer-Encoding': ['chunked'], - Date: ['Thu, 09 Apr 2020 11:36:01 GMT'], - 'Content-Type': ['application/json;charset=UTF-8'], - }, - status_code: 200, - finished: true, - headers_sent: true, - }, - version: '1.1', - }, - user_agent: { - original: 'Python/3.7 aiohttp/3.3.2', - name: 'Other', - device: { - name: 'Other', - }, - }, - transaction: { - duration: { - us: 237537, - }, - result: 'HTTP 2xx', - name: 'APIRestController#product', - span_count: { - dropped: 0, - started: 3, - }, - id: 'f2387d37260d00bd', - type: 'request', - sampled: true, - }, - timestamp: { - us: 1586432160786001, - }, - }, - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - parent: { - id: 'f2387d37260d00bd', - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.810Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - transaction: { - id: 'f2387d37260d00bd', - }, - span: { - duration: { - us: 204574, - }, - subtype: 'inferred', - name: 'ServletInvocableHandlerMethod#invokeAndHandle', - id: 'a5df600bd7bd5e38', - type: 'app', - }, - timestamp: { - us: 1586432160810441, - }, - }, - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - parent: { - id: 'a5df600bd7bd5e38', - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - type: 'apm-server', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.810Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - transaction: { - id: 'f2387d37260d00bd', - }, - timestamp: { - us: 1586432160810441, - }, - span: { - duration: { - us: 102993, - }, - stacktrace: [ - { - library_frame: true, - exclude_from_grouping: false, - filename: 'InvocableHandlerMethod.java', - line: { - number: -1, - }, - function: 'doInvoke', - }, - { - exclude_from_grouping: false, - library_frame: true, - filename: 'InvocableHandlerMethod.java', - line: { - number: -1, - }, - function: 'invokeForRequest', - }, - ], - subtype: 'inferred', - name: 'APIRestController#product', - id: '808dc34fc41ce522', - type: 'app', - }, - }, - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - parent: { - id: 'f2387d37260d00bd', - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - processor: { - name: 'transaction', - event: 'span', - }, - labels: { - productId: '2', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.832Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - transaction: { - id: 'f2387d37260d00bd', - }, - timestamp: { - us: 1586432160832300, - }, - span: { - duration: { - us: 99295, - }, - name: 'OpenTracing product span', - id: '41226ae63af4f235', - type: 'unknown', - }, - child: { id: ['8d80de06aa11a6fc'] }, - }, - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - parent: { - id: '808dc34fc41ce522', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.859Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - transaction: { - id: 'f2387d37260d00bd', - }, - timestamp: { - us: 1586432160859600, - }, - span: { - duration: { - us: 53835, - }, - subtype: 'inferred', - name: 'Loader#executeQueryStatement', - id: '8d80de06aa11a6fc', - type: 'app', - }, - }, - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - parent: { - id: '41226ae63af4f235', - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - destination: { - address: 'postgres', - port: 5432, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.903Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - transaction: { - id: 'f2387d37260d00bd', - }, - timestamp: { - us: 1586432160903236, - }, - span: { - duration: { - us: 10211, - }, - subtype: 'postgresql', - destination: { - service: { - resource: 'postgresql', - name: 'postgresql', - type: 'db', - }, - }, - name: 'SELECT FROM products', - action: 'query', - id: '3708d5623658182f', - type: 'db', - db: { - statement: - 'select product0_.id as col_0_0_, product0_.sku as col_1_0_, product0_.name as col_2_0_, product0_.description as col_3_0_, product0_.cost as col_4_0_, product0_.selling_price as col_5_0_, product0_.stock as col_6_0_, producttyp1_.id as col_7_0_, producttyp1_.name as col_8_0_, (select sum(orderline2_.amount) from order_lines orderline2_ where orderline2_.product_id=product0_.id) as col_9_0_ from products product0_ left outer join product_types producttyp1_ on product0_.type_id=producttyp1_.id where product0_.id=?', - type: 'sql', - user: { - name: 'postgres', - }, - }, - }, - }, - { - container: { - id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - parent: { - id: '41226ae63af4f235', - }, - process: { - pid: 6, - title: '/opt/java/openjdk/bin/java', - ppid: 1, - }, - agent: { - name: 'java', - ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', - version: '1.15.1-SNAPSHOT', - }, - destination: { - address: 'postgres', - port: 5432, - }, - processor: { - name: 'transaction', - event: 'span', - }, - observer: { - hostname: '7189f754b5a3', - id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', - ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', - type: 'apm-server', - version: '8.0.0', - version_major: 8, - }, - trace: { - id: '3b0dc77f3754e5bcb9da0e4c15e0db97', - }, - '@timestamp': '2020-04-09T11:36:00.859Z', - ecs: { - version: '1.5.0', - }, - service: { - node: { - name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', - }, - environment: 'production', - name: 'opbeans-java', - runtime: { - name: 'Java', - version: '11.0.6', - }, - language: { - name: 'Java', - version: '11.0.6', - }, - version: 'None', - }, - host: { - hostname: 'fc2ae281f56f', - os: { - platform: 'Linux', - }, - ip: '172.18.0.7', - name: 'fc2ae281f56f', - architecture: 'amd64', - }, - transaction: { - id: 'f2387d37260d00bd', - }, - timestamp: { - us: 1586432160859508, - }, - span: { - duration: { - us: 4503, - }, - subtype: 'postgresql', - destination: { - service: { - resource: 'postgresql', - name: 'postgresql', - type: 'db', - }, - }, - name: 'empty query', - action: 'query', - id: '9871cfd612368932', - type: 'db', - db: { - rows_affected: 0, - statement: '(empty query)', - type: 'sql', - user: { - name: 'postgres', - }, - }, - }, - }, - ], - exceedsMax: false, - errorDocs: [], -} as TraceAPIResponse; diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.data.ts b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.data.ts new file mode 100644 index 0000000000000..6cca3726a7d00 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.data.ts @@ -0,0 +1,5865 @@ +/* + * 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 type { Location } from 'history'; +import type { ApmUrlParams } from '../../../../../context/url_params_context/types'; +import { APIReturnType } from '../../../../../services/rest/createCallApmApi'; + +export const location = { + pathname: '/services/opbeans-go/transactions/view', + search: + '?rangeFrom=now-24h&rangeTo=now&refreshPaused=true&refreshInterval=0&kuery=service.name%253A%2520%2522opbeans-java%2522%2520or%2520service.name%2520%253A%2520%2522opbeans-go%2522&traceId=513d33fafe99bbe6134749310c9b5322&transactionId=975c8d5bfd1dd20b&transactionName=GET%20%2Fapi%2Forders&transactionType=request', + hash: '', +} as Location; + +type TraceAPIResponse = APIReturnType<'GET /internal/apm/traces/{traceId}'>; + +export const urlParams = { + start: '2020-03-22T15:16:38.742Z', + end: '2020-03-23T15:16:38.742Z', + rangeFrom: 'now-24h', + rangeTo: 'now', + refreshPaused: true, + refreshInterval: 0, + page: 0, + transactionId: '975c8d5bfd1dd20b', + traceId: '513d33fafe99bbe6134749310c9b5322', + transactionName: 'GET /api/orders', + transactionType: 'request', + processorEvent: 'transaction', + serviceName: 'opbeans-go', +} as ApmUrlParams; + +export const simpleTrace = { + traceDocs: [ + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT', + }, + internal: { + sampler: { + value: 46, + }, + }, + source: { + ip: '172.19.0.13', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: '172.19.0.9', + full: 'http://172.19.0.9:3000/api/orders', + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0', + }, + service: { + node: { + name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2', + }, + language: { + name: 'Java', + version: '10.0.2', + }, + version: 'None', + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux', + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64', + }, + http: { + request: { + headers: { + Accept: ['*/*'], + 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], + Host: ['172.19.0.9:3000'], + 'Accept-Encoding': ['gzip, deflate'], + }, + method: 'get', + socket: { + encrypted: false, + remote_address: '172.19.0.13', + }, + body: { + original: '[REDACTED]', + }, + }, + response: { + headers: { + 'Transfer-Encoding': ['chunked'], + Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], + 'Content-Type': ['application/json;charset=ISO-8859-1'], + }, + status_code: 200, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + client: { + ip: '172.19.0.13', + }, + transaction: { + duration: { + us: 18842, + }, + result: 'HTTP 2xx', + name: 'DispatcherServlet#doGet', + id: '49809ad3c26adf74', + span_count: { + dropped: 0, + started: 1, + }, + type: 'request', + sampled: true, + }, + user_agent: { + original: 'Python/3.7 aiohttp/3.3.2', + name: 'Other', + device: { + name: 'Other', + }, + }, + timestamp: { + us: 1584975868785000, + }, + }, + { + parent: { + id: 'fc107f7b556eb49b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + framework: { + name: 'gin', + version: 'v1.4.0', + }, + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + duration: { + us: 16597, + }, + result: 'HTTP 2xx', + name: 'GET /api/orders', + id: '975c8d5bfd1dd20b', + span_count: { + dropped: 0, + started: 1, + }, + type: 'request', + sampled: true, + }, + timestamp: { + us: 1584975868787052, + }, + }, + { + parent: { + id: 'daae24d83c269918', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + timestamp: { + us: 1584975868788603, + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders', + }, + '@timestamp': '2020-03-23T15:04:28.788Z', + service: { + node: { + name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13', + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10', + }, + language: { + name: 'python', + version: '3.6.10', + }, + version: 'None', + }, + transaction: { + result: 'HTTP 2xx', + duration: { + us: 14648, + }, + name: 'GET opbeans.views.orders', + span_count: { + dropped: 0, + started: 1, + }, + id: '6fb0ff7365b87298', + type: 'request', + sampled: true, + }, + }, + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + parent: { + id: '49809ad3c26adf74', + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT', + }, + internal: { + sampler: { + value: 44, + }, + }, + destination: { + address: 'opbeans-go', + port: 3000, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + type: 'apm-server', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0', + }, + service: { + node: { + name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2', + }, + language: { + name: 'Java', + version: '10.0.2', + }, + version: 'None', + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux', + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64', + }, + connection: { + hash: "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}", + }, + transaction: { + id: '49809ad3c26adf74', + }, + timestamp: { + us: 1584975868785273, + }, + span: { + duration: { + us: 17530, + }, + subtype: 'http', + name: 'GET opbeans-go', + destination: { + service: { + resource: 'opbeans-go:3000', + name: 'http://opbeans-go:3000', + type: 'external', + }, + }, + http: { + response: { + status_code: 200, + }, + url: { + original: 'http://opbeans-go:3000/api/orders', + }, + }, + id: 'fc107f7b556eb49b', + type: 'external', + }, + }, + { + parent: { + id: '975c8d5bfd1dd20b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + processor: { + name: 'transaction', + event: 'span', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + id: '975c8d5bfd1dd20b', + }, + timestamp: { + us: 1584975868787174, + }, + span: { + duration: { + us: 16250, + }, + subtype: 'http', + destination: { + service: { + resource: 'opbeans-python:3000', + name: 'http://opbeans-python:3000', + type: 'external', + }, + }, + name: 'GET opbeans-python:3000', + http: { + response: { + status_code: 200, + }, + url: { + original: 'http://opbeans-python:3000/api/orders', + }, + }, + id: 'daae24d83c269918', + type: 'external', + }, + }, + { + container: { + id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + parent: { + id: '6fb0ff7365b87298', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + processor: { + name: 'transaction', + event: 'span', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.790Z', + service: { + node: { + name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13', + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10', + }, + language: { + name: 'python', + version: '3.6.10', + }, + version: 'None', + }, + transaction: { + id: '6fb0ff7365b87298', + }, + timestamp: { + us: 1584975868790080, + }, + span: { + duration: { + us: 2519, + }, + subtype: 'postgresql', + name: 'SELECT FROM opbeans_order', + destination: { + service: { + resource: 'postgresql', + name: 'postgresql', + type: 'db', + }, + }, + action: 'query', + id: 'c9407abb4d08ead1', + type: 'db', + sync: true, + db: { + statement: + 'SELECT "opbeans_order"."id", "opbeans_order"."customer_id", "opbeans_customer"."full_name", "opbeans_order"."created_at" FROM "opbeans_order" INNER JOIN "opbeans_customer" ON ("opbeans_order"."customer_id" = "opbeans_customer"."id") LIMIT 1000', + type: 'sql', + }, + }, + }, + ], + exceedsMax: false, + errorDocs: [], +} as TraceAPIResponse; + +export const manyChildrenWithSameLength = { + exceedsMax: false, + traceDocs: [ + { + container: { + id: '46721e28e45ec1926798491069d8585865b031b4eaa9800e35d06fef6be5e170', + }, + kubernetes: { + pod: { + uid: '900f3cac-eb7c-4308-9376-f644f173c3ee', + }, + }, + process: { + args: ['-C', 'config/puma.rb'], + pid: 38, + title: '/usr/local/bundle/bin/puma', + }, + agent: { + name: 'ruby', + version: '4.3.0', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/products/3', + scheme: 'http', + port: 3000, + domain: '10.15.245.224', + full: 'http://10.15.245.224:3000/api/products/3', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'projects/8560181848/machineTypes/n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-zvs6p', + id: '69a7066f-46d2-42c4-a4cc-8400f60bf2b5', + ephemeral_id: '0ab88569-c301-40e9-8e78-cac7c1dac2bc', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.536Z', + ecs: { + version: '1.11.0', + }, + service: { + node: { + name: '46721e28e45ec1926798491069d8585865b031b4eaa9800e35d06fef6be5e170', + }, + environment: 'production', + framework: { + name: 'Ruby on Rails', + version: '6.1.4.1', + }, + name: 'opbeans-ruby', + runtime: { + name: 'ruby', + version: '2.7.3', + }, + language: { + name: 'ruby', + version: '2.7.3', + }, + version: '2021-10-14 17:49:53', + }, + host: { + os: { + platform: 'linux', + }, + ip: '10.12.0.22', + architecture: 'x86_64', + }, + http: { + request: { + headers: { + Accept: ['*/*'], + Version: ['HTTP/1.1'], + 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], + Host: ['10.15.245.224:3000'], + 'Accept-Encoding': ['gzip, deflate'], + }, + method: 'GET', + env: { + GATEWAY_INTERFACE: 'CGI/1.2', + ORIGINAL_FULLPATH: '/api/products/3', + SERVER_PORT: '3000', + SERVER_PROTOCOL: 'HTTP/1.1', + REMOTE_ADDR: '10.12.6.45', + REQUEST_URI: '/api/products/3', + ORIGINAL_SCRIPT_NAME: '', + SERVER_SOFTWARE: 'puma 5.5.0 Zawgyi', + QUERY_STRING: '', + SCRIPT_NAME: '', + REQUEST_METHOD: 'GET', + SERVER_NAME: '10.15.245.224', + REQUEST_PATH: '/api/products/3', + PATH_INFO: '/api/products/3', + ROUTES_9240_SCRIPT_NAME: '', + }, + body: { + original: '[SKIPPED]', + }, + }, + response: { + headers: { + 'Content-Type': ['application/json;charset=UTF-8'], + }, + status_code: 500, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + event: { + ingested: '2021-10-19T13:57:12.417144879Z', + outcome: 'failure', + }, + transaction: { + duration: { + us: 13359, + }, + result: 'HTTP 5xx', + name: 'Rack', + span_count: { + dropped: 0, + started: 1, + }, + id: '9a7f717439921d39', + type: 'request', + sampled: true, + }, + user_agent: { + original: 'Python/3.7 aiohttp/3.3.2', + name: 'Python aiohttp', + device: { + name: 'Other', + type: 'Other', + }, + version: '3.3.2', + }, + timestamp: { + us: 1634651822536408, + }, + }, + { + container: { + id: 'e7b69f99cb7523bedea6d7c97b684cf4b7ff458d0cba1efb1ac843300b3bf3c7', + }, + kubernetes: { + pod: { + uid: 'c5169b50-f3b3-4693-8e4b-150fca17c333', + name: 'opbeans-go-5d795ddf6b-rhlvf', + }, + }, + parent: { + id: '4eeaa6dfbfd047cd', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + source: { + ip: '10.12.0.22', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + type: 'apm-server', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + host: { + os: { + platform: 'linux', + }, + ip: '10.12.0.14', + architecture: 'amd64', + }, + client: { + ip: '10.12.0.22', + }, + event: { + ingested: '2021-10-19T13:57:05.413190788Z', + outcome: 'failure', + }, + user_agent: { + original: 'http.rb/5.0.2', + name: 'Other', + device: { + name: 'Generic Feature Phone', + type: 'Other', + }, + }, + timestamp: { + us: 1634651822536408, + }, + process: { + args: [ + '/opbeans-go', + '-log-level=debug', + '-log-json', + '-listen=:3000', + '-frontend=/opbeans-frontend', + '-db=postgres:', + '-cache=redis://redis-master:6379', + ], + pid: 1, + title: 'opbeans-go', + ppid: 0, + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/products/3', + scheme: 'http', + port: 3000, + domain: 'opbeans', + full: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.536Z', + service: { + node: { + name: 'e7b69f99cb7523bedea6d7c97b684cf4b7ff458d0cba1efb1ac843300b3bf3c7', + }, + environment: 'testing', + framework: { + name: 'gin', + version: 'v1.7.3', + }, + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.17.2', + }, + language: { + name: 'go', + version: 'go1.17.2', + }, + version: '2021-10-14 17:49:50', + }, + http: { + request: { + headers: { + Connection: ['close'], + 'User-Agent': ['http.rb/5.0.2'], + 'Elastic-Apm-Traceparent': [ + '00-d5e80ae688f1fef91533f02dd2bdc769-4eeaa6dfbfd047cd-01', + ], + Tracestate: ['es=s:1.0'], + Traceparent: [ + '00-d5e80ae688f1fef91533f02dd2bdc769-4eeaa6dfbfd047cd-01', + ], + }, + method: 'GET', + }, + response: { + headers: { + Date: ['Tue, 19 Oct 2021 13:57:02 GMT'], + 'Content-Type': ['application/json;charset=UTF-8'], + }, + status_code: 500, + }, + version: '1.1', + }, + transaction: { + result: 'HTTP 5xx', + duration: { + us: 13359, + }, + name: 'GET /api/products/:id', + id: '9f50f43e924d0b46', + span_count: { + dropped: 0, + started: 3, + }, + type: 'request', + sampled: true, + }, + }, + { + container: { + id: '015d1127421e2c3d42a0fb031fc75e989813f58973143b6c7e33dca6ccc6f31b', + }, + parent: { + id: '8d099ab4fcec4ab9', + }, + kubernetes: { + pod: { + uid: '459a6abf-198e-4107-b4dd-b0ae826755ab', + name: 'opbeans-go-nsn-69b89c4598-xsvgh', + }, + }, + agent: { + name: 'go', + version: '1.14.0', + }, + source: { + ip: '10.12.0.14', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-zvs6p', + id: '69a7066f-46d2-42c4-a4cc-8400f60bf2b5', + ephemeral_id: '0ab88569-c301-40e9-8e78-cac7c1dac2bc', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + host: { + os: { + platform: 'linux', + }, + ip: '10.12.0.13', + architecture: 'amd64', + }, + client: { + ip: '10.12.0.22', + }, + event: { + ingested: '2021-10-19T13:57:08.267103644Z', + outcome: 'failure', + }, + user_agent: { + original: 'http.rb/5.0.2', + name: 'Other', + device: { + name: 'Generic Feature Phone', + type: 'Other', + }, + }, + timestamp: { + us: 1634651822536408, + }, + process: { + args: [ + '/opbeans-go', + '-log-level=debug', + '-log-json', + '-listen=:3000', + '-frontend=/opbeans-frontend', + '-db=postgres:', + '-cache=redis://redis-master:6379', + ], + pid: 1, + title: 'opbeans-go', + ppid: 0, + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/products/3', + scheme: 'http', + port: 3000, + domain: 'opbeans', + full: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.536Z', + service: { + node: { + name: '015d1127421e2c3d42a0fb031fc75e989813f58973143b6c7e33dca6ccc6f31b', + }, + environment: 'testing', + framework: { + name: 'gin', + version: 'v1.7.3', + }, + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.17.2', + }, + language: { + name: 'go', + version: 'go1.17.2', + }, + version: '2021-10-14 17:49:50', + }, + http: { + request: { + headers: { + 'User-Agent': ['http.rb/5.0.2'], + 'X-Forwarded-For': ['10.12.0.22'], + 'Accept-Encoding': ['gzip'], + 'Elastic-Apm-Traceparent': [ + '00-d5e80ae688f1fef91533f02dd2bdc769-8d099ab4fcec4ab9-01', + ], + Tracestate: ['es=s:1.0'], + Traceparent: [ + '00-d5e80ae688f1fef91533f02dd2bdc769-8d099ab4fcec4ab9-01', + ], + }, + method: 'GET', + }, + response: { + headers: { + Date: ['Tue, 19 Oct 2021 13:57:02 GMT'], + 'Content-Type': ['application/json;charset=UTF-8'], + }, + status_code: 500, + }, + version: '1.1', + }, + transaction: { + result: 'HTTP 5xx', + duration: { + us: 13359, + }, + name: 'GET /api/products/:id', + span_count: { + dropped: 0, + started: 3, + }, + id: 'b7801be83bcdc972', + type: 'request', + sampled: true, + }, + }, + { + container: { + id: '59036ecb70908dfec4e03edc477f6875d08677871b4af0db3144373802d00cb1', + }, + kubernetes: { + pod: { + uid: '878bab2a-1309-44ae-a0e2-c98a0b187da1', + name: 'opbeans-java-5f45d77dd8-h8bnb', + }, + }, + parent: { + id: '35e3637e26919055', + }, + agent: { + name: 'java', + ephemeral_id: '75e36588-9adb-4bb0-bfee-a333b1c57e67', + version: 'unknown', + }, + source: { + ip: '10.12.0.13', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + host: { + os: { + platform: 'Linux', + }, + ip: '10.12.0.15', + architecture: 'amd64', + }, + client: { + ip: '10.12.0.22', + }, + event: { + ingested: '2021-10-19T13:57:10.382829210Z', + outcome: 'failure', + }, + user_agent: { + original: 'http.rb/5.0.2', + name: 'Other', + device: { + name: 'Generic Feature Phone', + type: 'Other', + }, + }, + timestamp: { + us: 1634651822536408, + }, + process: { + pid: 7, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/products/3', + scheme: 'http', + port: 3000, + domain: 'opbeans', + full: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.536Z', + service: { + node: { + name: '59036ecb70908dfec4e03edc477f6875d08677871b4af0db3144373802d00cb1', + }, + environment: 'production', + framework: { + name: 'Spring Web MVC', + version: '5.0.6.RELEASE', + }, + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.11', + }, + language: { + name: 'Java', + version: '11.0.11', + }, + version: '2021-10-14 17:49:52', + }, + http: { + request: { + headers: { + 'User-Agent': ['http.rb/5.0.2'], + 'X-Forwarded-For': ['10.12.0.22, 10.12.0.14'], + Host: ['opbeans:3000'], + 'Accept-Encoding': ['gzip'], + 'Elastic-Apm-Traceparent': [ + '00-d5e80ae688f1fef91533f02dd2bdc769-35e3637e26919055-01', + ], + Tracestate: ['es=s:1.0'], + Traceparent: [ + '00-d5e80ae688f1fef91533f02dd2bdc769-35e3637e26919055-01', + ], + }, + method: 'GET', + }, + response: { + status_code: 500, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + transaction: { + duration: { + us: 13359, + }, + result: 'HTTP 5xx', + name: 'APIRestController#product', + id: '2c30263c4ad8fe8b', + span_count: { + dropped: 0, + started: 3, + }, + type: 'request', + sampled: true, + }, + }, + { + parent: { + id: '9a7f717439921d39', + }, + agent: { + name: 'ruby', + version: '4.3.0', + }, + destination: { + address: 'opbeans', + port: 3000, + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'projects/8560181848/machineTypes/n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + event: { + outcome: 'failure', + }, + timestamp: { + us: 1634651822536408, + }, + processor: { + name: 'transaction', + event: 'span', + }, + url: { + original: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.536Z', + service: { + environment: 'production', + name: 'opbeans-ruby', + }, + http: { + request: { + method: 'GET', + }, + response: { + status_code: 500, + }, + }, + transaction: { + id: '9a7f717439921d39', + }, + span: { + duration: { + us: 13359, + }, + stacktrace: [ + { + exclude_from_grouping: false, + library_frame: true, + filename: 'elastic_apm.rb', + abs_path: + '/usr/local/bundle/gems/elastic-apm-4.3.0/lib/elastic_apm.rb', + line: { + number: 235, + }, + function: 'tap', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'elastic_apm.rb', + abs_path: + '/usr/local/bundle/gems/elastic-apm-4.3.0/lib/elastic_apm.rb', + line: { + number: 235, + }, + function: 'start_span', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'elastic_apm.rb', + abs_path: + '/usr/local/bundle/gems/elastic-apm-4.3.0/lib/elastic_apm.rb', + line: { + number: 287, + }, + function: 'with_span', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'elastic_apm/spies/http.rb', + abs_path: + '/usr/local/bundle/gems/elastic-apm-4.3.0/lib/elastic_apm/spies/http.rb', + line: { + number: 45, + }, + function: 'perform', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/usr/local/bundle/gems/http-5.0.2/lib/http/client.rb', + filename: 'http/client.rb', + line: { + number: 31, + }, + function: 'request', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'http/chainable.rb', + abs_path: '/usr/local/bundle/gems/http-5.0.2/lib/http/chainable.rb', + line: { + number: 75, + }, + function: 'request', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'http/chainable.rb', + abs_path: '/usr/local/bundle/gems/http-5.0.2/lib/http/chainable.rb', + line: { + number: 20, + }, + function: 'get', + }, + { + exclude_from_grouping: false, + filename: 'opbeans_shuffle.rb', + abs_path: '/app/lib/opbeans_shuffle.rb', + line: { + number: 23, + context: ' resp = HTTP.get("#{lucky_winner}#{path}")\n', + }, + function: 'block in call', + context: { + pre: ['\n', ' Timeout.timeout(15) do\n'], + post: ['\n', ' [\n'], + }, + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'timeout.rb', + abs_path: '/usr/local/lib/ruby/2.7.0/timeout.rb', + line: { + number: 95, + }, + function: 'block in timeout', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'timeout.rb', + abs_path: '/usr/local/lib/ruby/2.7.0/timeout.rb', + line: { + number: 33, + }, + function: 'block in catch', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/usr/local/lib/ruby/2.7.0/timeout.rb', + filename: 'timeout.rb', + line: { + number: 33, + }, + function: 'catch', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'timeout.rb', + abs_path: '/usr/local/lib/ruby/2.7.0/timeout.rb', + line: { + number: 33, + }, + function: 'catch', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'timeout.rb', + abs_path: '/usr/local/lib/ruby/2.7.0/timeout.rb', + line: { + number: 110, + }, + function: 'timeout', + }, + { + exclude_from_grouping: false, + filename: 'opbeans_shuffle.rb', + abs_path: '/app/lib/opbeans_shuffle.rb', + line: { + number: 22, + context: ' Timeout.timeout(15) do\n', + }, + function: 'call', + context: { + pre: [' end\n', '\n'], + post: [ + ' resp = HTTP.get("#{lucky_winner}#{path}")\n', + '\n', + ], + }, + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'elastic_apm/middleware.rb', + abs_path: + '/usr/local/bundle/gems/elastic-apm-4.3.0/lib/elastic_apm/middleware.rb', + line: { + number: 36, + }, + function: 'call', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'rails/engine.rb', + abs_path: + '/usr/local/bundle/gems/railties-6.1.4.1/lib/rails/engine.rb', + line: { + number: 539, + }, + function: 'call', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'puma/configuration.rb', + abs_path: + '/usr/local/bundle/gems/puma-5.5.0/lib/puma/configuration.rb', + line: { + number: 249, + }, + function: 'call', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/usr/local/bundle/gems/puma-5.5.0/lib/puma/request.rb', + filename: 'puma/request.rb', + line: { + number: 77, + }, + function: 'block in handle_request', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'puma/thread_pool.rb', + abs_path: + '/usr/local/bundle/gems/puma-5.5.0/lib/puma/thread_pool.rb', + line: { + number: 340, + }, + function: 'with_force_shutdown', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'puma/request.rb', + abs_path: '/usr/local/bundle/gems/puma-5.5.0/lib/puma/request.rb', + line: { + number: 76, + }, + function: 'handle_request', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'puma/server.rb', + abs_path: '/usr/local/bundle/gems/puma-5.5.0/lib/puma/server.rb', + line: { + number: 447, + }, + function: 'process_client', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'puma/thread_pool.rb', + abs_path: + '/usr/local/bundle/gems/puma-5.5.0/lib/puma/thread_pool.rb', + line: { + number: 147, + }, + function: 'block in spawn_thread', + }, + ], + subtype: 'http', + destination: { + service: { + resource: 'opbeans:3000', + name: 'http', + type: 'external', + }, + }, + name: 'GET opbeans', + http: { + method: 'GET', + response: { + status_code: 500, + }, + }, + 'http.url.original': 'http://opbeans:3000/api/products/3', + id: '4eeaa6dfbfd047cd', + type: 'external', + }, + }, + { + parent: { + id: '9f50f43e924d0b46', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + destination: { + address: 'opbeans', + port: 3000, + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + event: { + outcome: 'failure', + }, + timestamp: { + us: 1634651822536408, + }, + processor: { + name: 'transaction', + event: 'span', + }, + url: { + original: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.536Z', + service: { + environment: 'testing', + name: 'opbeans-go', + }, + http: { + response: { + status_code: 500, + }, + }, + transaction: { + id: '9f50f43e924d0b46', + }, + span: { + duration: { + us: 13359, + }, + stacktrace: [ + { + exclude_from_grouping: false, + library_frame: true, + filename: 'span.go', + abs_path: '/go/pkg/mod/go.elastic.co/apm@v1.14.0/span.go', + line: { + number: 334, + }, + module: 'go.elastic.co/apm', + function: '(*Span).End', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'client.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmhttp@v1.14.0/client.go', + line: { + number: 198, + }, + module: 'go.elastic.co/apm/module/apmhttp', + function: '(*responseBody).endSpan', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'client.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmhttp@v1.14.0/client.go', + line: { + number: 187, + }, + function: '(*responseBody).Read', + module: 'go.elastic.co/apm/module/apmhttp', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'reverseproxy.go', + abs_path: '/usr/local/go/src/net/http/httputil/reverseproxy.go', + line: { + number: 461, + }, + module: 'net/http/httputil', + function: '(*ReverseProxy).copyBuffer', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'reverseproxy.go', + abs_path: '/usr/local/go/src/net/http/httputil/reverseproxy.go', + line: { + number: 449, + }, + module: 'net/http/httputil', + function: '(*ReverseProxy).copyResponse', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'reverseproxy.go', + abs_path: '/usr/local/go/src/net/http/httputil/reverseproxy.go', + line: { + number: 338, + }, + module: 'net/http/httputil', + function: '(*ReverseProxy).ServeHTTP', + }, + { + exclude_from_grouping: false, + filename: 'main.go', + abs_path: '/src/opbeans-go/main.go', + line: { + number: 196, + }, + module: 'main', + function: 'Main.func2', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + filename: 'context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + exclude_from_grouping: false, + filename: 'main.go', + abs_path: '/src/opbeans-go/main.go', + line: { + number: 174, + }, + module: 'main', + function: 'Main.func1', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + exclude_from_grouping: false, + filename: 'logger.go', + abs_path: '/src/opbeans-go/logger.go', + line: { + number: 36, + }, + module: 'main', + function: 'logrusMiddleware', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'middleware.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmgin@v1.14.0/middleware.go', + line: { + number: 98, + }, + module: 'go.elastic.co/apm/module/apmgin', + function: '(*middleware).handle', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'cache.go', + abs_path: + '/go/pkg/mod/github.com/gin-contrib/cache@v1.1.0/cache.go', + line: { + number: 128, + }, + module: 'github.com/gin-contrib/cache', + function: 'Cache.func1', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'gin.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + line: { + number: 489, + }, + function: '(*Engine).handleHTTPRequest', + module: 'github.com/gin-gonic/gin', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'gin.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + line: { + number: 445, + }, + function: '(*Engine).ServeHTTP', + module: 'github.com/gin-gonic/gin', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'server.go', + abs_path: '/usr/local/go/src/net/http/server.go', + line: { + number: 2878, + }, + module: 'net/http', + function: 'serverHandler.ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'server.go', + abs_path: '/usr/local/go/src/net/http/server.go', + line: { + number: 1929, + }, + module: 'net/http', + function: '(*conn).serve', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'asm_amd64.s', + abs_path: '/usr/local/go/src/runtime/asm_amd64.s', + line: { + number: 1581, + }, + module: 'runtime', + function: 'goexit', + }, + ], + subtype: 'http', + name: 'GET opbeans:3000', + destination: { + service: { + resource: 'opbeans:3000', + name: 'http://opbeans:3000', + type: 'external', + }, + }, + http: { + response: { + status_code: 500, + }, + }, + 'http.url.original': 'http://opbeans:3000/api/products/3', + id: '8d099ab4fcec4ab9', + type: 'external', + }, + }, + { + parent: { + id: '86c43ac014573747', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + processor: { + name: 'transaction', + event: 'span', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + type: 'apm-server', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.539Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'testing', + name: 'opbeans-go', + }, + event: { + outcome: 'unknown', + }, + transaction: { + id: '9f50f43e924d0b46', + }, + span: { + duration: { + us: 13359, + }, + stacktrace: [ + { + exclude_from_grouping: false, + library_frame: true, + filename: 'span.go', + abs_path: '/go/pkg/mod/go.elastic.co/apm@v1.14.0/span.go', + line: { + number: 334, + }, + module: 'go.elastic.co/apm', + function: '(*Span).End', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'clienttrace.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmhttp@v1.14.0/clienttrace.go', + line: { + number: 130, + }, + module: 'go.elastic.co/apm/module/apmhttp', + function: 'withClientTrace.func8', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'transport.go', + abs_path: '/usr/local/go/src/net/http/transport.go', + line: { + number: 2272, + }, + function: '(*persistConn).readResponse', + module: 'net/http', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'transport.go', + abs_path: '/usr/local/go/src/net/http/transport.go', + line: { + number: 2102, + }, + function: '(*persistConn).readLoop', + module: 'net/http', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'asm_amd64.s', + abs_path: '/usr/local/go/src/runtime/asm_amd64.s', + line: { + number: 1581, + }, + module: 'runtime', + function: 'goexit', + }, + ], + subtype: 'http', + name: 'Request', + action: 'request', + id: '997cdcc26a60d0ad', + type: 'external', + }, + timestamp: { + us: 1634651822536408, + }, + }, + { + parent: { + id: 'b7801be83bcdc972', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + destination: { + address: 'opbeans', + port: 3000, + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-zvs6p', + id: '69a7066f-46d2-42c4-a4cc-8400f60bf2b5', + ephemeral_id: '0ab88569-c301-40e9-8e78-cac7c1dac2bc', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + event: { + outcome: 'failure', + }, + timestamp: { + us: 1634651822536408, + }, + processor: { + name: 'transaction', + event: 'span', + }, + url: { + original: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.539Z', + service: { + environment: 'testing', + name: 'opbeans-go', + }, + http: { + response: { + status_code: 500, + }, + }, + transaction: { + id: 'b7801be83bcdc972', + }, + span: { + duration: { + us: 13359, + }, + stacktrace: [ + { + library_frame: true, + exclude_from_grouping: false, + filename: 'span.go', + abs_path: '/go/pkg/mod/go.elastic.co/apm@v1.14.0/span.go', + line: { + number: 334, + }, + module: 'go.elastic.co/apm', + function: '(*Span).End', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'client.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmhttp@v1.14.0/client.go', + line: { + number: 198, + }, + module: 'go.elastic.co/apm/module/apmhttp', + function: '(*responseBody).endSpan', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'client.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmhttp@v1.14.0/client.go', + line: { + number: 187, + }, + module: 'go.elastic.co/apm/module/apmhttp', + function: '(*responseBody).Read', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'reverseproxy.go', + abs_path: '/usr/local/go/src/net/http/httputil/reverseproxy.go', + line: { + number: 461, + }, + module: 'net/http/httputil', + function: '(*ReverseProxy).copyBuffer', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'reverseproxy.go', + abs_path: '/usr/local/go/src/net/http/httputil/reverseproxy.go', + line: { + number: 449, + }, + module: 'net/http/httputil', + function: '(*ReverseProxy).copyResponse', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'reverseproxy.go', + abs_path: '/usr/local/go/src/net/http/httputil/reverseproxy.go', + line: { + number: 338, + }, + module: 'net/http/httputil', + function: '(*ReverseProxy).ServeHTTP', + }, + { + exclude_from_grouping: false, + filename: 'main.go', + abs_path: '/src/opbeans-go/main.go', + line: { + number: 196, + }, + module: 'main', + function: 'Main.func2', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + exclude_from_grouping: false, + filename: 'main.go', + abs_path: '/src/opbeans-go/main.go', + line: { + number: 174, + }, + module: 'main', + function: 'Main.func1', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + exclude_from_grouping: false, + filename: 'logger.go', + abs_path: '/src/opbeans-go/logger.go', + line: { + number: 36, + }, + module: 'main', + function: 'logrusMiddleware', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'middleware.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmgin@v1.14.0/middleware.go', + line: { + number: 98, + }, + function: '(*middleware).handle', + module: 'go.elastic.co/apm/module/apmgin', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: + '/go/pkg/mod/github.com/gin-contrib/cache@v1.1.0/cache.go', + filename: 'cache.go', + line: { + number: 128, + }, + module: 'github.com/gin-contrib/cache', + function: 'Cache.func1', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + function: '(*Context).Next', + module: 'github.com/gin-gonic/gin', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'gin.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + line: { + number: 489, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Engine).handleHTTPRequest', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'gin.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + line: { + number: 445, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Engine).ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/usr/local/go/src/net/http/server.go', + filename: 'server.go', + line: { + number: 2878, + }, + module: 'net/http', + function: 'serverHandler.ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/usr/local/go/src/net/http/server.go', + filename: 'server.go', + line: { + number: 1929, + }, + module: 'net/http', + function: '(*conn).serve', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'asm_amd64.s', + abs_path: '/usr/local/go/src/runtime/asm_amd64.s', + line: { + number: 1581, + }, + module: 'runtime', + function: 'goexit', + }, + ], + subtype: 'http', + name: 'GET opbeans:3000', + destination: { + service: { + resource: 'opbeans:3000', + name: 'http://opbeans:3000', + type: 'external', + }, + }, + http: { + response: { + status_code: 500, + }, + }, + 'http.url.original': 'http://opbeans:3000/api/products/3', + id: '35e3637e26919055', + type: 'external', + }, + }, + { + parent: { + id: '84749ec73b1268b3', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + processor: { + name: 'transaction', + event: 'span', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-zvs6p', + id: '69a7066f-46d2-42c4-a4cc-8400f60bf2b5', + type: 'apm-server', + ephemeral_id: '0ab88569-c301-40e9-8e78-cac7c1dac2bc', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.539Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'testing', + name: 'opbeans-go', + }, + event: { + outcome: 'unknown', + }, + transaction: { + id: 'b7801be83bcdc972', + }, + span: { + duration: { + us: 13359, + }, + stacktrace: [ + { + library_frame: true, + exclude_from_grouping: false, + filename: 'span.go', + abs_path: '/go/pkg/mod/go.elastic.co/apm@v1.14.0/span.go', + line: { + number: 334, + }, + module: 'go.elastic.co/apm', + function: '(*Span).End', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'clienttrace.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmhttp@v1.14.0/clienttrace.go', + line: { + number: 130, + }, + module: 'go.elastic.co/apm/module/apmhttp', + function: 'withClientTrace.func8', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'transport.go', + abs_path: '/usr/local/go/src/net/http/transport.go', + line: { + number: 2272, + }, + module: 'net/http', + function: '(*persistConn).readResponse', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'transport.go', + abs_path: '/usr/local/go/src/net/http/transport.go', + line: { + number: 2102, + }, + module: 'net/http', + function: '(*persistConn).readLoop', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'asm_amd64.s', + abs_path: '/usr/local/go/src/runtime/asm_amd64.s', + line: { + number: 1581, + }, + module: 'runtime', + function: 'goexit', + }, + ], + subtype: 'http', + name: 'Request', + action: 'request', + id: 'a9b4d44c3d699cbb', + type: 'external', + }, + timestamp: { + us: 1634651822536408, + }, + }, + { + parent: { + id: '2c30263c4ad8fe8b', + }, + agent: { + name: 'java', + ephemeral_id: '75e36588-9adb-4bb0-bfee-a333b1c57e67', + version: 'unknown', + }, + processor: { + name: 'transaction', + event: 'span', + }, + labels: { + productId: '3', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.540Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'production', + name: 'opbeans-java', + }, + event: { + outcome: 'success', + }, + transaction: { + id: '2c30263c4ad8fe8b', + }, + span: { + duration: { + us: 13359, + }, + name: 'OpenTracing product span', + id: 'd22c1e48b2489017', + type: 'custom', + }, + timestamp: { + us: 1634651822536408, + }, + }, + { + parent: { + id: 'd22c1e48b2489017', + }, + agent: { + name: 'java', + ephemeral_id: '75e36588-9adb-4bb0-bfee-a333b1c57e67', + version: 'unknown', + }, + destination: { + address: 'db-postgresql', + port: 5432, + }, + processor: { + name: 'transaction', + event: 'span', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + type: 'apm-server', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.542Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'production', + name: 'opbeans-java', + }, + event: { + outcome: 'success', + }, + transaction: { + id: '2c30263c4ad8fe8b', + }, + timestamp: { + us: 1634651822536408, + }, + span: { + duration: { + us: 13359, + }, + subtype: 'postgresql', + destination: { + service: { + resource: 'postgresql', + }, + }, + name: 'SELECT FROM products', + action: 'query', + id: '3851260ca4365f9e', + type: 'db', + db: { + instance: 'opbeans-java', + statement: + 'select product0_.id as col_0_0_, product0_.sku as col_1_0_, product0_.name as col_2_0_, product0_.description as col_3_0_, product0_.cost as col_4_0_, product0_.selling_price as col_5_0_, product0_.stock as col_6_0_, producttyp1_.id as col_7_0_, producttyp1_.name as col_8_0_, (select sum(orderline2_.amount) from order_lines orderline2_ where orderline2_.product_id=product0_.id) as col_9_0_ from products product0_ left outer join product_types producttyp1_ on product0_.type_id=producttyp1_.id where product0_.id=?', + type: 'sql', + user: { + name: 'elastic', + }, + }, + }, + }, + { + parent: { + id: '3851260ca4365f9e', + }, + agent: { + name: 'java', + ephemeral_id: '75e36588-9adb-4bb0-bfee-a333b1c57e67', + version: 'unknown', + }, + destination: { + address: 'db-postgresql', + port: 5432, + }, + processor: { + name: 'transaction', + event: 'span', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + type: 'apm-server', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.541Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'production', + name: 'opbeans-java', + }, + event: { + outcome: 'success', + }, + transaction: { + id: '2c30263c4ad8fe8b', + }, + span: { + duration: { + us: 13359, + }, + subtype: 'postgresql', + destination: { + service: { + resource: 'postgresql', + }, + }, + name: 'empty query', + action: 'query', + id: '86c43ac014573747', + type: 'db', + db: { + rows_affected: 0, + instance: 'opbeans-java', + statement: '(empty query)', + type: 'sql', + user: { + name: 'elastic', + }, + }, + }, + timestamp: { + us: 1634651822536408, + }, + }, + { + parent: { + id: '997cdcc26a60d0ad', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + processor: { + name: 'transaction', + event: 'span', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + type: 'apm-server', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.548Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'testing', + name: 'opbeans-go', + }, + event: { + outcome: 'unknown', + }, + transaction: { + id: '9f50f43e924d0b46', + }, + timestamp: { + us: 1634651822536408, + }, + span: { + duration: { + us: 13359, + }, + subtype: 'http', + name: 'Response', + action: 'response', + id: '84749ec73b1268b3', + type: 'external', + }, + }, + { + parent: { + id: 'a9b4d44c3d699cbb', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + processor: { + name: 'transaction', + event: 'span', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-zvs6p', + id: '69a7066f-46d2-42c4-a4cc-8400f60bf2b5', + type: 'apm-server', + ephemeral_id: '0ab88569-c301-40e9-8e78-cac7c1dac2bc', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.547Z', + ecs: { + version: '1.11.0', + }, + service: { + environment: 'testing', + name: 'opbeans-go', + }, + event: { + outcome: 'unknown', + }, + transaction: { + id: 'b7801be83bcdc972', + }, + span: { + duration: { + us: 13359, + }, + subtype: 'http', + name: 'Response', + action: 'response', + id: '04991f3b9d3696c5', + type: 'external', + }, + timestamp: { + us: 1634651822536408, + }, + }, + ], + errorDocs: [ + { + container: { + id: '59036ecb70908dfec4e03edc477f6875d08677871b4af0db3144373802d00cb1', + }, + kubernetes: { + pod: { + uid: '878bab2a-1309-44ae-a0e2-c98a0b187da1', + name: 'opbeans-java-5f45d77dd8-h8bnb', + }, + }, + parent: { + id: '2c30263c4ad8fe8b', + }, + agent: { + name: 'java', + ephemeral_id: '75e36588-9adb-4bb0-bfee-a333b1c57e67', + version: 'unknown', + }, + source: { + ip: '10.12.0.13', + }, + error: { + exception: [ + { + stacktrace: [ + { + exclude_from_grouping: false, + library_frame: true, + filename: 'AbstractMessageConverterMethodProcessor.java', + classname: + 'org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor', + line: { + number: 226, + }, + module: 'org.springframework.web.servlet.mvc.method.annotation', + function: 'writeWithMessageConverters', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'RequestResponseBodyMethodProcessor.java', + classname: + 'org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor', + line: { + number: 180, + }, + module: 'org.springframework.web.servlet.mvc.method.annotation', + function: 'handleReturnValue', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'HandlerMethodReturnValueHandlerComposite.java', + classname: + 'org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite', + line: { + number: 82, + }, + module: 'org.springframework.web.method.support', + function: 'handleReturnValue', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ServletInvocableHandlerMethod.java', + classname: + 'org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod', + line: { + number: 119, + }, + module: 'org.springframework.web.servlet.mvc.method.annotation', + function: 'invokeAndHandle', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'RequestMappingHandlerAdapter.java', + classname: + 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter', + line: { + number: 877, + }, + function: 'invokeHandlerMethod', + module: 'org.springframework.web.servlet.mvc.method.annotation', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'RequestMappingHandlerAdapter.java', + classname: + 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter', + line: { + number: 783, + }, + module: 'org.springframework.web.servlet.mvc.method.annotation', + function: 'handleInternal', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'AbstractHandlerMethodAdapter.java', + classname: + 'org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter', + line: { + number: 87, + }, + module: 'org.springframework.web.servlet.mvc.method', + function: 'handle', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'DispatcherServlet.java', + classname: 'org.springframework.web.servlet.DispatcherServlet', + line: { + number: 991, + }, + function: 'doDispatch', + module: 'org.springframework.web.servlet', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'DispatcherServlet.java', + classname: 'org.springframework.web.servlet.DispatcherServlet', + line: { + number: 925, + }, + module: 'org.springframework.web.servlet', + function: 'doService', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'FrameworkServlet.java', + classname: 'org.springframework.web.servlet.FrameworkServlet', + line: { + number: 974, + }, + module: 'org.springframework.web.servlet', + function: 'processRequest', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'FrameworkServlet.java', + classname: 'org.springframework.web.servlet.FrameworkServlet', + line: { + number: 866, + }, + module: 'org.springframework.web.servlet', + function: 'doGet', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'HttpServlet.java', + classname: 'javax.servlet.http.HttpServlet', + line: { + number: 635, + }, + module: 'javax.servlet.http', + function: 'service', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'FrameworkServlet.java', + classname: 'org.springframework.web.servlet.FrameworkServlet', + line: { + number: 851, + }, + function: 'service', + module: 'org.springframework.web.servlet', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'HttpServlet.java', + classname: 'javax.servlet.http.HttpServlet', + line: { + number: 742, + }, + module: 'javax.servlet.http', + function: 'service', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 231, + }, + module: 'org.apache.catalina.core', + function: 'internalDoFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 166, + }, + module: 'org.apache.catalina.core', + function: 'doFilter', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'WsFilter.java', + classname: 'org.apache.tomcat.websocket.server.WsFilter', + line: { + number: 52, + }, + module: 'org.apache.tomcat.websocket.server', + function: 'doFilter', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 193, + }, + module: 'org.apache.catalina.core', + function: 'internalDoFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 166, + }, + module: 'org.apache.catalina.core', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'RequestContextFilter.java', + classname: + 'org.springframework.web.filter.RequestContextFilter', + line: { + number: 99, + }, + function: 'doFilterInternal', + module: 'org.springframework.web.filter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'OncePerRequestFilter.java', + classname: + 'org.springframework.web.filter.OncePerRequestFilter', + line: { + number: 107, + }, + module: 'org.springframework.web.filter', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 193, + }, + module: 'org.apache.catalina.core', + function: 'internalDoFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 166, + }, + module: 'org.apache.catalina.core', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'HttpPutFormContentFilter.java', + classname: + 'org.springframework.web.filter.HttpPutFormContentFilter', + line: { + number: 109, + }, + function: 'doFilterInternal', + module: 'org.springframework.web.filter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'OncePerRequestFilter.java', + classname: + 'org.springframework.web.filter.OncePerRequestFilter', + line: { + number: 107, + }, + module: 'org.springframework.web.filter', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 193, + }, + module: 'org.apache.catalina.core', + function: 'internalDoFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 166, + }, + module: 'org.apache.catalina.core', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'HiddenHttpMethodFilter.java', + classname: + 'org.springframework.web.filter.HiddenHttpMethodFilter', + line: { + number: 81, + }, + function: 'doFilterInternal', + module: 'org.springframework.web.filter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'OncePerRequestFilter.java', + classname: + 'org.springframework.web.filter.OncePerRequestFilter', + line: { + number: 107, + }, + module: 'org.springframework.web.filter', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 193, + }, + module: 'org.apache.catalina.core', + function: 'internalDoFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 166, + }, + module: 'org.apache.catalina.core', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'CharacterEncodingFilter.java', + classname: + 'org.springframework.web.filter.CharacterEncodingFilter', + line: { + number: 200, + }, + module: 'org.springframework.web.filter', + function: 'doFilterInternal', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'OncePerRequestFilter.java', + classname: + 'org.springframework.web.filter.OncePerRequestFilter', + line: { + number: 107, + }, + module: 'org.springframework.web.filter', + function: 'doFilter', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 193, + }, + module: 'org.apache.catalina.core', + function: 'internalDoFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ApplicationFilterChain.java', + classname: 'org.apache.catalina.core.ApplicationFilterChain', + line: { + number: 166, + }, + module: 'org.apache.catalina.core', + function: 'doFilter', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'StandardWrapperValve.java', + classname: 'org.apache.catalina.core.StandardWrapperValve', + line: { + number: 198, + }, + module: 'org.apache.catalina.core', + function: 'invoke', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'StandardContextValve.java', + classname: 'org.apache.catalina.core.StandardContextValve', + line: { + number: 96, + }, + module: 'org.apache.catalina.core', + function: 'invoke', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'AuthenticatorBase.java', + classname: + 'org.apache.catalina.authenticator.AuthenticatorBase', + line: { + number: 496, + }, + module: 'org.apache.catalina.authenticator', + function: 'invoke', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'StandardHostValve.java', + classname: 'org.apache.catalina.core.StandardHostValve', + line: { + number: 140, + }, + module: 'org.apache.catalina.core', + function: 'invoke', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'ErrorReportValve.java', + classname: 'org.apache.catalina.valves.ErrorReportValve', + line: { + number: 81, + }, + module: 'org.apache.catalina.valves', + function: 'invoke', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'StandardEngineValve.java', + classname: 'org.apache.catalina.core.StandardEngineValve', + line: { + number: 87, + }, + module: 'org.apache.catalina.core', + function: 'invoke', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'CoyoteAdapter.java', + classname: 'org.apache.catalina.connector.CoyoteAdapter', + line: { + number: 342, + }, + module: 'org.apache.catalina.connector', + function: 'service', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'Http11Processor.java', + classname: 'org.apache.coyote.http11.Http11Processor', + line: { + number: 803, + }, + module: 'org.apache.coyote.http11', + function: 'service', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'AbstractProcessorLight.java', + classname: 'org.apache.coyote.AbstractProcessorLight', + line: { + number: 66, + }, + module: 'org.apache.coyote', + function: 'process', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'AbstractProtocol.java', + classname: + 'org.apache.coyote.AbstractProtocol$ConnectionHandler', + line: { + number: 790, + }, + module: 'org.apache.coyote', + function: 'process', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'NioEndpoint.java', + classname: + 'org.apache.tomcat.util.net.NioEndpoint$SocketProcessor', + line: { + number: 1468, + }, + module: 'org.apache.tomcat.util.net', + function: 'doRun', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'SocketProcessorBase.java', + classname: 'org.apache.tomcat.util.net.SocketProcessorBase', + line: { + number: 49, + }, + module: 'org.apache.tomcat.util.net', + function: 'run', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'TaskThread.java', + classname: + 'org.apache.tomcat.util.threads.TaskThread$WrappingRunnable', + line: { + number: 61, + }, + module: 'org.apache.tomcat.util.threads', + function: 'run', + }, + ], + message: + 'No converter found for return value of type: class com.sun.proxy.$Proxy158', + type: 'org.springframework.http.converter.HttpMessageNotWritableException', + }, + ], + id: '128f8ecf47bc8a800269ee6e5ac90008', + grouping_key: 'cc9272d7511c88a533ac41cc3e2ce54b', + grouping_name: + 'No converter found for return value of type: class com.sun.proxy.$Proxy158', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + ecs: { + version: '1.11.0', + }, + host: { + os: { + platform: 'Linux', + }, + ip: '10.12.0.15', + architecture: 'amd64', + }, + client: { + ip: '10.12.0.22', + }, + event: { + ingested: '2021-10-19T13:57:10.382394342Z', + }, + user_agent: { + original: 'http.rb/5.0.2', + name: 'Other', + device: { + name: 'Generic Feature Phone', + type: 'Other', + }, + }, + timestamp: { + us: 1634651822536408, + }, + process: { + pid: 7, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + message: + 'No converter found for return value of type: class com.sun.proxy.$Proxy158', + processor: { + name: 'error', + event: 'error', + }, + url: { + path: '/api/products/3', + scheme: 'http', + port: 3000, + domain: 'opbeans', + full: 'http://opbeans:3000/api/products/3', + }, + '@timestamp': '2021-10-19T13:57:02.546Z', + service: { + node: { + name: '59036ecb70908dfec4e03edc477f6875d08677871b4af0db3144373802d00cb1', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.11', + }, + language: { + name: 'Java', + version: '11.0.11', + }, + version: '2021-10-14 17:49:52', + }, + http: { + request: { + headers: { + 'User-Agent': ['http.rb/5.0.2'], + 'X-Forwarded-For': ['10.12.0.22, 10.12.0.14'], + Host: ['opbeans:3000'], + 'Accept-Encoding': ['gzip'], + 'Elastic-Apm-Traceparent': [ + '00-d5e80ae688f1fef91533f02dd2bdc769-35e3637e26919055-01', + ], + Tracestate: ['es=s:1.0'], + Traceparent: [ + '00-d5e80ae688f1fef91533f02dd2bdc769-35e3637e26919055-01', + ], + }, + method: 'GET', + }, + response: { + status_code: 500, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + transaction: { + id: '2c30263c4ad8fe8b', + type: 'request', + sampled: true, + }, + }, + { + container: { + id: 'e7b69f99cb7523bedea6d7c97b684cf4b7ff458d0cba1efb1ac843300b3bf3c7', + }, + kubernetes: { + pod: { + uid: 'c5169b50-f3b3-4693-8e4b-150fca17c333', + name: 'opbeans-go-5d795ddf6b-rhlvf', + }, + }, + parent: { + id: '9f50f43e924d0b46', + }, + agent: { + name: 'go', + version: '1.14.0', + }, + process: { + args: [ + '/opbeans-go', + '-log-level=debug', + '-log-json', + '-listen=:3000', + '-frontend=/opbeans-frontend', + '-db=postgres:', + '-cache=redis://redis-master:6379', + ], + pid: 1, + title: 'opbeans-go', + ppid: 0, + }, + error: { + culprit: 'logrusMiddleware', + log: { + stacktrace: [ + { + library_frame: true, + exclude_from_grouping: false, + filename: 'hook.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmlogrus@v1.14.0/hook.go', + line: { + number: 102, + }, + module: 'go.elastic.co/apm/module/apmlogrus', + function: '(*Hook).Fire', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'hooks.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/hooks.go', + line: { + number: 28, + }, + module: 'github.com/sirupsen/logrus', + function: 'LevelHooks.Fire', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + filename: 'entry.go', + line: { + number: 272, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).fireHooks', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 241, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).log', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 293, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).Log', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 338, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).Logf', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 367, + }, + function: '(*Entry).Errorf', + module: 'github.com/sirupsen/logrus', + }, + { + exclude_from_grouping: false, + filename: 'logger.go', + abs_path: '/src/opbeans-go/logger.go', + line: { + number: 56, + }, + module: 'main', + function: 'logrusMiddleware', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: + '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + filename: 'context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmgin@v1.14.0/middleware.go', + filename: 'middleware.go', + line: { + number: 98, + }, + module: 'go.elastic.co/apm/module/apmgin', + function: '(*middleware).handle', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: + '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + function: '(*Context).Next', + module: 'github.com/gin-gonic/gin', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'cache.go', + abs_path: + '/go/pkg/mod/github.com/gin-contrib/cache@v1.1.0/cache.go', + line: { + number: 128, + }, + module: 'github.com/gin-contrib/cache', + function: 'Cache.func1', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: + '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'gin.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + line: { + number: 489, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Engine).handleHTTPRequest', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + filename: 'gin.go', + line: { + number: 445, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Engine).ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'server.go', + abs_path: '/usr/local/go/src/net/http/server.go', + line: { + number: 2878, + }, + module: 'net/http', + function: 'serverHandler.ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'server.go', + abs_path: '/usr/local/go/src/net/http/server.go', + line: { + number: 1929, + }, + module: 'net/http', + function: '(*conn).serve', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'asm_amd64.s', + abs_path: '/usr/local/go/src/runtime/asm_amd64.s', + line: { + number: 1581, + }, + module: 'runtime', + function: 'goexit', + }, + ], + level: 'error', + message: 'GET /api/products/3 (500)', + }, + id: '1660f82c1340f415e9a31b47565300ee', + grouping_key: '7a640436a9be648fd708703d1ac84650', + grouping_name: 'GET /api/products/3 (500)', + }, + message: 'GET /api/products/3 (500)', + processor: { + name: 'error', + event: 'error', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-lmf6c', + id: '7eedab18-1171-4a1b-a590-975e13fd103a', + ephemeral_id: '90034868-48e6-418c-8ab4-6616b403bca7', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.539Z', + ecs: { + version: '1.11.0', + }, + service: { + node: { + name: 'e7b69f99cb7523bedea6d7c97b684cf4b7ff458d0cba1efb1ac843300b3bf3c7', + }, + environment: 'testing', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.17.2', + }, + language: { + name: 'go', + version: 'go1.17.2', + }, + version: '2021-10-14 17:49:50', + }, + host: { + os: { + platform: 'linux', + }, + ip: '10.12.0.14', + architecture: 'amd64', + }, + event: { + ingested: '2021-10-19T13:57:05.412811279Z', + }, + transaction: { + id: '9f50f43e924d0b46', + }, + timestamp: { + us: 1634651822536408, + }, + }, + { + container: { + id: '015d1127421e2c3d42a0fb031fc75e989813f58973143b6c7e33dca6ccc6f31b', + }, + kubernetes: { + pod: { + uid: '459a6abf-198e-4107-b4dd-b0ae826755ab', + name: 'opbeans-go-nsn-69b89c4598-xsvgh', + }, + }, + parent: { + id: 'b7801be83bcdc972', + }, + process: { + args: [ + '/opbeans-go', + '-log-level=debug', + '-log-json', + '-listen=:3000', + '-frontend=/opbeans-frontend', + '-db=postgres:', + '-cache=redis://redis-master:6379', + ], + pid: 1, + title: 'opbeans-go', + ppid: 0, + }, + agent: { + name: 'go', + version: '1.14.0', + }, + message: 'GET /api/products/3 (500)', + error: { + culprit: 'logrusMiddleware', + log: { + stacktrace: [ + { + library_frame: true, + exclude_from_grouping: false, + filename: 'hook.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmlogrus@v1.14.0/hook.go', + line: { + number: 102, + }, + module: 'go.elastic.co/apm/module/apmlogrus', + function: '(*Hook).Fire', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'hooks.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/hooks.go', + line: { + number: 28, + }, + function: 'LevelHooks.Fire', + module: 'github.com/sirupsen/logrus', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 272, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).fireHooks', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 241, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).log', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 293, + }, + function: '(*Entry).Log', + module: 'github.com/sirupsen/logrus', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 338, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).Logf', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'entry.go', + abs_path: + '/go/pkg/mod/github.com/sirupsen/logrus@v1.8.1/entry.go', + line: { + number: 367, + }, + module: 'github.com/sirupsen/logrus', + function: '(*Entry).Errorf', + }, + { + exclude_from_grouping: false, + filename: 'logger.go', + abs_path: '/src/opbeans-go/logger.go', + line: { + number: 56, + }, + module: 'main', + function: 'logrusMiddleware', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: + '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'middleware.go', + abs_path: + '/go/pkg/mod/go.elastic.co/apm/module/apmgin@v1.14.0/middleware.go', + line: { + number: 98, + }, + module: 'go.elastic.co/apm/module/apmgin', + function: '(*middleware).handle', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: + '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'cache.go', + abs_path: + '/go/pkg/mod/github.com/gin-contrib/cache@v1.1.0/cache.go', + line: { + number: 128, + }, + module: 'github.com/gin-contrib/cache', + function: 'Cache.func1', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'context.go', + abs_path: + '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/context.go', + line: { + number: 165, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Context).Next', + }, + { + library_frame: true, + exclude_from_grouping: false, + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + filename: 'gin.go', + line: { + number: 489, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Engine).handleHTTPRequest', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'gin.go', + abs_path: '/go/pkg/mod/github.com/gin-gonic/gin@v1.7.4/gin.go', + line: { + number: 445, + }, + module: 'github.com/gin-gonic/gin', + function: '(*Engine).ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'server.go', + abs_path: '/usr/local/go/src/net/http/server.go', + line: { + number: 2878, + }, + module: 'net/http', + function: 'serverHandler.ServeHTTP', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'server.go', + abs_path: '/usr/local/go/src/net/http/server.go', + line: { + number: 1929, + }, + module: 'net/http', + function: '(*conn).serve', + }, + { + library_frame: true, + exclude_from_grouping: false, + filename: 'asm_amd64.s', + abs_path: '/usr/local/go/src/runtime/asm_amd64.s', + line: { + number: 1581, + }, + function: 'goexit', + module: 'runtime', + }, + ], + level: 'error', + message: 'GET /api/products/3 (500)', + }, + id: '7a265a869ad88851591e0e9734aa4a70', + grouping_key: '7a640436a9be648fd708703d1ac84650', + grouping_name: 'GET /api/products/3 (500)', + }, + processor: { + name: 'error', + event: 'error', + }, + cloud: { + availability_zone: 'us-central1-c', + instance: { + name: 'gke-dev-oblt-dev-oblt-pool-18e89389-qntq', + id: '5278603844673466232', + }, + provider: 'gcp', + machine: { + type: 'n1-standard-4', + }, + project: { + name: 'elastic-observability', + id: '8560181848', + }, + region: 'us-central1', + }, + observer: { + hostname: 'apm-apm-server-65d9d8dd68-zvs6p', + id: '69a7066f-46d2-42c4-a4cc-8400f60bf2b5', + ephemeral_id: '0ab88569-c301-40e9-8e78-cac7c1dac2bc', + type: 'apm-server', + version: '7.16.0', + version_major: 7, + }, + trace: { + id: 'd5e80ae688f1fef91533f02dd2bdc769', + }, + '@timestamp': '2021-10-19T13:57:02.539Z', + ecs: { + version: '1.11.0', + }, + service: { + node: { + name: '015d1127421e2c3d42a0fb031fc75e989813f58973143b6c7e33dca6ccc6f31b', + }, + environment: 'testing', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.17.2', + }, + language: { + name: 'go', + version: 'go1.17.2', + }, + version: '2021-10-14 17:49:50', + }, + host: { + os: { + platform: 'linux', + }, + ip: '10.12.0.13', + architecture: 'amd64', + }, + event: { + ingested: '2021-10-19T13:57:08.266888578Z', + }, + transaction: { + id: 'b7801be83bcdc972', + }, + timestamp: { + us: 1634651822536408, + }, + }, + ], +} as TraceAPIResponse; + +export const traceWithErrors = { + traceDocs: [ + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT', + }, + internal: { + sampler: { + value: 46, + }, + }, + source: { + ip: '172.19.0.13', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: '172.19.0.9', + full: 'http://172.19.0.9:3000/api/orders', + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0', + }, + service: { + node: { + name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2', + }, + language: { + name: 'Java', + version: '10.0.2', + }, + version: 'None', + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux', + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64', + }, + http: { + request: { + headers: { + Accept: ['*/*'], + 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], + Host: ['172.19.0.9:3000'], + 'Accept-Encoding': ['gzip, deflate'], + }, + method: 'get', + socket: { + encrypted: false, + remote_address: '172.19.0.13', + }, + body: { + original: '[REDACTED]', + }, + }, + response: { + headers: { + 'Transfer-Encoding': ['chunked'], + Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], + 'Content-Type': ['application/json;charset=ISO-8859-1'], + }, + status_code: 200, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + client: { + ip: '172.19.0.13', + }, + transaction: { + duration: { + us: 18842, + }, + result: 'HTTP 2xx', + name: 'DispatcherServlet#doGet', + id: '49809ad3c26adf74', + span_count: { + dropped: 0, + started: 1, + }, + type: 'request', + sampled: true, + }, + user_agent: { + original: 'Python/3.7 aiohttp/3.3.2', + name: 'Other', + device: { + name: 'Other', + }, + }, + timestamp: { + us: 1584975868785000, + }, + }, + { + parent: { + id: 'fc107f7b556eb49b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + framework: { + name: 'gin', + version: 'v1.4.0', + }, + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + duration: { + us: 16597, + }, + result: 'HTTP 2xx', + name: 'GET /api/orders', + id: '975c8d5bfd1dd20b', + span_count: { + dropped: 0, + started: 1, + }, + type: 'request', + sampled: true, + }, + timestamp: { + us: 1584975868787052, + }, + }, + { + parent: { + id: 'daae24d83c269918', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + timestamp: { + us: 1584975868788603, + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders', + }, + '@timestamp': '2020-03-23T15:04:28.788Z', + service: { + node: { + name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13', + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10', + }, + language: { + name: 'python', + version: '3.6.10', + }, + version: 'None', + }, + transaction: { + result: 'HTTP 2xx', + duration: { + us: 14648, + }, + name: 'GET opbeans.views.orders', + span_count: { + dropped: 0, + started: 1, + }, + id: '6fb0ff7365b87298', + type: 'request', + sampled: true, + }, + }, + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + parent: { + id: '49809ad3c26adf74', + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT', + }, + internal: { + sampler: { + value: 44, + }, + }, + destination: { + address: 'opbeans-go', + port: 3000, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + type: 'apm-server', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0', + }, + service: { + node: { + name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2', + }, + language: { + name: 'Java', + version: '10.0.2', + }, + version: 'None', + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux', + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64', + }, + connection: { + hash: "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}", + }, + transaction: { + id: '49809ad3c26adf74', + }, + timestamp: { + us: 1584975868785273, + }, + span: { + duration: { + us: 17530, + }, + subtype: 'http', + name: 'GET opbeans-go', + destination: { + service: { + resource: 'opbeans-go:3000', + name: 'http://opbeans-go:3000', + type: 'external', + }, + }, + http: { + response: { + status_code: 200, + }, + url: { + original: 'http://opbeans-go:3000/api/orders', + }, + }, + id: 'fc107f7b556eb49b', + type: 'external', + }, + }, + { + parent: { + id: '975c8d5bfd1dd20b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + processor: { + name: 'transaction', + event: 'span', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + id: '975c8d5bfd1dd20b', + }, + timestamp: { + us: 1584975868787174, + }, + span: { + duration: { + us: 16250, + }, + subtype: 'http', + destination: { + service: { + resource: 'opbeans-python:3000', + name: 'http://opbeans-python:3000', + type: 'external', + }, + }, + name: 'GET opbeans-python:3000', + http: { + response: { + status_code: 200, + }, + url: { + original: 'http://opbeans-python:3000/api/orders', + }, + }, + id: 'daae24d83c269918', + type: 'external', + }, + }, + { + container: { + id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + parent: { + id: '6fb0ff7365b87298', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + processor: { + name: 'transaction', + event: 'span', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.790Z', + service: { + node: { + name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13', + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10', + }, + language: { + name: 'python', + version: '3.6.10', + }, + version: 'None', + }, + transaction: { + id: '6fb0ff7365b87298', + }, + timestamp: { + us: 1584975868790080, + }, + span: { + duration: { + us: 2519, + }, + subtype: 'postgresql', + name: 'SELECT "n"."id", "n"."address", "n"."city", "n"."company_name", "n"."country", "n"."email", "n"."full_name", "n"."postal_code" FROM "customers" AS "n" WHERE "n"."id" = @__id_0 LIMIT 1', + destination: { + service: { + resource: 'postgresql', + name: 'postgresql', + type: 'db', + }, + }, + action: 'query', + id: 'c9407abb4d08ead1', + type: 'db', + sync: true, + db: { + statement: + 'SELECT "n"."id", "n"."address", "n"."city", "n"."company_name", "n"."country", "n"."email", "n"."full_name", "n"."postal_code" FROM "customers" AS "n" WHERE "n"."id" = @__id_0 LIMIT 1', + type: 'sql', + }, + }, + }, + ], + exceedsMax: false, + errorDocs: [ + { + parent: { + id: '975c8d5bfd1dd20b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + error: { + culprit: 'logrusMiddleware', + log: { + level: 'error', + message: 'GET //api/products (502)', + }, + id: '1f3cb98206b5c54225cb7c8908a658da', + grouping_key: '4dba2ff58fe6c036a5dee2ce411e512a', + }, + processor: { + name: 'error', + event: 'error', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T16:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + id: '975c8d5bfd1dd20b', + sampled: false, + }, + timestamp: { + us: 1584975868787052, + }, + }, + { + parent: { + id: '6fb0ff7365b87298', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + error: { + culprit: 'logrusMiddleware', + log: { + level: 'error', + message: 'GET //api/products (502)', + }, + id: '1f3cb98206b5c54225cb7c8908a658d2', + grouping_key: '4dba2ff58fe6c036a5dee2ce411e512a', + }, + processor: { + name: 'error', + event: 'error', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T16:04:28.790Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + name: 'opbeans-python', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + id: '6fb0ff7365b87298', + sampled: false, + }, + timestamp: { + us: 1584975868790000, + }, + }, + ], +} as unknown as TraceAPIResponse; + +export const traceChildStartBeforeParent = { + traceDocs: [ + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT', + }, + internal: { + sampler: { + value: 46, + }, + }, + source: { + ip: '172.19.0.13', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: '172.19.0.9', + full: 'http://172.19.0.9:3000/api/orders', + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0', + }, + service: { + node: { + name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2', + }, + language: { + name: 'Java', + version: '10.0.2', + }, + version: 'None', + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux', + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64', + }, + http: { + request: { + headers: { + Accept: ['*/*'], + 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], + Host: ['172.19.0.9:3000'], + 'Accept-Encoding': ['gzip, deflate'], + }, + method: 'get', + socket: { + encrypted: false, + remote_address: '172.19.0.13', + }, + body: { + original: '[REDACTED]', + }, + }, + response: { + headers: { + 'Transfer-Encoding': ['chunked'], + Date: ['Mon, 23 Mar 2020 15:04:28 GMT'], + 'Content-Type': ['application/json;charset=ISO-8859-1'], + }, + status_code: 200, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + client: { + ip: '172.19.0.13', + }, + transaction: { + duration: { + us: 18842, + }, + result: 'HTTP 2xx', + name: 'DispatcherServlet#doGet', + id: '49809ad3c26adf74', + span_count: { + dropped: 0, + started: 1, + }, + type: 'request', + sampled: true, + }, + user_agent: { + original: 'Python/3.7 aiohttp/3.3.2', + name: 'Other', + device: { + name: 'Other', + }, + }, + timestamp: { + us: 1584975868785000, + }, + }, + { + parent: { + id: 'fc107f7b556eb49b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + framework: { + name: 'gin', + version: 'v1.4.0', + }, + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + duration: { + us: 16597, + }, + result: 'HTTP 2xx', + name: 'GET /api/orders', + id: '975c8d5bfd1dd20b', + span_count: { + dropped: 0, + started: 1, + }, + type: 'request', + sampled: true, + }, + timestamp: { + us: 1584975868787052, + }, + }, + { + parent: { + id: 'daae24d83c269918', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + timestamp: { + us: 1584975868780000, + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/orders', + scheme: 'http', + port: 3000, + domain: 'opbeans-go', + full: 'http://opbeans-go:3000/api/orders', + }, + '@timestamp': '2020-03-23T15:04:28.788Z', + service: { + node: { + name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13', + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10', + }, + language: { + name: 'python', + version: '3.6.10', + }, + version: 'None', + }, + transaction: { + result: 'HTTP 2xx', + duration: { + us: 1464, + }, + name: 'I started before my parent 😰', + span_count: { + dropped: 0, + started: 1, + }, + id: '6fb0ff7365b87298', + type: 'request', + sampled: true, + }, + }, + { + container: { + id: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + parent: { + id: '49809ad3c26adf74', + }, + process: { + pid: 6, + title: '/usr/lib/jvm/java-10-openjdk-amd64/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '99ce8403-5875-4945-b074-d37dc10563eb', + version: '1.14.1-SNAPSHOT', + }, + internal: { + sampler: { + value: 44, + }, + }, + destination: { + address: 'opbeans-go', + port: 3000, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: 'f37f48d8b60b', + id: 'd8522e1f-be8e-43c2-b290-ac6b6c0f171e', + type: 'apm-server', + ephemeral_id: '6ed88f14-170e-478d-a4f5-ea5e7f4b16b9', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.785Z', + ecs: { + version: '1.4.0', + }, + service: { + node: { + name: '4cf84d094553201997ddb7fea344b7c6ef18dcb8233eba39278946ee8449794e', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '10.0.2', + }, + language: { + name: 'Java', + version: '10.0.2', + }, + version: 'None', + }, + host: { + hostname: '4cf84d094553', + os: { + platform: 'Linux', + }, + ip: '172.19.0.9', + name: '4cf84d094553', + architecture: 'amd64', + }, + connection: { + hash: "{service.environment:'production'}/{service.name:'opbeans-java'}/{span.subtype:'http'}/{destination.address:'opbeans-go'}/{span.type:'external'}", + }, + transaction: { + id: '49809ad3c26adf74', + }, + timestamp: { + us: 1584975868785273, + }, + span: { + duration: { + us: 17530, + }, + subtype: 'http', + name: 'GET opbeans-go', + destination: { + service: { + resource: 'opbeans-go:3000', + name: 'http://opbeans-go:3000', + type: 'external', + }, + }, + http: { + response: { + status_code: 200, + }, + url: { + original: 'http://opbeans-go:3000/api/orders', + }, + }, + id: 'fc107f7b556eb49b', + type: 'external', + }, + }, + { + parent: { + id: '975c8d5bfd1dd20b', + }, + agent: { + name: 'go', + version: '1.7.2', + }, + processor: { + name: 'transaction', + event: 'span', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.787Z', + service: { + node: { + name: 'e948a08b8f5efe99b5da01f50da48c7d8aee3bbf4701f3da85ebe760c2ffef29', + }, + environment: 'production', + name: 'opbeans-go', + runtime: { + name: 'gc', + version: 'go1.14.1', + }, + language: { + name: 'go', + version: 'go1.14.1', + }, + version: 'None', + }, + transaction: { + id: '975c8d5bfd1dd20b', + }, + timestamp: { + us: 1584975868787174, + }, + span: { + duration: { + us: 16250, + }, + subtype: 'http', + destination: { + service: { + resource: 'opbeans-python:3000', + name: 'http://opbeans-python:3000', + type: 'external', + }, + }, + name: 'I am his 👇🏻 parent 😡', + http: { + response: { + status_code: 200, + }, + url: { + original: 'http://opbeans-python:3000/api/orders', + }, + }, + id: 'daae24d83c269918', + type: 'external', + }, + }, + { + container: { + id: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + parent: { + id: '6fb0ff7365b87298', + }, + agent: { + name: 'python', + version: '5.5.2', + }, + processor: { + name: 'transaction', + event: 'span', + }, + trace: { + id: '513d33fafe99bbe6134749310c9b5322', + }, + '@timestamp': '2020-03-23T15:04:28.790Z', + service: { + node: { + name: 'a636915f1f6eec81ab44342b13a3ea9597ef03a24391e4e55f34ae2e20b30f51', + }, + environment: 'production', + framework: { + name: 'django', + version: '2.1.13', + }, + name: 'opbeans-python', + runtime: { + name: 'CPython', + version: '3.6.10', + }, + language: { + name: 'python', + version: '3.6.10', + }, + version: 'None', + }, + transaction: { + id: '6fb0ff7365b87298', + }, + timestamp: { + us: 1584975868781000, + }, + span: { + duration: { + us: 2519, + }, + subtype: 'postgresql', + name: 'I am using my parents skew 😇', + destination: { + service: { + resource: 'postgresql', + name: 'postgresql', + type: 'db', + }, + }, + action: 'query', + id: 'c9407abb4d08ead1', + type: 'db', + sync: true, + db: { + statement: + 'SELECT "opbeans_order"."id", "opbeans_order"."customer_id", "opbeans_customer"."full_name", "opbeans_order"."created_at" FROM "opbeans_order" INNER JOIN "opbeans_customer" ON ("opbeans_order"."customer_id" = "opbeans_customer"."id") LIMIT 1000', + type: 'sql', + }, + }, + }, + ], + exceedsMax: false, + errorDocs: [], +} as TraceAPIResponse; + +export const inferredSpans = { + traceDocs: [ + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + source: { + ip: '172.18.0.8', + }, + processor: { + name: 'transaction', + event: 'transaction', + }, + url: { + path: '/api/products/2', + scheme: 'http', + port: 3000, + domain: '172.18.0.7', + full: 'http://172.18.0.7:3000/api/products/2', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.786Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + client: { + ip: '172.18.0.8', + }, + http: { + request: { + headers: { + Accept: ['*/*'], + 'User-Agent': ['Python/3.7 aiohttp/3.3.2'], + Host: ['172.18.0.7:3000'], + 'Accept-Encoding': ['gzip, deflate'], + }, + method: 'get', + socket: { + encrypted: false, + remote_address: '172.18.0.8', + }, + }, + response: { + headers: { + 'Transfer-Encoding': ['chunked'], + Date: ['Thu, 09 Apr 2020 11:36:01 GMT'], + 'Content-Type': ['application/json;charset=UTF-8'], + }, + status_code: 200, + finished: true, + headers_sent: true, + }, + version: '1.1', + }, + user_agent: { + original: 'Python/3.7 aiohttp/3.3.2', + name: 'Other', + device: { + name: 'Other', + }, + }, + transaction: { + duration: { + us: 237537, + }, + result: 'HTTP 2xx', + name: 'APIRestController#product', + span_count: { + dropped: 0, + started: 3, + }, + id: 'f2387d37260d00bd', + type: 'request', + sampled: true, + }, + timestamp: { + us: 1586432160786001, + }, + }, + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + parent: { + id: 'f2387d37260d00bd', + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.810Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + transaction: { + id: 'f2387d37260d00bd', + }, + span: { + duration: { + us: 204574, + }, + subtype: 'inferred', + name: 'ServletInvocableHandlerMethod#invokeAndHandle', + id: 'a5df600bd7bd5e38', + type: 'app', + }, + timestamp: { + us: 1586432160810441, + }, + }, + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + parent: { + id: 'a5df600bd7bd5e38', + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + type: 'apm-server', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.810Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + transaction: { + id: 'f2387d37260d00bd', + }, + timestamp: { + us: 1586432160810441, + }, + span: { + duration: { + us: 102993, + }, + stacktrace: [ + { + library_frame: true, + exclude_from_grouping: false, + filename: 'InvocableHandlerMethod.java', + line: { + number: -1, + }, + function: 'doInvoke', + }, + { + exclude_from_grouping: false, + library_frame: true, + filename: 'InvocableHandlerMethod.java', + line: { + number: -1, + }, + function: 'invokeForRequest', + }, + ], + subtype: 'inferred', + name: 'APIRestController#product', + id: '808dc34fc41ce522', + type: 'app', + }, + }, + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + parent: { + id: 'f2387d37260d00bd', + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + processor: { + name: 'transaction', + event: 'span', + }, + labels: { + productId: '2', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.832Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + transaction: { + id: 'f2387d37260d00bd', + }, + timestamp: { + us: 1586432160832300, + }, + span: { + duration: { + us: 99295, + }, + name: 'OpenTracing product span', + id: '41226ae63af4f235', + type: 'unknown', + }, + child: { id: ['8d80de06aa11a6fc'] }, + }, + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + parent: { + id: '808dc34fc41ce522', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.859Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + transaction: { + id: 'f2387d37260d00bd', + }, + timestamp: { + us: 1586432160859600, + }, + span: { + duration: { + us: 53835, + }, + subtype: 'inferred', + name: 'Loader#executeQueryStatement', + id: '8d80de06aa11a6fc', + type: 'app', + }, + }, + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + parent: { + id: '41226ae63af4f235', + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + destination: { + address: 'postgres', + port: 5432, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.903Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + transaction: { + id: 'f2387d37260d00bd', + }, + timestamp: { + us: 1586432160903236, + }, + span: { + duration: { + us: 10211, + }, + subtype: 'postgresql', + destination: { + service: { + resource: 'postgresql', + name: 'postgresql', + type: 'db', + }, + }, + name: 'SELECT FROM products', + action: 'query', + id: '3708d5623658182f', + type: 'db', + db: { + statement: + 'select product0_.id as col_0_0_, product0_.sku as col_1_0_, product0_.name as col_2_0_, product0_.description as col_3_0_, product0_.cost as col_4_0_, product0_.selling_price as col_5_0_, product0_.stock as col_6_0_, producttyp1_.id as col_7_0_, producttyp1_.name as col_8_0_, (select sum(orderline2_.amount) from order_lines orderline2_ where orderline2_.product_id=product0_.id) as col_9_0_ from products product0_ left outer join product_types producttyp1_ on product0_.type_id=producttyp1_.id where product0_.id=?', + type: 'sql', + user: { + name: 'postgres', + }, + }, + }, + }, + { + container: { + id: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + parent: { + id: '41226ae63af4f235', + }, + process: { + pid: 6, + title: '/opt/java/openjdk/bin/java', + ppid: 1, + }, + agent: { + name: 'java', + ephemeral_id: '1cb5c830-c677-4b13-b340-ab1502f527c3', + version: '1.15.1-SNAPSHOT', + }, + destination: { + address: 'postgres', + port: 5432, + }, + processor: { + name: 'transaction', + event: 'span', + }, + observer: { + hostname: '7189f754b5a3', + id: 'f32d8d9f-a9f9-4355-8370-548dfd8024dc', + ephemeral_id: 'bff20764-0195-4f78-aa84-d799fc47b954', + type: 'apm-server', + version: '8.0.0', + version_major: 8, + }, + trace: { + id: '3b0dc77f3754e5bcb9da0e4c15e0db97', + }, + '@timestamp': '2020-04-09T11:36:00.859Z', + ecs: { + version: '1.5.0', + }, + service: { + node: { + name: 'fc2ae281f56fb84728bc9b5e6c17f3d13bbb7f4efd461158558e5c38e655abad', + }, + environment: 'production', + name: 'opbeans-java', + runtime: { + name: 'Java', + version: '11.0.6', + }, + language: { + name: 'Java', + version: '11.0.6', + }, + version: 'None', + }, + host: { + hostname: 'fc2ae281f56f', + os: { + platform: 'Linux', + }, + ip: '172.18.0.7', + name: 'fc2ae281f56f', + architecture: 'amd64', + }, + transaction: { + id: 'f2387d37260d00bd', + }, + timestamp: { + us: 1586432160859508, + }, + span: { + duration: { + us: 4503, + }, + subtype: 'postgresql', + destination: { + service: { + resource: 'postgresql', + name: 'postgresql', + type: 'db', + }, + }, + name: 'empty query', + action: 'query', + id: '9871cfd612368932', + type: 'db', + db: { + rows_affected: 0, + statement: '(empty query)', + type: 'sql', + user: { + name: 'postgres', + }, + }, + }, + }, + ], + exceedsMax: false, + errorDocs: [], +} as TraceAPIResponse; diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.tsx new file mode 100644 index 0000000000000..895b83136a097 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.stories.tsx @@ -0,0 +1,88 @@ +/* + * 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 { Meta, Story } from '@storybook/react'; +import React, { ComponentProps } from 'react'; +import { MemoryRouter } from 'react-router-dom'; +import { MockApmPluginContextWrapper } from '../../../../../context/apm_plugin/mock_apm_plugin_context'; +import { WaterfallContainer } from './index'; +import { getWaterfall } from './Waterfall/waterfall_helpers/waterfall_helpers'; +import { + inferredSpans, + manyChildrenWithSameLength, + simpleTrace, + traceChildStartBeforeParent, + traceWithErrors, + urlParams as testUrlParams, +} from './waterfall_container.stories.data'; + +type Args = ComponentProps; + +const stories: Meta = { + title: 'app/TransactionDetails/Waterfall', + component: WaterfallContainer, + decorators: [ + (StoryComponent) => ( + + + + + + ), + ], +}; +export default stories; + +export const Example: Story = ({ urlParams, waterfall }) => { + return ; +}; +Example.args = { + urlParams: testUrlParams, + waterfall: getWaterfall(simpleTrace, '975c8d5bfd1dd20b'), +}; + +export const WithErrors: Story = ({ urlParams, waterfall }) => { + return ; +}; +WithErrors.args = { + urlParams: testUrlParams, + waterfall: getWaterfall(traceWithErrors, '975c8d5bfd1dd20b'), +}; + +export const ChildStartsBeforeParent: Story = ({ + urlParams, + waterfall, +}) => { + return ; +}; +ChildStartsBeforeParent.args = { + urlParams: testUrlParams, + waterfall: getWaterfall(traceChildStartBeforeParent, '975c8d5bfd1dd20b'), +}; + +export const InferredSpans: Story = ({ urlParams, waterfall }) => { + return ; +}; +InferredSpans.args = { + urlParams: testUrlParams, + waterfall: getWaterfall(inferredSpans, 'f2387d37260d00bd'), +}; + +export const ManyChildrenWithSameLength: Story = ({ + urlParams, + waterfall, +}) => { + return ; +}; +ManyChildrenWithSameLength.args = { + urlParams: testUrlParams, + waterfall: getWaterfall(manyChildrenWithSameLength, '9a7f717439921d39'), +}; diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.test.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.test.tsx new file mode 100644 index 0000000000000..47610569dfa06 --- /dev/null +++ b/x-pack/plugins/apm/public/components/app/transaction_details/waterfall_with_summary/waterfall_container/waterfall_container.test.tsx @@ -0,0 +1,42 @@ +/* + * 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 { composeStories } from '@storybook/testing-react'; +import { render } from '@testing-library/react'; +import React from 'react'; +import { disableConsoleWarning } from '../../../../../utils/testHelpers'; +import * as stories from './waterfall_container.stories'; + +const { Example } = composeStories(stories); + +describe('WaterfallContainer', () => { + let consoleMock: jest.SpyInstance; + + beforeAll(() => { + consoleMock = disableConsoleWarning('Warning: componentWillReceiveProps'); + }); + + afterAll(() => { + consoleMock.mockRestore(); + }); + + it('renders', () => { + expect(() => render()).not.toThrowError(); + }); + + it('expands and contracts the accordion', () => { + const { getAllByRole } = render(); + const buttons = getAllByRole('button'); + const parentItem = buttons[2]; + const childItem = buttons[3]; + + parentItem.click(); + + expect(parentItem).toHaveAttribute('aria-expanded', 'false'); + expect(childItem).toHaveAttribute('aria-expanded', 'true'); + }); +}); From ab16b485cd3a31ab7f5d1dbee35f6ac90cb0f66a Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 19 Oct 2021 14:12:31 -0700 Subject: [PATCH 06/13] [ci] Disable Bazel cache on CI (#115428) This is a short-term solution to reduce our hit count in Buildkite due to the increase in bootstraps so we can move forward with enabling Buildkite across all PRs. --- .bazelrc | 3 -- .bazelrc.common | 4 +- .buildkite/pipelines/on_merge.yml | 2 + .buildkite/scripts/bootstrap.sh | 1 + .../scripts/common/persist_bazel_cache.sh | 15 -------- .buildkite/scripts/common/setup_bazel.sh | 38 +++++++++++++++++++ .buildkite/scripts/common/util.sh | 2 +- .../steps/on_merge_build_and_metrics.sh | 3 -- src/dev/ci_setup/.bazelrc-ci | 15 -------- 9 files changed, 44 insertions(+), 39 deletions(-) delete mode 100755 .buildkite/scripts/common/persist_bazel_cache.sh create mode 100755 .buildkite/scripts/common/setup_bazel.sh delete mode 100644 src/dev/ci_setup/.bazelrc-ci diff --git a/.bazelrc b/.bazelrc index 524542a02a4aa..91c4870ebd126 100644 --- a/.bazelrc +++ b/.bazelrc @@ -10,9 +10,6 @@ build --remote_timeout=30 build --remote_header=x-buildbuddy-api-key=3EYk49W2NefOx2n3yMze build --remote_accept_cached=true -# BuildBuddy -## Metadata settings -build --workspace_status_command="node ./src/dev/bazel_workspace_status.js" # Enable this in case you want to share your build info # build --build_metadata=VISIBILITY=PUBLIC build --build_metadata=TEST_GROUPS=//packages diff --git a/.bazelrc.common b/.bazelrc.common index 3de2bceaad3a6..c401a90507982 100644 --- a/.bazelrc.common +++ b/.bazelrc.common @@ -120,8 +120,8 @@ test --incompatible_strict_action_env # collect coverage information from test targets coverage --instrument_test_targets -# Settings for CI -# Bazel flags for CI are in /src/dev/ci_setup/.bazelrc-ci +# Metadata settings +build --workspace_status_command="node ./src/dev/bazel_workspace_status.js" # Load any settings specific to the current user. # .bazelrc.user should appear in .gitignore so that settings are not shared with team members diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index bceb1796479a2..dc0541c6397d9 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -9,6 +9,8 @@ steps: - command: .buildkite/scripts/steps/on_merge_build_and_metrics.sh label: Default Build and Metrics + env: + BAZEL_CACHE_MODE: read-write agents: queue: c2-8 diff --git a/.buildkite/scripts/bootstrap.sh b/.buildkite/scripts/bootstrap.sh index 3c6283a4fe3fd..df38c105d2fd3 100755 --- a/.buildkite/scripts/bootstrap.sh +++ b/.buildkite/scripts/bootstrap.sh @@ -3,6 +3,7 @@ set -euo pipefail source .buildkite/scripts/common/util.sh +source .buildkite/scripts/common/setup_bazel.sh echo "--- yarn install and bootstrap" retry 2 15 yarn kbn bootstrap diff --git a/.buildkite/scripts/common/persist_bazel_cache.sh b/.buildkite/scripts/common/persist_bazel_cache.sh deleted file mode 100755 index 357805c11acec..0000000000000 --- a/.buildkite/scripts/common/persist_bazel_cache.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env bash - -source .buildkite/scripts/common/util.sh - -KIBANA_BUILDBUDDY_CI_API_KEY=$(retry 5 5 vault read -field=value secret/kibana-issues/dev/kibana-buildbuddy-ci-api-key) -export KIBANA_BUILDBUDDY_CI_API_KEY - -# overwrites the file checkout .bazelrc file with the one intended for CI env -cp "$KIBANA_DIR/src/dev/ci_setup/.bazelrc-ci" "$KIBANA_DIR/.bazelrc" - -### -### append auth token to buildbuddy into "$KIBANA_DIR/.bazelrc"; -### -echo "# Appended by .buildkite/scripts/persist_bazel_cache.sh" >> "$KIBANA_DIR/.bazelrc" -echo "build --remote_header=x-buildbuddy-api-key=$KIBANA_BUILDBUDDY_CI_API_KEY" >> "$KIBANA_DIR/.bazelrc" diff --git a/.buildkite/scripts/common/setup_bazel.sh b/.buildkite/scripts/common/setup_bazel.sh new file mode 100755 index 0000000000000..bff44c7ba8dd3 --- /dev/null +++ b/.buildkite/scripts/common/setup_bazel.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash + +source .buildkite/scripts/common/util.sh + +KIBANA_BUILDBUDDY_CI_API_KEY=$(retry 5 5 vault read -field=value secret/kibana-issues/dev/kibana-buildbuddy-ci-api-key) +export KIBANA_BUILDBUDDY_CI_API_KEY + +echo "[bazel] writing .bazelrc" +cat < $KIBANA_DIR/.bazelrc + # Generated by .buildkite/scripts/common/setup_bazel.sh + + import %workspace%/.bazelrc.common + + build --build_metadata=ROLE=CI +EOF + +if [[ "${BAZEL_CACHE_MODE:-none}" == read* ]]; then + echo "[bazel] enabling caching" +cat <> $KIBANA_DIR/.bazelrc + build --bes_results_url=https://app.buildbuddy.io/invocation/ + build --bes_backend=grpcs://cloud.buildbuddy.io + build --remote_cache=grpcs://cloud.buildbuddy.io + build --remote_timeout=3600 + build --remote_header=x-buildbuddy-api-key=$KIBANA_BUILDBUDDY_CI_API_KEY +EOF +fi + +if [[ "${BAZEL_CACHE_MODE:-none}" == "read" ]]; then + echo "[bazel] cache set to read-only" +cat <> $KIBANA_DIR/.bazelrc + build --noremote_upload_local_results +EOF +fi + +if [[ "${BAZEL_CACHE_MODE:-none}" != @(read|read-write|none|) ]]; then + echo "invalid value for BAZEL_CACHE_MODE received ($BAZEL_CACHE_MODE), expected one of [read,read-write,none]" + exit 1 +fi diff --git a/.buildkite/scripts/common/util.sh b/.buildkite/scripts/common/util.sh index a884a147577c9..0e5fb1c40eb6f 100755 --- a/.buildkite/scripts/common/util.sh +++ b/.buildkite/scripts/common/util.sh @@ -18,7 +18,7 @@ verify_no_git_changes() { RED='\033[0;31m' C_RESET='\033[0m' # Reset color - GIT_CHANGES="$(git ls-files --modified)" + GIT_CHANGES="$(git ls-files --modified -- . ':!:.bazelrc')" if [ "$GIT_CHANGES" ]; then echo -e "\n${RED}ERROR: '$1' caused changes to the following files:${C_RESET}\n" echo -e "$GIT_CHANGES\n" diff --git a/.buildkite/scripts/steps/on_merge_build_and_metrics.sh b/.buildkite/scripts/steps/on_merge_build_and_metrics.sh index 315ba08f8719b..1f1e492f87bec 100755 --- a/.buildkite/scripts/steps/on_merge_build_and_metrics.sh +++ b/.buildkite/scripts/steps/on_merge_build_and_metrics.sh @@ -2,9 +2,6 @@ set -euo pipefail -# Write Bazel cache for Linux -.buildkite/scripts/common/persist_bazel_cache.sh - .buildkite/scripts/bootstrap.sh .buildkite/scripts/build_kibana.sh .buildkite/scripts/post_build_kibana.sh diff --git a/src/dev/ci_setup/.bazelrc-ci b/src/dev/ci_setup/.bazelrc-ci deleted file mode 100644 index 9aee657f37bcb..0000000000000 --- a/src/dev/ci_setup/.bazelrc-ci +++ /dev/null @@ -1,15 +0,0 @@ -# Used in the on-merge job to persist the Bazel cache to BuildBuddy -# from: .buildkite/scripts/common/persist_bazel_cache.sh - -import %workspace%/.bazelrc.common - -# BuildBuddy settings -build --bes_results_url=https://app.buildbuddy.io/invocation/ -build --bes_backend=grpcs://cloud.buildbuddy.io -build --remote_cache=grpcs://cloud.buildbuddy.io -build --remote_timeout=3600 -# --remote_header=x-buildbuddy-api-key= # appended in CI script - -# Metadata settings -build --build_metadata=ROLE=CI -build --workspace_status_command="node ./src/dev/bazel_workspace_status.js" From b1dd89e173e5c7416d9b74d387edd7f22b0cba86 Mon Sep 17 00:00:00 2001 From: Ashokaditya Date: Tue, 19 Oct 2021 23:29:03 +0200 Subject: [PATCH 07/13] [Security Solution][Endpoint] Update host isolation pending status API to work with new endpoint (#115441) * get the whole response when fetching responses by agentIds fixes elastic/security-team/issues/1705 * search new response index with actionId fixes elastic/security-team/issues/1705 * Find matching responses in new response index if fleet action response has an `ack` * review changes * hasIndexedDoc fix fetch logic * remove file * simplify code * add some acks to generator responses * meaningful names review changes Co-authored-by: Esteban Beltran --- .../data_loaders/index_fleet_actions.ts | 6 +- .../common/endpoint/types/actions.ts | 1 + .../server/endpoint/services/actions.ts | 126 ++++++++++++++++-- 3 files changed, 119 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_actions.ts b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_actions.ts index 246cf059f4cbd..5cc564ee3d41d 100644 --- a/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/data_loaders/index_fleet_actions.ts @@ -66,7 +66,11 @@ export const indexFleetActionsForHost = async ( const actionResponse = fleetActionGenerator.generateResponse({ action_id: action.action_id, agent_id: agentId, - action_data: action.data, + action_data: { + ...action.data, + // add ack to 4/5th of fleet response + ack: fleetActionGenerator.randomFloat() < 0.8 ? true : undefined, + }, }); esClient diff --git a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts index fb29297eb5929..d7ad417fc7d3f 100644 --- a/x-pack/plugins/security_solution/common/endpoint/types/actions.ts +++ b/x-pack/plugins/security_solution/common/endpoint/types/actions.ts @@ -64,6 +64,7 @@ export interface LogsEndpointActionResponse { export interface EndpointActionData { command: ISOLATION_ACTIONS; comment?: string; + ack?: boolean; } export interface EndpointAction { diff --git a/x-pack/plugins/security_solution/server/endpoint/services/actions.ts b/x-pack/plugins/security_solution/server/endpoint/services/actions.ts index d59ecb674196c..b25b599517300 100644 --- a/x-pack/plugins/security_solution/server/endpoint/services/actions.ts +++ b/x-pack/plugins/security_solution/server/endpoint/services/actions.ts @@ -9,6 +9,7 @@ import { ElasticsearchClient, Logger } from 'kibana/server'; import { SearchHit, SearchResponse } from '@elastic/elasticsearch/api/types'; import { ApiResponse } from '@elastic/elasticsearch'; import { AGENT_ACTIONS_INDEX, AGENT_ACTIONS_RESULTS_INDEX } from '../../../../fleet/common'; +import { ENDPOINT_ACTION_RESPONSES_INDEX } from '../../../common/endpoint/constants'; import { SecuritySolutionRequestHandlerContext } from '../../types'; import { ActivityLog, @@ -146,6 +147,41 @@ const getActivityLog = async ({ return sortedData; }; +const hasAckInResponse = (response: EndpointActionResponse): boolean => { + return typeof response.action_data.ack !== 'undefined'; +}; + +// return TRUE if for given action_id/agent_id +// there is no doc in .logs-endpoint.action.response-default +const hasNoEndpointResponse = ({ + action, + agentId, + indexedActionIds, +}: { + action: EndpointAction; + agentId: string; + indexedActionIds: string[]; +}): boolean => { + return action.agents.includes(agentId) && !indexedActionIds.includes(action.action_id); +}; + +// return TRUE if for given action_id/agent_id +// there is no doc in .fleet-actions-results +const hasNoFleetResponse = ({ + action, + agentId, + agentResponses, +}: { + action: EndpointAction; + agentId: string; + agentResponses: EndpointActionResponse[]; +}): boolean => { + return ( + action.agents.includes(agentId) && + !agentResponses.map((e) => e.action_id).includes(action.action_id) + ); +}; + export const getPendingActionCounts = async ( esClient: ElasticsearchClient, metadataService: EndpointMetadataService, @@ -179,21 +215,45 @@ export const getPendingActionCounts = async ( .catch(catchAndWrapError); // retrieve any responses to those action IDs from these agents - const responses = await fetchActionResponseIds( + const responses = await fetchActionResponses( esClient, metadataService, recentActions.map((a) => a.action_id), agentIDs ); - const pending: EndpointPendingActions[] = []; + // + + const pending: EndpointPendingActions[] = []; for (const agentId of agentIDs) { - const responseIDsFromAgent = responses[agentId]; + const agentResponses = responses[agentId]; + + // get response actionIds for responses with ACKs + const ackResponseActionIdList: string[] = agentResponses + .filter(hasAckInResponse) + .map((response) => response.action_id); + + // actions Ids that are indexed in new response index + const indexedActionIds = await hasEndpointResponseDoc({ + agentId, + actionIds: ackResponseActionIdList, + esClient, + }); + + const pendingActions: EndpointAction[] = recentActions.filter((action) => { + return ackResponseActionIdList.includes(action.action_id) // if has ack + ? hasNoEndpointResponse({ action, agentId, indexedActionIds }) // then find responses in new index + : hasNoFleetResponse({ + // else use the legacy way + action, + agentId, + agentResponses, + }); + }); pending.push({ agent_id: agentId, - pending_actions: recentActions - .filter((a) => a.agents.includes(agentId) && !responseIDsFromAgent.includes(a.action_id)) + pending_actions: pendingActions .map((a) => a.data.command) .reduce((acc, cur) => { if (cur in acc) { @@ -209,6 +269,43 @@ export const getPendingActionCounts = async ( return pending; }; +/** + * Returns a boolean for search result + * + * @param esClient + * @param actionIds + * @param agentIds + */ +const hasEndpointResponseDoc = async ({ + actionIds, + agentId, + esClient, +}: { + actionIds: string[]; + agentId: string; + esClient: ElasticsearchClient; +}): Promise => { + const response = await esClient + .search( + { + index: ENDPOINT_ACTION_RESPONSES_INDEX, + body: { + query: { + bool: { + filter: [{ terms: { action_id: actionIds } }, { term: { agent_id: agentId } }], + }, + }, + }, + }, + { ignore: [404] } + ) + .then( + (result) => result.body?.hits?.hits?.map((a) => a._source?.EndpointActions.action_id) || [] + ) + .catch(catchAndWrapError); + return response.filter((action): action is string => action !== undefined); +}; + /** * Returns back a map of elastic Agent IDs to array of Action IDs that have received a response. * @@ -217,16 +314,19 @@ export const getPendingActionCounts = async ( * @param actionIds * @param agentIds */ -const fetchActionResponseIds = async ( +const fetchActionResponses = async ( esClient: ElasticsearchClient, metadataService: EndpointMetadataService, actionIds: string[], agentIds: string[] -): Promise> => { - const actionResponsesByAgentId: Record = agentIds.reduce((acc, agentId) => { - acc[agentId] = []; - return acc; - }, {} as Record); +): Promise> => { + const actionResponsesByAgentId: Record = agentIds.reduce( + (acc, agentId) => { + acc[agentId] = []; + return acc; + }, + {} as Record + ); const actionResponses = await esClient .search( @@ -255,7 +355,7 @@ const fetchActionResponseIds = async ( return actionResponsesByAgentId; } - // Get the latest docs from the metadata datastream for the Elastic Agent IDs in the action responses + // Get the latest docs from the metadata data-stream for the Elastic Agent IDs in the action responses // This will be used determine if we should withhold the action id from the returned list in cases where // the Endpoint might not yet have sent an updated metadata document (which would be representative of // the state of the endpoint post-action) @@ -288,7 +388,7 @@ const fetchActionResponseIds = async ( enoughTimeHasLapsed || lastEndpointMetadataEventTimestamp > actionCompletedAtTimestamp ) { - actionResponsesByAgentId[actionResponse.agent_id].push(actionResponse.action_id); + actionResponsesByAgentId[actionResponse.agent_id].push(actionResponse); } } From fa1a233d232ce261699d082c289478949138a0b7 Mon Sep 17 00:00:00 2001 From: Jonathan Budzenski Date: Tue, 19 Oct 2021 17:01:52 -0500 Subject: [PATCH 08/13] Development and build support for ARM based Apple devices (#114879) --- .bazeliskversion | 2 +- .bazelversion | 2 +- WORKSPACE.bazel | 1 + src/dev/build/lib/config.test.ts | 3 ++- src/dev/build/lib/platform.test.ts | 3 +++ src/dev/build/lib/platform.ts | 1 + src/dev/build/tasks/install_chromium.js | 6 ++++- .../nodejs/download_node_builds_task.test.ts | 9 +++++++ .../nodejs/extract_node_builds_task.test.ts | 7 ++++++ .../verify_existing_node_builds_task.test.ts | 24 +++++++++++++++++++ .../build/tasks/patch_native_modules_task.ts | 4 ++++ 11 files changed, 58 insertions(+), 4 deletions(-) diff --git a/.bazeliskversion b/.bazeliskversion index 6a126f402d53d..4dae2985b58cc 100644 --- a/.bazeliskversion +++ b/.bazeliskversion @@ -1 +1 @@ -1.7.5 +1.10.1 diff --git a/.bazelversion b/.bazelversion index fcdb2e109f68c..fae6e3d04b2ca 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -4.0.0 +4.2.1 diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel index 287b376037abe..d3c44eab2a526 100644 --- a/WORKSPACE.bazel +++ b/WORKSPACE.bazel @@ -28,6 +28,7 @@ check_rules_nodejs_version(minimum_version_string = "3.8.0") node_repositories( node_repositories = { "16.11.1-darwin_amd64": ("node-v16.11.1-darwin-x64.tar.gz", "node-v16.11.1-darwin-x64", "ba54b8ed504bd934d03eb860fefe991419b4209824280d4274f6a911588b5e45"), + "16.11.1-darwin_arm64": ("node-v16.11.1-darwin-arm64.tar.gz", "node-v16.11.1-darwin-arm64", "5e772e478390fab3001b7148a923e4f22fca50170000f18b28475337d3a97248"), "16.11.1-linux_arm64": ("node-v16.11.1-linux-arm64.tar.xz", "node-v16.11.1-linux-arm64", "083fc51f0ea26de9041aaf9821874651a9fd3b20d1cf57071ce6b523a0436f17"), "16.11.1-linux_s390x": ("node-v16.11.1-linux-s390x.tar.xz", "node-v16.11.1-linux-s390x", "855b5c83c2ccb05273d50bb04376335c68d47df57f3187cdebe1f22b972d2825"), "16.11.1-linux_amd64": ("node-v16.11.1-linux-x64.tar.xz", "node-v16.11.1-linux-x64", "493bcc9b660eff983a6de65a0f032eb2717f57207edf74c745bcb86e360310b3"), diff --git a/src/dev/build/lib/config.test.ts b/src/dev/build/lib/config.test.ts index 8bc5eb70c9437..b2afe3337230d 100644 --- a/src/dev/build/lib/config.test.ts +++ b/src/dev/build/lib/config.test.ts @@ -107,6 +107,7 @@ describe('#getTargetPlatforms()', () => { .sort() ).toMatchInlineSnapshot(` Array [ + "darwin-arm64", "darwin-x64", "linux-arm64", "linux-x64", @@ -132,7 +133,7 @@ describe('#getNodePlatforms()', () => { .getTargetPlatforms() .map((p) => p.getNodeArch()) .sort() - ).toEqual(['darwin-x64', 'linux-arm64', 'linux-x64', 'win32-x64']); + ).toEqual(['darwin-arm64', 'darwin-x64', 'linux-arm64', 'linux-x64', 'win32-x64']); }); it('returns this platform and linux, when targetAllPlatforms = false', async () => { diff --git a/src/dev/build/lib/platform.test.ts b/src/dev/build/lib/platform.test.ts index 79b07cac09cfc..193579d1a35c1 100644 --- a/src/dev/build/lib/platform.test.ts +++ b/src/dev/build/lib/platform.test.ts @@ -31,6 +31,7 @@ describe('isWindows()', () => { expect(new Platform('win32', 'x64', 'foo').isWindows()).toBe(true); expect(new Platform('linux', 'x64', 'foo').isWindows()).toBe(false); expect(new Platform('darwin', 'x64', 'foo').isWindows()).toBe(false); + expect(new Platform('darwin', 'arm64', 'foo').isWindows()).toBe(false); }); }); @@ -39,6 +40,7 @@ describe('isLinux()', () => { expect(new Platform('win32', 'x64', 'foo').isLinux()).toBe(false); expect(new Platform('linux', 'x64', 'foo').isLinux()).toBe(true); expect(new Platform('darwin', 'x64', 'foo').isLinux()).toBe(false); + expect(new Platform('darwin', 'arm64', 'foo').isLinux()).toBe(false); }); }); @@ -47,5 +49,6 @@ describe('isMac()', () => { expect(new Platform('win32', 'x64', 'foo').isMac()).toBe(false); expect(new Platform('linux', 'x64', 'foo').isMac()).toBe(false); expect(new Platform('darwin', 'x64', 'foo').isMac()).toBe(true); + expect(new Platform('darwin', 'arm64', 'foo').isMac()).toBe(true); }); }); diff --git a/src/dev/build/lib/platform.ts b/src/dev/build/lib/platform.ts index 2df7801ffc10e..4c4ec271318d6 100644 --- a/src/dev/build/lib/platform.ts +++ b/src/dev/build/lib/platform.ts @@ -49,5 +49,6 @@ export const ALL_PLATFORMS = [ new Platform('linux', 'x64', 'linux-x86_64'), new Platform('linux', 'arm64', 'linux-aarch64'), new Platform('darwin', 'x64', 'darwin-x86_64'), + new Platform('darwin', 'arm64', 'darwin-aarch64'), new Platform('win32', 'x64', 'windows-x86_64'), ]; diff --git a/src/dev/build/tasks/install_chromium.js b/src/dev/build/tasks/install_chromium.js index 95e0df8984f9d..ad60019ea81a4 100644 --- a/src/dev/build/tasks/install_chromium.js +++ b/src/dev/build/tasks/install_chromium.js @@ -16,7 +16,11 @@ export const InstallChromium = { async run(config, log, build) { for (const platform of config.getNodePlatforms()) { - log.info(`Installing Chromium for ${platform.getName()}-${platform.getArchitecture()}`); + const target = `${platform.getName()}-${platform.getArchitecture()}`; + log.info(`Installing Chromium for ${target}`); + + // revert after https://github.com/elastic/kibana/issues/109949 + if (target === 'darwin-arm64') continue; const { binaryPath$ } = installBrowser( log, diff --git a/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts index ca43f78a40cfd..31374d2050971 100644 --- a/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/download_node_builds_task.test.ts @@ -98,6 +98,15 @@ it('downloads node builds for each platform', async () => { "url": "darwin:url", }, ], + Array [ + Object { + "destination": "darwin:downloadPath", + "log": , + "retries": 3, + "sha256": "darwin:sha256", + "url": "darwin:url", + }, + ], Array [ Object { "destination": "win32:downloadPath", diff --git a/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts index 37a017ed083d0..9f869b99c18ae 100644 --- a/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/extract_node_builds_task.test.ts @@ -105,6 +105,13 @@ it('runs expected fs operations', async () => { "strip": 1, }, ], + Array [ + /.node_binaries//node-v-darwin-arm64.tar.gz, + /.node_binaries//darwin-arm64, + Object { + "strip": 1, + }, + ], ], } `); diff --git a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts index b097deb46f61c..c636db145694c 100644 --- a/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts +++ b/src/dev/build/tasks/nodejs/verify_existing_node_builds_task.test.ts @@ -98,6 +98,7 @@ it('checks shasums for each downloaded node build', async () => { Object { "type": "return", "value": Object { + "darwin:darwin-arm64:downloadName": "valid shasum", "darwin:darwin-x64:downloadName": "valid shasum", "linux:linux-arm64:downloadName": "valid shasum", "linux:linux-x64:downloadName": "valid shasum", @@ -134,6 +135,14 @@ it('checks shasums for each downloaded node build', async () => { "name": "darwin", }, ], + Array [ + , + Platform { + "architecture": "arm64", + "buildName": "darwin-aarch64", + "name": "darwin", + }, + ], Array [ , Platform { @@ -165,6 +174,13 @@ it('checks shasums for each downloaded node build', async () => { "downloadPath": "darwin:darwin-x64:downloadPath", }, }, + Object { + "type": "return", + "value": Object { + "downloadName": "darwin:darwin-arm64:downloadName", + "downloadPath": "darwin:darwin-arm64:downloadPath", + }, + }, Object { "type": "return", "value": Object { @@ -190,6 +206,10 @@ it('checks shasums for each downloaded node build', async () => { "darwin:darwin-x64:downloadPath", "sha256", ], + Array [ + "darwin:darwin-arm64:downloadPath", + "sha256", + ], Array [ "win32:win32-x64:downloadPath", "sha256", @@ -212,6 +232,10 @@ it('checks shasums for each downloaded node build', async () => { "type": "return", "value": "valid shasum", }, + Object { + "type": "return", + "value": "valid shasum", + }, ], } `); diff --git a/src/dev/build/tasks/patch_native_modules_task.ts b/src/dev/build/tasks/patch_native_modules_task.ts index bb2b9cc96b677..37cb729053785 100644 --- a/src/dev/build/tasks/patch_native_modules_task.ts +++ b/src/dev/build/tasks/patch_native_modules_task.ts @@ -58,6 +58,10 @@ const packages: Package[] = [ url: 'https://storage.googleapis.com/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.16.0/linux-arm64-93.gz', sha256: '7a786e0b75985e5aafdefa9af55cad8e85e69a3326f16d8c63d21d6b5b3bff1b', }, + 'darwin-arm64': { + url: 'https://storage.googleapis.com/kibana-ci-proxy-cache/node-re2/uhop/node-re2/releases/download/1.16.0/darwin-arm64-93.gz', + sha256: '28b540cdddf13578f1bd28a03e29ffdc26a7f00ec859c369987b8d51ec6357c8', + }, 'win32-x64': { url: 'https://github.com/uhop/node-re2/releases/download/1.16.0/win32-x64-93.gz', sha256: '37245ceb59a086b5e7e9de8746a3cdf148c383be9ae2580f92baea90d0d39947', From 94e1144ba7b82b878ee0ef2dad0265a947c64bdd Mon Sep 17 00:00:00 2001 From: Scotty Bollinger Date: Tue, 19 Oct 2021 17:09:40 -0500 Subject: [PATCH 09/13] [App Search] Update copy on product selector page (#115637) https://github.com/elastic/enterprise-search-team/issues/759#issuecomment-947032787 --- x-pack/plugins/enterprise_search/common/constants.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/x-pack/plugins/enterprise_search/common/constants.ts b/x-pack/plugins/enterprise_search/common/constants.ts index bc60c917094f8..f84668068f413 100644 --- a/x-pack/plugins/enterprise_search/common/constants.ts +++ b/x-pack/plugins/enterprise_search/common/constants.ts @@ -32,7 +32,8 @@ export const APP_SEARCH_PLUGIN = { 'Leverage dashboards, analytics, and APIs for advanced application search made simple.', }), CARD_DESCRIPTION: i18n.translate('xpack.enterpriseSearch.appSearch.productCardDescription', { - defaultMessage: 'Design and deploy a powerful search to your websites and apps.', + defaultMessage: + 'Design, deploy, and manage powerful search experiences for your websites and web/mobile apps.', }), URL: '/app/enterprise_search/app_search', SUPPORT_URL: 'https://discuss.elastic.co/c/enterprise-search/app-search/', From 0c9e4b2decfad52d29f6bbbccfd8f0cde63a6151 Mon Sep 17 00:00:00 2001 From: Tyler Smalley Date: Tue, 19 Oct 2021 15:29:17 -0700 Subject: [PATCH 10/13] [ci] Re-add removed file still used in Jenkins Signed-off-by: Tyler Smalley --- src/dev/ci_setup/.bazelrc-ci | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/dev/ci_setup/.bazelrc-ci diff --git a/src/dev/ci_setup/.bazelrc-ci b/src/dev/ci_setup/.bazelrc-ci new file mode 100644 index 0000000000000..a0a0c3de73405 --- /dev/null +++ b/src/dev/ci_setup/.bazelrc-ci @@ -0,0 +1,5 @@ +# Generated by .buildkite/scripts/common/setup_bazel.sh + +import %workspace%/.bazelrc.common + +build --build_metadata=ROLE=CI From 4f1e07117c720e104184fece229117d5728520f0 Mon Sep 17 00:00:00 2001 From: Michael Olorunnisola Date: Tue, 19 Oct 2021 18:51:06 -0400 Subject: [PATCH 11/13] [Security Solution][Timelines] - Resolve UI (#114350) --- .../template_wrapper/bottom_bar/index.tsx | 2 + .../common/components/url_state/index.tsx | 1 + .../url_state/initialize_redux_by_url.tsx | 2 +- ...query_timeline_by_id_on_url_change.test.ts | 140 +++++++++++++++ .../query_timeline_by_id_on_url_change.ts | 84 +++++++++ .../common/components/url_state/types.ts | 1 + .../components/url_state/use_url_state.tsx | 16 +- .../hooks/use_resolve_conflict.test.tsx | 166 ++++++++++++++++++ .../common/hooks/use_resolve_conflict.tsx | 100 +++++++++++ .../common/hooks/use_resolve_redirect.test.ts | 140 +++++++++++++++ .../common/hooks/use_resolve_redirect.ts | 89 ++++++++++ .../open_timeline/__mocks__/index.ts | 6 +- .../components/open_timeline/helpers.test.ts | 28 +-- .../components/open_timeline/helpers.ts | 21 ++- .../components/open_timeline/types.ts | 7 + .../components/timeline/index.test.tsx | 6 + .../timelines/components/timeline/index.tsx | 4 +- .../public/timelines/containers/api.ts | 13 -- .../timelines/store/timeline/actions.ts | 2 + .../timelines/store/timeline/defaults.ts | 121 ++++++------- .../timelines/store/timeline/helpers.ts | 4 + .../public/timelines/store/timeline/model.ts | 2 + .../timelines/store/timeline/reducer.ts | 9 +- .../plugins/security_solution/public/types.ts | 2 +- .../plugins/security_solution/tsconfig.json | 5 +- 25 files changed, 868 insertions(+), 103 deletions(-) create mode 100644 x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.ts create mode 100644 x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx create mode 100644 x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx create mode 100644 x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts create mode 100644 x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts diff --git a/x-pack/plugins/security_solution/public/app/home/template_wrapper/bottom_bar/index.tsx b/x-pack/plugins/security_solution/public/app/home/template_wrapper/bottom_bar/index.tsx index eb606cd8ff583..8e972b92c2fa1 100644 --- a/x-pack/plugins/security_solution/public/app/home/template_wrapper/bottom_bar/index.tsx +++ b/x-pack/plugins/security_solution/public/app/home/template_wrapper/bottom_bar/index.tsx @@ -16,6 +16,7 @@ import { useSourcererScope, getScopeFromPath } from '../../../../common/containe import { TimelineId } from '../../../../../common/types/timeline'; import { AutoSaveWarningMsg } from '../../../../timelines/components/timeline/auto_save_warning'; import { Flyout } from '../../../../timelines/components/flyout'; +import { useResolveRedirect } from '../../../../common/hooks/use_resolve_redirect'; export const BOTTOM_BAR_CLASSNAME = 'timeline-bottom-bar'; @@ -26,6 +27,7 @@ export const SecuritySolutionBottomBar = React.memo( const [showTimeline] = useShowTimeline(); const { indicesExist } = useSourcererScope(getScopeFromPath(pathname)); + useResolveRedirect(); return indicesExist && showTimeline ? ( <> diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/index.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/index.tsx index 281db88ebd057..2e04bbc5f1daf 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/index.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/index.tsx @@ -25,6 +25,7 @@ export const UseUrlStateMemo = React.memo( prevProps.pathName === nextProps.pathName && deepEqual(prevProps.urlState, nextProps.urlState) && deepEqual(prevProps.indexPattern, nextProps.indexPattern) && + prevProps.search === nextProps.search && deepEqual(prevProps.navTabs, nextProps.navTabs) ); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx index 4a448e9064090..f04cf30da61f5 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx @@ -124,7 +124,7 @@ export const useSetInitialStateFromUrl = () => { [dispatch, updateTimeline, updateTimelineIsLoading] ); - return setInitialStateFromUrl; + return Object.freeze({ setInitialStateFromUrl, updateTimeline, updateTimelineIsLoading }); }; const updateTimerange = (newUrlStateString: string, dispatch: Dispatch) => { diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.test.ts b/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.test.ts new file mode 100644 index 0000000000000..5cc4f8e8b80f9 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.test.ts @@ -0,0 +1,140 @@ +/* + * 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 { queryTimelineById } from '../../../timelines/components/open_timeline/helpers'; +import { queryTimelineByIdOnUrlChange } from './query_timeline_by_id_on_url_change'; +import * as urlHelpers from './helpers'; + +jest.mock('../../../timelines/components/open_timeline/helpers'); + +describe('queryTimelineByIdOnUrlChange', () => { + const oldTestTimelineId = '04e8ffb0-2c2a-11ec-949c-39005af91f70'; + const newTestTimelineId = `${oldTestTimelineId}-newId`; + const oldTimelineRisonSearchString = `?timeline=(activeTab:query,graphEventId:%27%27,id:%27${oldTestTimelineId}%27,isOpen:!t)`; + const newTimelineRisonSearchString = `?timeline=(activeTab:query,graphEventId:%27%27,id:%27${newTestTimelineId}%27,isOpen:!t)`; + const mockUpdateTimeline = jest.fn(); + const mockUpdateTimelineIsLoading = jest.fn(); + const mockQueryTimelineById = jest.fn(); + beforeEach(() => { + (queryTimelineById as jest.Mock).mockImplementation(mockQueryTimelineById); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('when search strings are empty', () => { + it('should not call queryTimelineById', () => { + queryTimelineByIdOnUrlChange({ + oldSearch: '', + search: '', + timelineIdFromReduxStore: 'current-timeline-id', + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).not.toBeCalled(); + }); + }); + + describe('when search string has not changed', () => { + it('should not call queryTimelineById', () => { + queryTimelineByIdOnUrlChange({ + oldSearch: oldTimelineRisonSearchString, + search: oldTimelineRisonSearchString, + timelineIdFromReduxStore: 'timeline-id', + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).not.toBeCalled(); + }); + }); + + describe('when decode rison fails', () => { + it('should not call queryTimelineById', () => { + jest.spyOn(urlHelpers, 'decodeRisonUrlState').mockImplementationOnce(() => { + throw new Error('Unable to decode'); + }); + + queryTimelineByIdOnUrlChange({ + oldSearch: oldTimelineRisonSearchString, + search: newTimelineRisonSearchString, + timelineIdFromReduxStore: '', + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).not.toBeCalled(); + }); + }); + + describe('when new id is not provided', () => { + it('should not call queryTimelineById', () => { + queryTimelineByIdOnUrlChange({ + oldSearch: oldTimelineRisonSearchString, + search: '?timeline=(activeTab:query)', // no id + timelineIdFromReduxStore: newTestTimelineId, + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).not.toBeCalled(); + }); + }); + + describe('when new id matches the data in redux', () => { + it('should not call queryTimelineById', () => { + queryTimelineByIdOnUrlChange({ + oldSearch: oldTimelineRisonSearchString, + search: newTimelineRisonSearchString, + timelineIdFromReduxStore: newTestTimelineId, + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).not.toBeCalled(); + }); + }); + + // You can only redirect or run into conflict scenarios when already viewing a timeline + describe('when not actively on a page with timeline in the search field', () => { + it('should not call queryTimelineById', () => { + queryTimelineByIdOnUrlChange({ + oldSearch: '?random=foo', + search: newTimelineRisonSearchString, + timelineIdFromReduxStore: oldTestTimelineId, + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).not.toBeCalled(); + }); + }); + + describe('when an old timeline id exists, but a new id is given', () => { + it('should call queryTimelineById', () => { + queryTimelineByIdOnUrlChange({ + oldSearch: oldTimelineRisonSearchString, + search: newTimelineRisonSearchString, + timelineIdFromReduxStore: oldTestTimelineId, + updateTimeline: mockUpdateTimeline, + updateTimelineIsLoading: mockUpdateTimelineIsLoading, + }); + + expect(queryTimelineById).toBeCalledWith({ + activeTimelineTab: 'query', + duplicate: false, + graphEventId: '', + timelineId: newTestTimelineId, + openTimeline: true, + updateIsLoading: mockUpdateTimelineIsLoading, + updateTimeline: mockUpdateTimeline, + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.ts b/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.ts new file mode 100644 index 0000000000000..2778cefdc7953 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/components/url_state/query_timeline_by_id_on_url_change.ts @@ -0,0 +1,84 @@ +/* + * 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 { Action } from 'typescript-fsa'; +import { DispatchUpdateTimeline } from '../../../timelines/components/open_timeline/types'; +import { queryTimelineById } from '../../../timelines/components/open_timeline/helpers'; +import { TimelineTabs } from '../../../../common/types/timeline'; +import { + decodeRisonUrlState, + getQueryStringFromLocation, + getParamFromQueryString, +} from './helpers'; +import { TimelineUrl } from '../../../timelines/store/timeline/model'; +import { CONSTANTS } from './constants'; + +const getQueryStringKeyValue = ({ search, urlKey }: { search: string; urlKey: string }) => + getParamFromQueryString(getQueryStringFromLocation(search), urlKey); + +interface QueryTimelineIdOnUrlChange { + oldSearch?: string; + search: string; + timelineIdFromReduxStore: string; + updateTimeline: DispatchUpdateTimeline; + updateTimelineIsLoading: (status: { id: string; isLoading: boolean }) => Action<{ + id: string; + isLoading: boolean; + }>; +} + +/** + * After the initial load of the security solution, timeline is not updated when the timeline url search value is changed + * This is because those state changes happen in place and doesn't lead to a requerying of data for the new id. + * To circumvent this for the sake of the redirects needed for the saved object Id changes happening in 8.0 + * We are actively pulling the id changes that take place for timeline in the url and calling the query below + * to request the new data. + */ +export const queryTimelineByIdOnUrlChange = ({ + oldSearch, + search, + timelineIdFromReduxStore, + updateTimeline, + updateTimelineIsLoading, +}: QueryTimelineIdOnUrlChange) => { + const oldUrlStateString = getQueryStringKeyValue({ + urlKey: CONSTANTS.timeline, + search: oldSearch ?? '', + }); + + const newUrlStateString = getQueryStringKeyValue({ urlKey: CONSTANTS.timeline, search }); + + if (oldUrlStateString != null && newUrlStateString != null) { + let newTimeline = null; + let oldTimeline = null; + try { + newTimeline = decodeRisonUrlState(newUrlStateString); + } catch (error) { + // do nothing as timeline is defaulted to null + } + + try { + oldTimeline = decodeRisonUrlState(oldUrlStateString); + } catch (error) { + // do nothing as timeline is defaulted to null + } + const newId = newTimeline?.id; + const oldId = oldTimeline?.id; + + if (newId && newId !== oldId && newId !== timelineIdFromReduxStore) { + queryTimelineById({ + activeTimelineTab: newTimeline?.activeTab ?? TimelineTabs.query, + duplicate: false, + graphEventId: newTimeline?.graphEventId, + timelineId: newId, + openTimeline: true, + updateIsLoading: updateTimelineIsLoading, + updateTimeline, + }); + } + } +}; diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/types.ts b/x-pack/plugins/security_solution/public/common/components/url_state/types.ts index e803c091423be..06ed33ac69f6e 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/types.ts +++ b/x-pack/plugins/security_solution/public/common/components/url_state/types.ts @@ -79,6 +79,7 @@ export interface PreviousLocationUrlState { pathName: string | undefined; pageName: string | undefined; urlState: UrlState; + search: string | undefined; } export interface UrlStateToRedux { diff --git a/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx b/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx index bc47ba9d8ae99..3245d647227ad 100644 --- a/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx +++ b/x-pack/plugins/security_solution/public/common/components/url_state/use_url_state.tsx @@ -39,6 +39,7 @@ import { } from './types'; import { TimelineUrl } from '../../../timelines/store/timeline/model'; import { UrlInputsModel } from '../../store/inputs/model'; +import { queryTimelineByIdOnUrlChange } from './query_timeline_by_id_on_url_change'; function usePrevious(value: PreviousLocationUrlState) { const ref = useRef(value); @@ -60,9 +61,10 @@ export const useUrlStateHooks = ({ const [isFirstPageLoad, setIsFirstPageLoad] = useState(true); const { filterManager, savedQueries } = useKibana().services.data.query; const { pathname: browserPathName } = useLocation(); - const prevProps = usePrevious({ pathName, pageName, urlState }); + const prevProps = usePrevious({ pathName, pageName, urlState, search }); - const setInitialStateFromUrl = useSetInitialStateFromUrl(); + const { setInitialStateFromUrl, updateTimeline, updateTimelineIsLoading } = + useSetInitialStateFromUrl(); const handleInitialize = useCallback( (type: UrlStateType) => { @@ -190,6 +192,16 @@ export const useUrlStateHooks = ({ document.title = `${getTitle(pageName, navTabs)} - Kibana`; }, [pageName, navTabs]); + useEffect(() => { + queryTimelineByIdOnUrlChange({ + oldSearch: prevProps.search, + search, + timelineIdFromReduxStore: urlState.timeline.id, + updateTimeline, + updateTimelineIsLoading, + }); + }, [search, prevProps.search, urlState.timeline.id, updateTimeline, updateTimelineIsLoading]); + return null; }; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx new file mode 100644 index 0000000000000..bafbe078cdbdb --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.test.tsx @@ -0,0 +1,166 @@ +/* + * 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 { useLocation } from 'react-router-dom'; +import { renderHook } from '@testing-library/react-hooks'; +import { useDeepEqualSelector } from './use_selector'; +import { useKibana } from '../lib/kibana'; +import { useResolveConflict } from './use_resolve_conflict'; +import * as urlHelpers from '../components/url_state/helpers'; + +jest.mock('react-router-dom', () => { + const original = jest.requireActual('react-router-dom'); + + return { + ...original, + useLocation: jest.fn(), + }; +}); +jest.mock('../lib/kibana'); +jest.mock('./use_selector'); +jest.mock('../../timelines/store/timeline/', () => ({ + timelineSelectors: { + getTimelineByIdSelector: () => jest.fn(), + }, +})); + +describe('useResolveConflict', () => { + const mockGetLegacyUrlConflict = jest.fn().mockReturnValue('Test!'); + beforeEach(() => { + jest.resetAllMocks(); + // Mock rison format in actual url + (useLocation as jest.Mock).mockReturnValue({ + pathname: 'my/cool/path', + search: + 'timeline=(activeTab:query,graphEventId:%27%27,id:%2704e8ffb0-2c2a-11ec-949c-39005af91f70%27,isOpen:!t)', + }); + (useKibana as jest.Mock).mockReturnValue({ + services: { + spaces: { + ui: { + components: { + getLegacyUrlConflict: mockGetLegacyUrlConflict, + }, + }, + }, + }, + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('resolve object is not provided', () => { + it('should not show the conflict message', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + savedObjectId: 'current-saved-object-id', + activeTab: 'some-tab', + graphEventId: 'current-graph-event-id', + show: false, + })); + const { result } = renderHook<{}, JSX.Element | null>(() => useResolveConflict()); + expect(mockGetLegacyUrlConflict).not.toHaveBeenCalled(); + expect(result.current).toEqual(null); + }); + }); + + describe('outcome is exactMatch', () => { + it('should not show the conflict message', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'exactMatch', + }, + savedObjectId: 'current-saved-object-id', + activeTab: 'some-tab', + graphEventId: 'current-graph-event-id', + show: false, + })); + const { result } = renderHook<{}, JSX.Element | null>(() => useResolveConflict()); + expect(mockGetLegacyUrlConflict).not.toHaveBeenCalled(); + expect(result.current).toEqual(null); + }); + }); + + describe('outcome is aliasMatch', () => { + it('should not show the conflict message', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'aliasMatch', + alias_target_id: 'new-id', + }, + })); + const { result } = renderHook<{}, JSX.Element | null>(() => useResolveConflict()); + expect(mockGetLegacyUrlConflict).not.toHaveBeenCalled(); + expect(result.current).toEqual(null); + }); + }); + + describe('outcome is conflict', () => { + const mockTextContent = 'I am the visible conflict message'; + it('should show the conflict message', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'conflict', + alias_target_id: 'new-id', + }, + })); + mockGetLegacyUrlConflict.mockImplementation(() => mockTextContent); + const { result } = renderHook<{}, JSX.Element | null>(() => useResolveConflict()); + expect(mockGetLegacyUrlConflict).toHaveBeenCalledWith({ + objectNoun: 'timeline', + currentObjectId: '04e8ffb0-2c2a-11ec-949c-39005af91f70', + otherObjectId: 'new-id', + otherObjectPath: + 'my/cool/path?timeline=%28activeTab%3Aquery%2CgraphEventId%3A%27%27%2Cid%3Anew-id%2CisOpen%3A%21t%29', + }); + expect(result.current).toMatchInlineSnapshot(` + + I am the visible conflict message + + + `); + }); + + describe('rison is unable to be decoded', () => { + it('should use timeline values from redux to create the otherObjectPath', async () => { + jest.spyOn(urlHelpers, 'decodeRisonUrlState').mockImplementation(() => { + throw new Error('Unable to decode'); + }); + (useLocation as jest.Mock).mockReturnValue({ + pathname: 'my/cool/path', + search: '?foo=bar', + }); + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'conflict', + alias_target_id: 'new-id', + }, + savedObjectId: 'current-saved-object-id', + activeTab: 'some-tab', + graphEventId: 'current-graph-event-id', + show: false, + })); + mockGetLegacyUrlConflict.mockImplementation(() => mockTextContent); + renderHook(() => useResolveConflict()); + const { result } = renderHook<{}, JSX.Element | null>(() => useResolveConflict()); + expect(mockGetLegacyUrlConflict).toHaveBeenCalledWith({ + objectNoun: 'timeline', + currentObjectId: 'current-saved-object-id', + otherObjectId: 'new-id', + otherObjectPath: + 'my/cool/path?foo=bar&timeline=%28activeTab%3Asome-tab%2CgraphEventId%3Acurrent-graph-event-id%2Cid%3Anew-id%2CisOpen%3A%21f%29', + }); + expect(result.current).toMatchInlineSnapshot(` + + I am the visible conflict message + + + `); + }); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx new file mode 100644 index 0000000000000..6a493d944ecda --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_conflict.tsx @@ -0,0 +1,100 @@ +/* + * 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, { useCallback, useMemo } from 'react'; +import { useLocation } from 'react-router-dom'; +import { EuiSpacer } from '@elastic/eui'; +import { useDeepEqualSelector } from './use_selector'; +import { TimelineId } from '../../../common/types/timeline'; +import { timelineSelectors } from '../../timelines/store/timeline'; +import { TimelineUrl } from '../../timelines/store/timeline/model'; +import { timelineDefaults } from '../../timelines/store/timeline/defaults'; +import { decodeRisonUrlState, encodeRisonUrlState } from '../components/url_state/helpers'; +import { useKibana } from '../lib/kibana'; +import { CONSTANTS } from '../components/url_state/constants'; + +/** + * Unfortunately the url change initiated when clicking the button to otherObjectPath doesn't seem to be + * respected by the useSetInitialStateFromUrl here: x-pack/plugins/security_solution/public/common/components/url_state/initialize_redux_by_url.tsx + * + * FYI: It looks like the routing causes replaceStateInLocation to be called instead: + * x-pack/plugins/security_solution/public/common/components/url_state/helpers.ts + * + * Potentially why the markdown component needs a click handler as well for timeline? + * see: /x-pack/plugins/security_solution/public/common/components/markdown_editor/plugins/timeline/processor.tsx + */ +export const useResolveConflict = () => { + const { search, pathname } = useLocation(); + const { spaces } = useKibana().services; + const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); + const { resolveTimelineConfig, savedObjectId, show, graphEventId, activeTab } = + useDeepEqualSelector((state) => getTimeline(state, TimelineId.active) ?? timelineDefaults); + + const getLegacyUrlConflictCallout = useCallback(() => { + // This function returns a callout component *if* we have encountered a "legacy URL conflict" scenario + if ( + !spaces || + resolveTimelineConfig?.outcome !== 'conflict' || + resolveTimelineConfig?.alias_target_id == null + ) { + return null; + } + + const searchQuery = new URLSearchParams(search); + const timelineRison = searchQuery.get(CONSTANTS.timeline) ?? undefined; + // Try to get state on URL, but default to what's in Redux in case of decodeRisonFailure + const currentTimelineState = { + id: savedObjectId ?? '', + isOpen: !!show, + activeTab, + graphEventId, + }; + let timelineSearch: TimelineUrl = currentTimelineState; + try { + timelineSearch = decodeRisonUrlState(timelineRison) ?? currentTimelineState; + } catch (error) { + // do nothing as it's already defaulted on line 77 + } + // We have resolved to one object, but another object has a legacy URL alias associated with this ID/page. We should display a + // callout with a warning for the user, and provide a way for them to navigate to the other object. + const currentObjectId = timelineSearch?.id; + const newSavedObjectId = resolveTimelineConfig?.alias_target_id ?? ''; // This is always defined if outcome === 'conflict' + + const newTimelineSearch: TimelineUrl = { + ...timelineSearch, + id: newSavedObjectId, + }; + const newTimelineRison = encodeRisonUrlState(newTimelineSearch); + searchQuery.set(CONSTANTS.timeline, newTimelineRison); + + const newPath = `${pathname}?${searchQuery.toString()}${window.location.hash}`; + + return ( + <> + {spaces.ui.components.getLegacyUrlConflict({ + objectNoun: CONSTANTS.timeline, + currentObjectId, + otherObjectId: newSavedObjectId, + otherObjectPath: newPath, + })} + + + ); + }, [ + activeTab, + graphEventId, + pathname, + resolveTimelineConfig?.alias_target_id, + resolveTimelineConfig?.outcome, + savedObjectId, + search, + show, + spaces, + ]); + + return useMemo(() => getLegacyUrlConflictCallout(), [getLegacyUrlConflictCallout]); +}; diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts new file mode 100644 index 0000000000000..c9a0eedefd0af --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.test.ts @@ -0,0 +1,140 @@ +/* + * 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 { useLocation } from 'react-router-dom'; +import { renderHook } from '@testing-library/react-hooks'; +import { useDeepEqualSelector } from './use_selector'; +import { useKibana } from '../lib/kibana'; +import { useResolveRedirect } from './use_resolve_redirect'; +import * as urlHelpers from '../components/url_state/helpers'; + +jest.mock('react-router-dom', () => { + const original = jest.requireActual('react-router-dom'); + + return { + ...original, + useLocation: jest.fn(), + }; +}); +jest.mock('../lib/kibana'); +jest.mock('./use_selector'); +jest.mock('../../timelines/store/timeline/', () => ({ + timelineSelectors: { + getTimelineByIdSelector: () => jest.fn(), + }, +})); + +describe('useResolveRedirect', () => { + const mockRedirectLegacyUrl = jest.fn(); + beforeEach(() => { + jest.resetAllMocks(); + // Mock rison format in actual url + (useLocation as jest.Mock).mockReturnValue({ + pathname: 'my/cool/path', + search: + 'timeline=(activeTab:query,graphEventId:%27%27,id:%2704e8ffb0-2c2a-11ec-949c-39005af91f70%27,isOpen:!t)', + }); + (useKibana as jest.Mock).mockReturnValue({ + services: { + spaces: { + ui: { + redirectLegacyUrl: mockRedirectLegacyUrl, + }, + }, + }, + }); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('resolve object is not provided', () => { + it('should not redirect', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + savedObjectId: 'current-saved-object-id', + activeTab: 'some-tab', + graphEventId: 'current-graph-event-id', + show: false, + })); + renderHook(() => useResolveRedirect()); + expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); + }); + }); + + describe('outcome is exactMatch', () => { + it('should not redirect', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'exactMatch', + }, + savedObjectId: 'current-saved-object-id', + activeTab: 'some-tab', + graphEventId: 'current-graph-event-id', + show: false, + })); + renderHook(() => useResolveRedirect()); + expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); + }); + }); + + describe('outcome is aliasMatch', () => { + it('should redirect to url with id:new-id if outcome is aliasMatch', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'aliasMatch', + alias_target_id: 'new-id', + }, + })); + renderHook(() => useResolveRedirect()); + expect(mockRedirectLegacyUrl).toHaveBeenCalledWith( + 'my/cool/path?timeline=%28activeTab%3Aquery%2CgraphEventId%3A%27%27%2Cid%3Anew-id%2CisOpen%3A%21t%29', + 'timeline' + ); + }); + + describe('rison is unable to be decoded', () => { + it('should use timeline values from redux to create the redirect path', async () => { + jest.spyOn(urlHelpers, 'decodeRisonUrlState').mockImplementation(() => { + throw new Error('Unable to decode'); + }); + (useLocation as jest.Mock).mockReturnValue({ + pathname: 'my/cool/path', + search: '?foo=bar', + }); + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'aliasMatch', + alias_target_id: 'new-id', + }, + savedObjectId: 'current-saved-object-id', + activeTab: 'some-tab', + graphEventId: 'current-graph-event-id', + show: false, + })); + renderHook(() => useResolveRedirect()); + expect(mockRedirectLegacyUrl).toHaveBeenCalledWith( + 'my/cool/path?foo=bar&timeline=%28activeTab%3Asome-tab%2CgraphEventId%3Acurrent-graph-event-id%2Cid%3Anew-id%2CisOpen%3A%21f%29', + 'timeline' + ); + }); + }); + }); + + describe('outcome is conflict', () => { + it('should not redirect', async () => { + (useDeepEqualSelector as jest.Mock).mockImplementation(() => ({ + resolveTimelineConfig: { + outcome: 'conflict', + alias_target_id: 'new-id', + }, + })); + renderHook(() => useResolveRedirect()); + expect(mockRedirectLegacyUrl).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts new file mode 100644 index 0000000000000..a6ba0b24828e7 --- /dev/null +++ b/x-pack/plugins/security_solution/public/common/hooks/use_resolve_redirect.ts @@ -0,0 +1,89 @@ +/* + * 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 { useCallback, useEffect, useMemo, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { useDeepEqualSelector } from './use_selector'; +import { TimelineId } from '../../../common/types/timeline'; +import { timelineSelectors } from '../../timelines/store/timeline/'; +import { timelineDefaults } from '../../timelines/store/timeline/defaults'; +import { decodeRisonUrlState, encodeRisonUrlState } from '../components/url_state/helpers'; +import { useKibana } from '../lib/kibana'; +import { TimelineUrl } from '../../timelines/store/timeline/model'; +import { CONSTANTS } from '../components/url_state/constants'; + +/** + * This hooks is specifically for use with the resolve api that was introduced as part of 7.16 + * If a deep link id has been migrated to a new id, this hook will cause a redirect to a url with + * the new ID. + */ + +export const useResolveRedirect = () => { + const { search, pathname } = useLocation(); + const [hasRedirected, updateHasRedirected] = useState(false); + const { spaces } = useKibana().services; + + const getTimeline = useMemo(() => timelineSelectors.getTimelineByIdSelector(), []); + const { resolveTimelineConfig, savedObjectId, show, activeTab, graphEventId } = + useDeepEqualSelector((state) => getTimeline(state, TimelineId.active) ?? timelineDefaults); + + const redirect = useCallback(() => { + const searchQuery = new URLSearchParams(search); + const timelineRison = searchQuery.get(CONSTANTS.timeline) ?? undefined; + + // Try to get state on URL, but default to what's in Redux in case of decodeRisonFailure + const currentTimelineState = { + id: savedObjectId ?? '', + isOpen: !!show, + activeTab, + graphEventId, + }; + let timelineSearch: TimelineUrl = currentTimelineState; + try { + timelineSearch = decodeRisonUrlState(timelineRison) ?? currentTimelineState; + } catch (error) { + // do nothing as it's already defaulted on line 77 + } + + if ( + hasRedirected || + !spaces || + resolveTimelineConfig?.outcome !== 'aliasMatch' || + resolveTimelineConfig?.alias_target_id == null + ) { + return null; + } + + // We found this object by a legacy URL alias from its old ID; redirect the user to the page with its new ID, preserving any URL hash + const newObjectId = resolveTimelineConfig?.alias_target_id ?? ''; // This is always defined if outcome === 'aliasMatch' + const newTimelineSearch = { + ...timelineSearch, + id: newObjectId, + }; + const newTimelineRison = encodeRisonUrlState(newTimelineSearch); + searchQuery.set(CONSTANTS.timeline, newTimelineRison); + const newPath = `${pathname}?${searchQuery.toString()}`; + spaces.ui.redirectLegacyUrl(newPath, CONSTANTS.timeline); + // Prevent the effect from being called again as the url change takes place in location rather than a true redirect + updateHasRedirected(true); + }, [ + activeTab, + graphEventId, + hasRedirected, + pathname, + resolveTimelineConfig?.outcome, + resolveTimelineConfig?.alias_target_id, + savedObjectId, + search, + show, + spaces, + ]); + + useEffect(() => { + redirect(); + }, [redirect]); +}; diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/__mocks__/index.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/__mocks__/index.ts index f1e1c42539eff..2521d14481ca8 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/__mocks__/index.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/__mocks__/index.ts @@ -9,7 +9,7 @@ import { TimelineStatus, TimelineType } from '../../../../../common/types/timeli export const mockTimeline = { data: { - getOneTimeline: { + timeline: { savedObjectId: 'eb2781c0-1df5-11eb-8589-2f13958b79f7', columns: [ { @@ -163,6 +163,7 @@ export const mockTimeline = { version: 'WzQ4NSwxXQ==', __typename: 'TimelineResult', }, + outcome: 'exactMatch', }, loading: false, networkStatus: 7, @@ -171,7 +172,7 @@ export const mockTimeline = { export const mockTemplate = { data: { - getOneTimeline: { + timeline: { savedObjectId: '0c70a200-1de0-11eb-885c-6fc13fca1850', columns: [ { @@ -416,6 +417,7 @@ export const mockTemplate = { version: 'WzQwMywxXQ==', __typename: 'TimelineResult', }, + outcome: 'exactMatch', }, loading: false, networkStatus: 7, diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts index 5d52d2c8a4d48..1b93f1556a95c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.test.ts @@ -50,7 +50,7 @@ import { mockTimeline as mockSelectedTimeline, mockTemplate as mockSelectedTemplate, } from './__mocks__'; -import { getTimeline } from '../../containers/api'; +import { resolveTimeline } from '../../containers/api'; import { defaultHeaders } from '../timeline/body/column_headers/default_headers'; jest.mock('../../../common/store/inputs/actions'); @@ -951,7 +951,7 @@ describe('helpers', () => { }; beforeAll(async () => { - (getTimeline as jest.Mock).mockRejectedValue(mockError); + (resolveTimeline as jest.Mock).mockRejectedValue(mockError); queryTimelineById<{}>(args as unknown as QueryTimelineById<{}>); }); @@ -986,7 +986,7 @@ describe('helpers', () => { }; beforeAll(async () => { - (getTimeline as jest.Mock).mockResolvedValue(selectedTimeline); + (resolveTimeline as jest.Mock).mockResolvedValue(selectedTimeline); await queryTimelineById<{}>(args as unknown as QueryTimelineById<{}>); }); @@ -1002,7 +1002,7 @@ describe('helpers', () => { }); test('get timeline by Id', () => { - expect(getTimeline).toHaveBeenCalled(); + expect(resolveTimeline).toHaveBeenCalled(); }); test('it does not call onError when an error does not occur', () => { @@ -1011,7 +1011,7 @@ describe('helpers', () => { test('Do not override daterange if TimelineStatus is active', () => { const { timeline } = formatTimelineResultToModel( - omitTypenameInTimeline(getOr({}, 'data.getOneTimeline', selectedTimeline)), + omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)), args.duplicate, args.timelineType ); @@ -1044,7 +1044,7 @@ describe('helpers', () => { }; beforeAll(async () => { - (getTimeline as jest.Mock).mockResolvedValue(selectedTimeline); + (resolveTimeline as jest.Mock).mockResolvedValue(selectedTimeline); await queryTimelineById<{}>(args as unknown as QueryTimelineById<{}>); }); @@ -1060,12 +1060,12 @@ describe('helpers', () => { }); test('get timeline by Id', () => { - expect(getTimeline).toHaveBeenCalled(); + expect(resolveTimeline).toHaveBeenCalled(); }); test('should not override daterange if TimelineStatus is active', () => { const { timeline } = formatTimelineResultToModel( - omitTypenameInTimeline(getOr({}, 'data.getOneTimeline', selectedTimeline)), + omitTypenameInTimeline(getOr({}, 'data.timeline', selectedTimeline)), args.duplicate, args.timelineType ); @@ -1085,6 +1085,10 @@ describe('helpers', () => { to: '2020-07-08T08:20:18.966Z', notes: [], id: TimelineId.active, + resolveTimelineConfig: { + outcome: 'exactMatch', + alias_target_id: undefined, + }, }); }); @@ -1112,12 +1116,12 @@ describe('helpers', () => { }; beforeAll(async () => { - (getTimeline as jest.Mock).mockResolvedValue(template); + (resolveTimeline as jest.Mock).mockResolvedValue(template); await queryTimelineById<{}>(args as unknown as QueryTimelineById<{}>); }); afterAll(() => { - (getTimeline as jest.Mock).mockReset(); + (resolveTimeline as jest.Mock).mockReset(); jest.clearAllMocks(); }); @@ -1129,12 +1133,12 @@ describe('helpers', () => { }); test('get timeline by Id', () => { - expect(getTimeline).toHaveBeenCalled(); + expect(resolveTimeline).toHaveBeenCalled(); }); test('override daterange if TimelineStatus is immutable', () => { const { timeline } = formatTimelineResultToModel( - omitTypenameInTimeline(getOr({}, 'data.getOneTimeline', template)), + omitTypenameInTimeline(getOr({}, 'data.timeline', template)), args.duplicate, args.timelineType ); diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts index 1fbddf61f8cd3..f325ab34e88d5 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/helpers.ts @@ -20,6 +20,7 @@ import { TimelineType, TimelineTabs, TimelineResult, + SingleTimelineResolveResponse, ColumnHeaderResult, FilterTimelineResult, DataProviderResult, @@ -65,7 +66,7 @@ import { DEFAULT_FROM_MOMENT, DEFAULT_TO_MOMENT, } from '../../../common/utils/default_date_settings'; -import { getTimeline } from '../../containers/api'; +import { resolveTimeline } from '../../containers/api'; import { PinnedEvent } from '../../../../common/types/timeline/pinned_event'; import { NoteResult } from '../../../../common/types/timeline/note'; @@ -346,11 +347,12 @@ export const queryTimelineById = ({ updateTimeline, }: QueryTimelineById) => { updateIsLoading({ id: TimelineId.active, isLoading: true }); - Promise.resolve(getTimeline(timelineId)) + Promise.resolve(resolveTimeline(timelineId)) .then((result) => { - const timelineToOpen: TimelineResult = omitTypenameInTimeline( - getOr({}, 'data.getOneTimeline', result) - ); + const data: SingleTimelineResolveResponse['data'] | null = getOr(null, 'data', result); + if (!data) return; + + const timelineToOpen = omitTypenameInTimeline(data.timeline); const { timeline, notes } = formatTimelineResultToModel( timelineToOpen, @@ -370,6 +372,10 @@ export const queryTimelineById = ({ from, id: TimelineId.active, notes, + resolveTimelineConfig: { + outcome: data.outcome, + alias_target_id: data.alias_target_id, + }, timeline: { ...timeline, activeTab: activeTimelineTab, @@ -399,6 +405,7 @@ export const dispatchUpdateTimeline = forceNotes = false, from, notes, + resolveTimelineConfig, timeline, to, ruleNote, @@ -429,7 +436,9 @@ export const dispatchUpdateTimeline = } else { dispatch(dispatchSetTimelineRangeDatePicker({ from, to })); } - dispatch(dispatchAddTimeline({ id, timeline, savedTimeline: duplicate })); + dispatch( + dispatchAddTimeline({ id, timeline, resolveTimelineConfig, savedTimeline: duplicate }) + ); if ( timeline.kqlQuery != null && timeline.kqlQuery.filterQuery != null && diff --git a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts index 79a700856c00f..4c9ce991252dc 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts +++ b/x-pack/plugins/security_solution/public/timelines/components/open_timeline/types.ts @@ -16,6 +16,7 @@ import { TemplateTimelineTypeLiteral, RowRendererId, TimelineStatusLiteralWithNull, + SingleTimelineResolveResponse, } from '../../../../common/types/timeline'; /** The users who added a timeline to favorites */ @@ -194,12 +195,17 @@ export interface OpenTimelineProps { hideActions?: ActionTimelineToShow[]; } +export interface ResolveTimelineConfig { + alias_target_id: SingleTimelineResolveResponse['data']['alias_target_id']; + outcome: SingleTimelineResolveResponse['data']['outcome']; +} export interface UpdateTimeline { duplicate: boolean; id: string; forceNotes?: boolean; from: string; notes: NoteResult[] | null | undefined; + resolveTimelineConfig?: ResolveTimelineConfig; timeline: TimelineModel; to: string; ruleNote?: string; @@ -210,6 +216,7 @@ export type DispatchUpdateTimeline = ({ id, from, notes, + resolveTimelineConfig, timeline, to, ruleNote, diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx index db7a3cc3c9900..c91673e5f931c 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.test.tsx @@ -40,6 +40,12 @@ const mockUseResizeObserver: jest.Mock = useResizeObserver as jest.Mock; jest.mock('use-resize-observer/polyfilled'); mockUseResizeObserver.mockImplementation(() => ({})); +jest.mock('../../../common/hooks/use_resolve_conflict', () => { + return { + useResolveConflict: jest.fn().mockImplementation(() => null), + }; +}); + jest.mock('react-router-dom', () => { const original = jest.requireActual('react-router-dom'); diff --git a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx index a199dd5aa55f8..ca883529b5ce6 100644 --- a/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx +++ b/x-pack/plugins/security_solution/public/timelines/components/timeline/index.tsx @@ -28,6 +28,7 @@ import { TabsContent } from './tabs_content'; import { HideShowContainer, TimelineContainer } from './styles'; import { useTimelineFullScreen } from '../../../common/containers/use_full_screen'; import { EXIT_FULL_SCREEN_CLASS_NAME } from '../../../common/components/exit_full_screen'; +import { useResolveConflict } from '../../../common/hooks/use_resolve_conflict'; const TimelineTemplateBadge = styled.div` background: ${({ theme }) => theme.eui.euiColorVis3_behindText}; @@ -119,6 +120,7 @@ const StatefulTimelineComponent: React.FC = ({ [containerElement, onSkipFocusBeforeEventsTable, onSkipFocusAfterEventsTable] ); const timelineContext = useMemo(() => ({ timelineId }), [timelineId]); + const resolveConflictComponent = useResolveConflict(); return ( @@ -132,7 +134,7 @@ const StatefulTimelineComponent: React.FC = ({ {timelineType === TimelineType.template && ( {i18n.TIMELINE_TEMPLATE} )} - + {resolveConflictComponent} { return decodeSingleTimelineResponse(response); }; -export const getResolvedTimelineTemplate = async (templateTimelineId: string) => { - const response = await KibanaServices.get().http.get( - TIMELINE_RESOLVE_URL, - { - query: { - template_timeline_id: templateTimelineId, - }, - } - ); - - return decodeResolvedSingleTimelineResponse(response); -}; - export const getAllTimelines = async (args: GetTimelinesArgs, abortSignal: AbortSignal) => { const response = await KibanaServices.get().http.fetch(TIMELINES_URL, { method: 'GET', diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts index 95ad6c5d44ca3..f3a70bd1390ae 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/actions.ts @@ -25,6 +25,7 @@ import type { SerializedFilterQuery, } from '../../../../common/types/timeline'; import { tGridActions } from '../../../../../timelines/public'; +import { ResolveTimelineConfig } from '../../components/open_timeline/types'; export const { applyDeltaToColumnWidth, clearEventsDeleted, @@ -91,6 +92,7 @@ export const updateTimeline = actionCreator<{ export const addTimeline = actionCreator<{ id: string; timeline: TimelineModel; + resolveTimelineConfig?: ResolveTimelineConfig; savedTimeline?: boolean; }>('ADD_TIMELINE'); diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts index 4691872bfb927..4c2b8d2992d3d 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/defaults.ts @@ -14,64 +14,65 @@ import { SubsetTimelineModel, TimelineModel } from './model'; // normalizeTimeRange uses getTimeRangeSettings which cannot be used outside Kibana context if the uiSettings is not false const { from: start, to: end } = normalizeTimeRange({ from: '', to: '' }, false); -export const timelineDefaults: SubsetTimelineModel & Pick = - { - activeTab: TimelineTabs.query, - prevActiveTab: TimelineTabs.query, - columns: defaultHeaders, - documentType: '', - defaultColumns: defaultHeaders, - dataProviders: [], - dateRange: { start, end }, - deletedEventIds: [], - description: '', - eqlOptions: { - eventCategoryField: 'event.category', - tiebreakerField: '', - timestampField: '@timestamp', - query: '', - size: 100, +export const timelineDefaults: SubsetTimelineModel & + Pick = { + activeTab: TimelineTabs.query, + prevActiveTab: TimelineTabs.query, + columns: defaultHeaders, + documentType: '', + defaultColumns: defaultHeaders, + dataProviders: [], + dateRange: { start, end }, + deletedEventIds: [], + description: '', + eqlOptions: { + eventCategoryField: 'event.category', + tiebreakerField: '', + timestampField: '@timestamp', + query: '', + size: 100, + }, + eventType: 'all', + eventIdToNoteIds: {}, + excludedRowRendererIds: [], + expandedDetail: {}, + highlightedDropAndProviderId: '', + historyIds: [], + filters: [], + indexNames: [], + isFavorite: false, + isLive: false, + isSelectAllChecked: false, + isLoading: false, + isSaving: false, + itemsPerPage: 25, + itemsPerPageOptions: [10, 25, 50, 100], + kqlMode: 'filter', + kqlQuery: { + filterQuery: null, + }, + loadingEventIds: [], + resolveTimelineConfig: undefined, + queryFields: [], + title: '', + timelineType: TimelineType.default, + templateTimelineId: null, + templateTimelineVersion: null, + noteIds: [], + pinnedEventIds: {}, + pinnedEventsSaveObject: {}, + savedObjectId: null, + selectAll: false, + selectedEventIds: {}, + show: false, + showCheckboxes: false, + sort: [ + { + columnId: '@timestamp', + columnType: 'number', + sortDirection: 'desc', }, - eventType: 'all', - eventIdToNoteIds: {}, - excludedRowRendererIds: [], - expandedDetail: {}, - highlightedDropAndProviderId: '', - historyIds: [], - filters: [], - indexNames: [], - isFavorite: false, - isLive: false, - isSelectAllChecked: false, - isLoading: false, - isSaving: false, - itemsPerPage: 25, - itemsPerPageOptions: [10, 25, 50, 100], - kqlMode: 'filter', - kqlQuery: { - filterQuery: null, - }, - loadingEventIds: [], - queryFields: [], - title: '', - timelineType: TimelineType.default, - templateTimelineId: null, - templateTimelineVersion: null, - noteIds: [], - pinnedEventIds: {}, - pinnedEventsSaveObject: {}, - savedObjectId: null, - selectAll: false, - selectedEventIds: {}, - show: false, - showCheckboxes: false, - sort: [ - { - columnId: '@timestamp', - columnType: 'number', - sortDirection: 'desc', - }, - ], - status: TimelineStatus.draft, - version: null, - }; + ], + status: TimelineStatus.draft, + version: null, +}; diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts index 6ee844958aeed..b7af561ae2a04 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/helpers.ts @@ -47,6 +47,7 @@ import { RESIZED_COLUMN_MIN_WITH, } from '../../components/timeline/body/constants'; import { activeTimeline } from '../../containers/active_timeline_context'; +import { ResolveTimelineConfig } from '../../components/open_timeline/types'; export const isNotNull = (value: T | null): value is T => value !== null; @@ -124,6 +125,7 @@ export const addTimelineNoteToEvent = ({ interface AddTimelineParams { id: string; + resolveTimelineConfig?: ResolveTimelineConfig; timeline: TimelineModel; timelineById: TimelineById; } @@ -145,6 +147,7 @@ export const shouldResetActiveTimelineContext = ( */ export const addTimelineToStore = ({ id, + resolveTimelineConfig, timeline, timelineById, }: AddTimelineParams): TimelineById => { @@ -159,6 +162,7 @@ export const addTimelineToStore = ({ filterManager: timelineById[id].filterManager, isLoading: timelineById[id].isLoading, initialized: timelineById[id].initialized, + resolveTimelineConfig, dateRange: timeline.status === TimelineStatus.immutable && timeline.timelineType === TimelineType.template diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts index b53da997c08cb..29b49197ef797 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/model.ts @@ -15,6 +15,7 @@ import type { } from '../../../../common/types/timeline'; import { PinnedEvent } from '../../../../common/types/timeline/pinned_event'; import type { TGridModelForTimeline } from '../../../../../timelines/public'; +import { ResolveTimelineConfig } from '../../components/open_timeline/types'; export const DEFAULT_PAGE_COUNT = 2; // Eui Pager will not render unless this is a minimum of 2 pages export type KqlMode = 'filter' | 'search'; @@ -59,6 +60,7 @@ export type TimelineModel = TGridModelForTimeline & { /** Events pinned to this timeline */ pinnedEventIds: Record; pinnedEventsSaveObject: Record; + resolveTimelineConfig?: ResolveTimelineConfig; showSaveModal?: boolean; savedQueryId?: string | null; /** When true, show the timeline flyover */ diff --git a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts index 97fa72667a3c6..e997bbd848d50 100644 --- a/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts +++ b/x-pack/plugins/security_solution/public/timelines/store/timeline/reducer.ts @@ -94,9 +94,14 @@ export const initialTimelineState: TimelineState = { /** The reducer for all timeline actions */ export const timelineReducer = reducerWithInitialState(initialTimelineState) - .case(addTimeline, (state, { id, timeline }) => ({ + .case(addTimeline, (state, { id, timeline, resolveTimelineConfig }) => ({ ...state, - timelineById: addTimelineToStore({ id, timeline, timelineById: state.timelineById }), + timelineById: addTimelineToStore({ + id, + timeline, + resolveTimelineConfig, + timelineById: state.timelineById, + }), })) .case(createTimeline, (state, { id, timelineType = TimelineType.default, ...timelineProps }) => { return { diff --git a/x-pack/plugins/security_solution/public/types.ts b/x-pack/plugins/security_solution/public/types.ts index bee0e9b3a3d1d..61813d1a122b4 100644 --- a/x-pack/plugins/security_solution/public/types.ts +++ b/x-pack/plugins/security_solution/public/types.ts @@ -9,7 +9,6 @@ import { CoreStart } from '../../../../src/core/public'; import { HomePublicPluginSetup } from '../../../../src/plugins/home/public'; import { DataPublicPluginStart } from '../../../../src/plugins/data/public'; import { EmbeddableStart } from '../../../../src/plugins/embeddable/public'; -import { SpacesPluginStart } from '../../../plugins/spaces/public'; import { LensPublicStart } from '../../../plugins/lens/public'; import { NewsfeedPublicPluginStart } from '../../../../src/plugins/newsfeed/public'; import { Start as InspectorStart } from '../../../../src/plugins/inspector/public'; @@ -18,6 +17,7 @@ import { UsageCollectionSetup } from '../../../../src/plugins/usage_collection/p import { Storage } from '../../../../src/plugins/kibana_utils/public'; import { FleetStart } from '../../fleet/public'; import { PluginStart as ListsPluginStart } from '../../lists/public'; +import { SpacesPluginStart } from '../../spaces/public'; import { TriggersAndActionsUIPublicPluginSetup as TriggersActionsSetup, TriggersAndActionsUIPublicPluginStart as TriggersActionsStart, diff --git a/x-pack/plugins/security_solution/tsconfig.json b/x-pack/plugins/security_solution/tsconfig.json index 5c87d58199df4..8f4d602e26461 100644 --- a/x-pack/plugins/security_solution/tsconfig.json +++ b/x-pack/plugins/security_solution/tsconfig.json @@ -1,4 +1,3 @@ - { "extends": "../../../tsconfig.base.json", "compilerOptions": { @@ -40,7 +39,7 @@ { "path": "../maps/tsconfig.json" }, { "path": "../ml/tsconfig.json" }, { "path": "../spaces/tsconfig.json" }, - { "path": "../security/tsconfig.json"}, - { "path": "../timelines/tsconfig.json"}, + { "path": "../security/tsconfig.json" }, + { "path": "../timelines/tsconfig.json" } ] } From 1a5570baf45551bc8bacc6786a0ab513d3b9af79 Mon Sep 17 00:00:00 2001 From: Brian Seeders Date: Tue, 19 Oct 2021 18:55:02 -0400 Subject: [PATCH 12/13] [buildkite] Add timeouts to all steps missing them (#115656) --- .buildkite/pipelines/es_snapshots/verify.yml | 4 ++++ .buildkite/pipelines/hourly.yml | 6 ++++++ .buildkite/pipelines/on_merge.yml | 4 ++++ .buildkite/pipelines/pull_request/base.yml | 7 ++++++- .buildkite/pipelines/update_demo_env.yml | 2 ++ 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/.buildkite/pipelines/es_snapshots/verify.yml b/.buildkite/pipelines/es_snapshots/verify.yml index b9aa0e0e3727a..9cddade0b7482 100755 --- a/.buildkite/pipelines/es_snapshots/verify.yml +++ b/.buildkite/pipelines/es_snapshots/verify.yml @@ -13,6 +13,7 @@ steps: - command: .buildkite/scripts/lifecycle/pre_build.sh label: Pre-Build + timeout_in_minutes: 10 - wait @@ -22,6 +23,7 @@ steps: queue: c2-16 key: build if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" + timeout_in_minutes: 60 - command: .buildkite/scripts/steps/functional/xpack_cigroup.sh label: 'Default CI Group' @@ -81,6 +83,7 @@ steps: - command: .buildkite/scripts/steps/es_snapshots/trigger_promote.sh label: Trigger promotion + timeout_in_minutes: 10 depends_on: - default-cigroup - default-cigroup-docker @@ -93,3 +96,4 @@ steps: - command: .buildkite/scripts/lifecycle/post_build.sh label: Post-Build + timeout_in_minutes: 10 diff --git a/.buildkite/pipelines/hourly.yml b/.buildkite/pipelines/hourly.yml index 0edba11836fcd..3337cfb5dfcdd 100644 --- a/.buildkite/pipelines/hourly.yml +++ b/.buildkite/pipelines/hourly.yml @@ -3,6 +3,7 @@ env: steps: - command: .buildkite/scripts/lifecycle/pre_build.sh label: Pre-Build + timeout_in_minutes: 10 - wait @@ -12,6 +13,7 @@ steps: queue: c2-16 key: build if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" + timeout_in_minutes: 60 - command: .buildkite/scripts/steps/functional/xpack_cigroup.sh label: 'Default CI Group' @@ -143,21 +145,25 @@ steps: agents: queue: n2-2 key: linting + timeout_in_minutes: 90 - command: .buildkite/scripts/steps/checks.sh label: 'Checks' agents: queue: c2-4 key: checks + timeout_in_minutes: 120 - command: .buildkite/scripts/steps/storybooks/build_and_upload.sh label: 'Build Storybooks' agents: queue: c2-4 key: storybooks + timeout_in_minutes: 60 - wait: ~ continue_on_failure: true - command: .buildkite/scripts/lifecycle/post_build.sh label: Post-Build + timeout_in_minutes: 10 diff --git a/.buildkite/pipelines/on_merge.yml b/.buildkite/pipelines/on_merge.yml index dc0541c6397d9..2aba49bfa6460 100644 --- a/.buildkite/pipelines/on_merge.yml +++ b/.buildkite/pipelines/on_merge.yml @@ -4,6 +4,7 @@ env: steps: - command: .buildkite/scripts/lifecycle/pre_build.sh label: Pre-Build + timeout_in_minutes: 10 - wait @@ -13,14 +14,17 @@ steps: BAZEL_CACHE_MODE: read-write agents: queue: c2-8 + timeout_in_minutes: 60 - command: .buildkite/scripts/steps/on_merge_ts_refs_api_docs.sh label: Build TS Refs and Check Public API Docs agents: queue: c2-4 + timeout_in_minutes: 80 - wait: ~ continue_on_failure: true - command: .buildkite/scripts/lifecycle/post_build.sh label: Post-Build + timeout_in_minutes: 10 diff --git a/.buildkite/pipelines/pull_request/base.yml b/.buildkite/pipelines/pull_request/base.yml index 404bfb273b6f7..1013a841dfd27 100644 --- a/.buildkite/pipelines/pull_request/base.yml +++ b/.buildkite/pipelines/pull_request/base.yml @@ -1,6 +1,7 @@ steps: - command: .buildkite/scripts/lifecycle/pre_build.sh label: Pre-Build + timeout_in_minutes: 10 - wait @@ -10,6 +11,7 @@ steps: queue: c2-16 key: build if: "build.env('KIBANA_BUILD_ID') == null || build.env('KIBANA_BUILD_ID') == ''" + timeout_in_minutes: 60 - command: .buildkite/scripts/steps/functional/xpack_cigroup.sh label: 'Default CI Group' @@ -17,7 +19,7 @@ steps: agents: queue: ci-group-6 depends_on: build - timeout_in_minutes: 120 + timeout_in_minutes: 150 key: default-cigroup retry: automatic: @@ -141,15 +143,18 @@ steps: agents: queue: n2-2 key: linting + timeout_in_minutes: 90 - command: .buildkite/scripts/steps/checks.sh label: 'Checks' agents: queue: c2-4 key: checks + timeout_in_minutes: 120 - command: .buildkite/scripts/steps/storybooks/build_and_upload.sh label: 'Build Storybooks' agents: queue: c2-4 key: storybooks + timeout_in_minutes: 60 diff --git a/.buildkite/pipelines/update_demo_env.yml b/.buildkite/pipelines/update_demo_env.yml index 1c15b227a2e4a..e2dfdd782fd41 100644 --- a/.buildkite/pipelines/update_demo_env.yml +++ b/.buildkite/pipelines/update_demo_env.yml @@ -1,8 +1,10 @@ steps: - command: .buildkite/scripts/steps/demo_env/es_and_init.sh label: Initialize Environment and Deploy ES + timeout_in_minutes: 10 - command: .buildkite/scripts/steps/demo_env/kibana.sh label: Build and Deploy Kibana agents: queue: c2-8 + timeout_in_minutes: 60 From 67d6355b2e8fe3f40acca7e669cec30fbd227bc5 Mon Sep 17 00:00:00 2001 From: Matthew Kime Date: Tue, 19 Oct 2021 18:36:24 -0500 Subject: [PATCH 13/13] [data view management] Copy improvements (#114413) * better copy --- .../empty_index_list_prompt.test.tsx.snap | 14 ++++++++------ .../empty_index_list_prompt.tsx | 12 ++++++------ .../index_header/index_header.tsx | 4 ++-- .../__snapshots__/field_editor.test.tsx.snap | 10 +++++----- .../components/field_editor/field_editor.tsx | 3 +-- .../plugins/translations/translations/ja-JP.json | 1 - .../plugins/translations/translations/zh-CN.json | 1 - 7 files changed, 22 insertions(+), 23 deletions(-) diff --git a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/__snapshots__/empty_index_list_prompt.test.tsx.snap b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/__snapshots__/empty_index_list_prompt.test.tsx.snap index a104c36e3a8a0..47cad9c7a8216 100644 --- a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/__snapshots__/empty_index_list_prompt.test.tsx.snap +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/__snapshots__/empty_index_list_prompt.test.tsx.snap @@ -120,10 +120,12 @@ exports[`EmptyIndexListPrompt should render normally 1`] = `
- + diff --git a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx index d00f9e2368e21..a550209095898 100644 --- a/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx +++ b/src/plugins/index_pattern_editor/public/components/empty_prompts/empty_index_list_prompt/empty_index_list_prompt.tsx @@ -46,14 +46,14 @@ export const EmptyIndexListPrompt = ({ const createAnywayLink = ( createAnyway()} data-test-subj="createAnyway"> ), @@ -153,8 +153,8 @@ export const EmptyIndexListPrompt = ({
- - + + - + = ({ diff --git a/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap b/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap index b9d1b3992778f..87aa20c4617c1 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap +++ b/src/plugins/index_pattern_management/public/components/field_editor/__snapshots__/field_editor.test.tsx.snap @@ -122,7 +122,7 @@ exports[`FieldEditor should render create new scripted field correctly 1`] = ` @@ -365,7 +365,7 @@ exports[`FieldEditor should render edit scripted field correctly 1`] = ` @@ -658,7 +658,7 @@ exports[`FieldEditor should show conflict field warning 1`] = ` @@ -994,7 +994,7 @@ exports[`FieldEditor should show deprecated lang warning 1`] = ` @@ -1343,7 +1343,7 @@ exports[`FieldEditor should show multiple type field warning with a table contai diff --git a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx index 695d02d0744fb..9509f4fb46e0b 100644 --- a/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx +++ b/src/plugins/index_pattern_management/public/components/field_editor/field_editor.tsx @@ -506,8 +506,7 @@ export class FieldEditor extends PureComponent } > diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index 781ef74a872eb..c54512203677d 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -3722,7 +3722,6 @@ "indexPatternEditor.aliasLabel": "エイリアス", "indexPatternEditor.createIndex.noMatch": "名前は1つ以上のデータストリーム、インデックス、またはインデックスエイリアスと一致する必要があります。", "indexPatternEditor.createIndexPattern.emptyState.checkDataButton": "新規データを確認", - "indexPatternEditor.createIndexPattern.emptyState.createAnyway": "一部のインデックスは表示されない場合があります。{link}してください。", "indexPatternEditor.createIndexPattern.emptyState.createAnywayLink": "インデックスパターンを作成します", "indexPatternEditor.createIndexPattern.emptyState.haveData": "すでにデータがある場合", "indexPatternEditor.createIndexPattern.emptyState.integrationCardDescription": "さまざまなソースからデータを追加します。", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 92646cc1f9cd9..fbc65161a47f8 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -3755,7 +3755,6 @@ "indexPatternEditor.aliasLabel": "别名", "indexPatternEditor.createIndex.noMatch": "名称必须匹配一个或多个数据流、索引或索引别名。", "indexPatternEditor.createIndexPattern.emptyState.checkDataButton": "检查新数据", - "indexPatternEditor.createIndexPattern.emptyState.createAnyway": "部分索引可能已隐藏。仍然尝试{link}。", "indexPatternEditor.createIndexPattern.emptyState.createAnywayLink": "创建索引模式", "indexPatternEditor.createIndexPattern.emptyState.haveData": "假设您已有数据?", "indexPatternEditor.createIndexPattern.emptyState.integrationCardDescription": "从各种源添加数据。",