From ed3a3bdfb03fa60bedfffa9b11d7a8eea93da457 Mon Sep 17 00:00:00 2001 From: Eugene Lee Date: Wed, 23 Mar 2022 15:37:04 -0700 Subject: [PATCH] Modularize and cleanup traces (#601) Signed-off-by: Eugene Lee --- .../integration/app_analytics.spec.js | 23 +- .../integration/event_analytics.spec.js | 17 +- .../.cypress/integration/notebooks.spec.js | 28 + .../.cypress/integration/panels.spec.js | 14 + .../trace_analytics_dashboard.spec.js | 12 + .../trace_analytics_services.spec.js | 17 +- .../trace_analytics_traces.spec.js | 21 + .../components/application.tsx | 18 +- .../public/components/explorer/home.tsx | 2 +- .../__snapshots__/dashboard.test.tsx.snap | 16 +- .../dashboard/__tests__/dashboard.test.tsx | 4 +- .../components/dashboard/dashboard.tsx | 2 +- .../components/services/services.tsx | 2 +- .../__snapshots__/traces.test.tsx.snap | 3432 +++++++++-------- .../__snapshots__/traces_table.test.tsx.snap | 14 +- .../traces/__tests__/traces.test.tsx | 28 + .../traces/__tests__/traces_table.test.tsx | 28 +- .../components/traces/traces.tsx | 114 +- .../components/traces/traces_content.tsx | 88 + .../components/traces/traces_table.tsx | 59 +- .../components/trace_analytics/home.tsx | 23 +- 21 files changed, 2164 insertions(+), 1798 deletions(-) create mode 100644 dashboards-observability/public/components/trace_analytics/components/traces/traces_content.tsx diff --git a/dashboards-observability/.cypress/integration/app_analytics.spec.js b/dashboards-observability/.cypress/integration/app_analytics.spec.js index 701c79bdd..2ebe3b4b1 100644 --- a/dashboards-observability/.cypress/integration/app_analytics.spec.js +++ b/dashboards-observability/.cypress/integration/app_analytics.spec.js @@ -46,14 +46,14 @@ describe('Creating application', () => { expectMessageOnHover('Provide at least one log source, service, entity or trace group.'); cy.get('.euiAccordion').contains('Services & entities').click(); cy.get('[data-test-subj="servicesEntitiesComboBox"]').click(); - cy.get('.euiFilterSelectItem').contains(service_one).trigger('click'); + cy.get('.euiFilterSelectItem').contains(service_one).click(); cy.get('.euiBadge').contains('1').should('exist'); cy.get('.euiButton').contains('Create').should('not.be.disabled'); cy.get('[data-test-subj="searchAutocompleteTextArea"]').type(baseQuery); cy.get('.euiAccordion').contains('Trace groups').click(); cy.get('[data-test-subj="traceGroupsComboBox"]').type('http'); - cy.get('.euiFilterSelectItem').contains(trace_one).trigger('click'); - cy.get('.euiFilterSelectItem').contains(trace_two).trigger('click'); + cy.get('.euiFilterSelectItem').contains(trace_one).click(); + cy.get('.euiFilterSelectItem').contains(trace_two).click(); cy.get('.euiBadge').contains('2').should('exist'); cy.get('.euiButton').contains('Create').should('not.be.disabled'); }); @@ -74,7 +74,7 @@ describe('Creating application', () => { cy.get('.aa-List').find('.aa-Item').should('have.length', 1); cy.focused().type('{enter}'); cy.get('[data-test-subj="searchAutocompleteTextArea"]').should('contain', 'source '); - }) + }); it('Creates an application and redirects to application', () => { cy.get('[data-test-subj="nameFormRow"]').type(name); @@ -163,9 +163,22 @@ describe('Creating application', () => { describe('Viewing application', () => { before(() => { moveToApplication(); - }) + }); + + it('Has working breadcrumbs', () => { + cy.get('.euiBreadcrumb').contains('Cypress').click(); + cy.wait(delay); + cy.get('.euiTitle').contains(name).should('exist'); + cy.get('.euiBreadcrumb').contains('Application analytics').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Applications').should('exist'); + cy.get('.euiBreadcrumb').contains('Observability').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Event analytics').should('exist'); + }); it('Shares time range among tabs', () => { + moveToApplication(); changeTimeTo24('months'); cy.get('[data-test-subj="superDatePickerShowDatesButton"]').should('contain', 'Last 24 months'); cy.get('.euiTab').contains('Services').click(); diff --git a/dashboards-observability/.cypress/integration/event_analytics.spec.js b/dashboards-observability/.cypress/integration/event_analytics.spec.js index 1e784a275..e5347551e 100644 --- a/dashboards-observability/.cypress/integration/event_analytics.spec.js +++ b/dashboards-observability/.cypress/integration/event_analytics.spec.js @@ -24,7 +24,7 @@ const landOnEventExplorer = () => { cy.visit( `${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/event_analytics/explorer` ); - cy.wait(delay); + cy.wait(delay * 2); }; const landOnPanels = () => { @@ -51,6 +51,21 @@ describe('Adding sample data and visualization', () => { }); }); +describe('Has working breadcrumbs', () => { + it('Redirect to correct page on breadcrumb click', () => { + landOnEventExplorer(); + cy.get('.euiBreadcrumb').contains('Explorer').click(); + cy.wait(delay); + cy.get('[data-test-subj="searchAutocompleteTextArea"]').should('exist'); + cy.get('.euiBreadcrumb').contains('Event analytics').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Event analytics').should('exist'); + cy.get('.euiBreadcrumb').contains('Observability').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Event analytics').should('exist'); + }); +}); + describe('Open flyout for a data row to see details', () => { beforeEach(() => { landOnEventExplorer(); diff --git a/dashboards-observability/.cypress/integration/notebooks.spec.js b/dashboards-observability/.cypress/integration/notebooks.spec.js index a44f531f5..d15b922a7 100644 --- a/dashboards-observability/.cypress/integration/notebooks.spec.js +++ b/dashboards-observability/.cypress/integration/notebooks.spec.js @@ -202,6 +202,18 @@ describe('Testing paragraphs', () => { cy.wait(delay); }); + it('Has working breadcrumbs', () => { + cy.get('.euiBreadcrumb').contains(TEST_NOTEBOOK).click(); + cy.wait(delay); + cy.get('.euiTitle').contains(TEST_NOTEBOOK).should('exist'); + cy.get('.euiBreadcrumb').contains('Notebooks').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Notebooks').should('exist'); + cy.get('.euiBreadcrumb').contains('Observability').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Event analytics').should('exist'); + }); + it('Renders markdown', () => { cy.get('.euiTextArea').should('not.exist'); cy.get(`a[href="${SAMPLE_URL}"]`).should('exist'); @@ -432,4 +444,20 @@ describe('Testing paragraphs', () => { cy.get('.euiButton__text').contains('Create notebook').should('exist'); }); + + it('Cleans up test notebooks', () => { + cy.get('[data-test-subj="notebook-notebook-actions-button"]').click(); + cy.wait(delay); + cy.get('.euiContextMenuItem__text').contains('Delete notebook').click(); + cy.wait(delay); + + cy.get('button.euiButton--danger').should('be.disabled'); + + cy.get('input.euiFieldText[placeholder="delete"]').type('delete'); + cy.get('button.euiButton--danger').should('not.be.disabled'); + cy.get('.euiButton__text').contains('Delete').click(); + cy.wait(delay * 3); + + cy.get('.euiText').contains('No notebooks').should('exist'); + }); }); diff --git a/dashboards-observability/.cypress/integration/panels.spec.js b/dashboards-observability/.cypress/integration/panels.spec.js index b2eb67402..038472413 100644 --- a/dashboards-observability/.cypress/integration/panels.spec.js +++ b/dashboards-observability/.cypress/integration/panels.spec.js @@ -183,7 +183,21 @@ describe('Testing a panel', () => { moveToTestPanel(); }); + it('Redirects to correct page on breadcrumb click', () => { + moveToTestPanel(); + cy.get('.euiBreadcrumb').contains(TEST_PANEL).click(); + cy.wait(delay); + cy.get('.euiTitle').contains(TEST_PANEL).should('exist'); + cy.get('.euiBreadcrumb').contains('Operational panels').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Operational panels').should('exist'); + cy.get('.euiBreadcrumb').contains('Observability').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Event analytics').should('exist'); + }); + it('Duplicate the open panel', () => { + moveToTestPanel(); cy.get('.euiButton__text').contains('Panel actions').click(); cy.wait(delay); cy.get('.euiContextMenuItem__text').contains('Duplicate panel').click(); diff --git a/dashboards-observability/.cypress/integration/trace_analytics_dashboard.spec.js b/dashboards-observability/.cypress/integration/trace_analytics_dashboard.spec.js index 72f8da596..5512635ca 100644 --- a/dashboards-observability/.cypress/integration/trace_analytics_dashboard.spec.js +++ b/dashboards-observability/.cypress/integration/trace_analytics_dashboard.spec.js @@ -98,6 +98,18 @@ describe('Testing dashboard table', () => { cy.contains('7.14%').should('exist'); }); + it('Has working breadcrumbs', () => { + cy.get('.euiBreadcrumb').contains('Dashboard').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Dashboard').should('exist'); + cy.get('.euiBreadcrumb').contains('Trace analytics').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Dashboard').should('exist'); + cy.get('.euiBreadcrumb').contains('Observability').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Event analytics').should('exist'); + }); + it('Adds the percentile filters', () => { cy.contains(' >= 95 percentile').click({ force: true }); cy.wait(delay); diff --git a/dashboards-observability/.cypress/integration/trace_analytics_services.spec.js b/dashboards-observability/.cypress/integration/trace_analytics_services.spec.js index 0cbbc2287..bb3c43490 100644 --- a/dashboards-observability/.cypress/integration/trace_analytics_services.spec.js +++ b/dashboards-observability/.cypress/integration/trace_analytics_services.spec.js @@ -87,12 +87,27 @@ describe('Testing service view', () => { }); it('Renders service view', () => { - cy.get('h2.euiTitle').contains('frontend-client').should('exist'); + cy.get('h2.euiTitle').contains(SERVICE_NAME).should('exist'); cy.contains('178.6').should('exist'); cy.contains('3.57%').should('exist'); cy.get('div.vis-network').should('exist'); }); + it('Has working breadcrumbs', () => { + cy.get('.euiBreadcrumb').contains(SERVICE_NAME).click(); + cy.wait(delay); + cy.get('h2.euiTitle').contains(SERVICE_NAME).should('exist'); + cy.get('.euiBreadcrumb').contains('Services').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Services').should('exist'); + cy.get('.euiBreadcrumb').contains('Trace analytics').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Dashboard').should('exist'); + cy.get('.euiBreadcrumb').contains('Observability').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Event analytics').should('exist'); + }); + it('Renders spans data grid, flyout, filters', () => { cy.get('button[data-datagrid-interactable="true"]').eq(0).click({ force: true }); cy.wait(delay); diff --git a/dashboards-observability/.cypress/integration/trace_analytics_traces.spec.js b/dashboards-observability/.cypress/integration/trace_analytics_traces.spec.js index 847090612..2f96fab54 100644 --- a/dashboards-observability/.cypress/integration/trace_analytics_traces.spec.js +++ b/dashboards-observability/.cypress/integration/trace_analytics_traces.spec.js @@ -45,6 +45,12 @@ describe('Testing traces table', () => { cy.contains('-').should('exist'); }); + it('Sorts the traces table', () => { + cy.get('.euiTableRow').first().contains('-').should('exist'); + cy.get('.euiTableCellContent').contains('Trace group').click(); + cy.get('.euiTableRow').first().contains('/%2A%2A').should('exist'); + }); + it('Searches correctly', () => { cy.get('input[type="search"]').focus().type(`${TRACE_ID}{enter}`); cy.get('.euiButton__text').contains('Refresh').click(); @@ -75,6 +81,21 @@ describe('Testing trace view', () => { cy.contains(`"${SPAN_ID}"`).should('exist'); }); + it('Has working breadcrumbs', () => { + cy.get('.euiBreadcrumb').contains(TRACE_ID).click(); + cy.wait(delay); + cy.get('h2.euiTitle').contains(TRACE_ID).should('exist'); + cy.get('.euiBreadcrumb').contains('Traces').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Traces').should('exist'); + cy.get('.euiBreadcrumb').contains('Trace analytics').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Dashboard').should('exist'); + cy.get('.euiBreadcrumb').contains('Observability').click(); + cy.wait(delay); + cy.get('.euiTitle').contains('Event analytics').should('exist'); + }); + it('Renders data grid, flyout and filters', () => { cy.get('.euiToggle__input[title="Span list"]').click({ force: true }); cy.contains('2 columns hidden').should('exist'); diff --git a/dashboards-observability/public/components/application_analytics/components/application.tsx b/dashboards-observability/public/components/application_analytics/components/application.tsx index 3712afd97..9c47d1a4f 100644 --- a/dashboards-observability/public/components/application_analytics/components/application.tsx +++ b/dashboards-observability/public/components/application_analytics/components/application.tsx @@ -7,7 +7,6 @@ import { EuiHorizontalRule, - EuiLink, EuiPage, EuiPageBody, EuiPageHeader, @@ -25,18 +24,19 @@ import PPLService from 'public/services/requests/ppl'; import SavedObjects from 'public/services/saved_objects/event_analytics/saved_objects'; import TimestampUtils from 'public/services/timestamp/timestamp'; import React, { ReactChild, useEffect, useState } from 'react'; -import { truncate, uniqueId } from 'lodash'; +import { uniqueId } from 'lodash'; import { useHistory } from 'react-router-dom'; import { useDispatch } from 'react-redux'; import { last } from 'lodash'; +import { TracesContent } from '../../../components/trace_analytics/components/traces/traces_content'; import { DashboardContent } from '../../../components/trace_analytics/components/dashboard/dashboard_content'; +import { ServicesContent } from '../../trace_analytics/components/services/services_content'; import { filtersToDsl, PanelTitle, } from '../../../../public/components/trace_analytics/components/common/helper_functions'; import { SpanDetailTable } from '../../../../public/components/trace_analytics/components/traces/span_detail_table'; import { Explorer } from '../../explorer/explorer'; -import { Traces } from '../../trace_analytics/components/traces'; import { Configuration } from './configuration'; import { TAB_CONFIG_ID_TXT_PFX, @@ -63,7 +63,6 @@ import { ServiceDetailFlyout } from './flyout_components/service_detail_flyout'; import { SpanDetailFlyout } from '../../../../public/components/trace_analytics/components/traces/span_detail_flyout'; import { TraceDetailFlyout } from './flyout_components/trace_detail_flyout'; import { fetchAppById, initializeTabData } from '../helpers/utils'; -import { ServicesContent } from '../../trace_analytics/components/services/services_content'; const TAB_OVERVIEW_ID = uniqueId(TAB_OVERVIEW_ID_TXT_PFX); const TAB_SERVICE_ID = uniqueId(TAB_SERVICE_ID_TXT_PFX); @@ -318,18 +317,19 @@ export function Application(props: AppDetailProps) { setSelectedTab(TAB_TRACE_ID); }; + const traceIdColumnAction = (item: any) => openTraceFlyout(item); + const getTrace = () => { return ( <> - + diff --git a/dashboards-observability/public/components/explorer/home.tsx b/dashboards-observability/public/components/explorer/home.tsx index 8a9d7cec0..88606cd6f 100644 --- a/dashboards-observability/public/components/explorer/home.tsx +++ b/dashboards-observability/public/components/explorer/home.tsx @@ -350,7 +350,7 @@ export const Home = (props: IHomeProps) => { -

Event Analytics

+

Event analytics

diff --git a/dashboards-observability/public/components/trace_analytics/components/dashboard/__tests__/__snapshots__/dashboard.test.tsx.snap b/dashboards-observability/public/components/trace_analytics/components/dashboard/__tests__/__snapshots__/dashboard.test.tsx.snap index b28ee37d9..45ef0c814 100644 --- a/dashboards-observability/public/components/trace_analytics/components/dashboard/__tests__/__snapshots__/dashboard.test.tsx.snap +++ b/dashboards-observability/public/components/trace_analytics/components/dashboard/__tests__/__snapshots__/dashboard.test.tsx.snap @@ -11,7 +11,7 @@ exports[`Dashboard component renders dashboard 1`] = ` }, Object { "href": "#/trace_analytics/home", - "text": "Dashboards", + "text": "Dashboard", }, ] } @@ -32,7 +32,7 @@ exports[`Dashboard component renders dashboard 1`] = ` }, Object { "href": "#/trace_analytics/home", - "text": "Dashboards", + "text": "Dashboard", }, ], ], @@ -104,7 +104,7 @@ exports[`Dashboard component renders dashboard 1`] = ` }, Object { "href": "#/trace_analytics/home", - "text": "Dashboards", + "text": "Dashboard", }, ] } @@ -125,7 +125,7 @@ exports[`Dashboard component renders dashboard 1`] = ` }, Object { "href": "#/trace_analytics/home", - "text": "Dashboards", + "text": "Dashboard", }, ], ], @@ -2096,7 +2096,7 @@ exports[`Dashboard component renders empty dashboard 1`] = ` }, Object { "href": "#/trace_analytics/home", - "text": "Dashboards", + "text": "Dashboard", }, ] } @@ -2117,7 +2117,7 @@ exports[`Dashboard component renders empty dashboard 1`] = ` }, Object { "href": "#/trace_analytics/home", - "text": "Dashboards", + "text": "Dashboard", }, ], ], @@ -2189,7 +2189,7 @@ exports[`Dashboard component renders empty dashboard 1`] = ` }, Object { "href": "#/trace_analytics/home", - "text": "Dashboards", + "text": "Dashboard", }, ] } @@ -2210,7 +2210,7 @@ exports[`Dashboard component renders empty dashboard 1`] = ` }, Object { "href": "#/trace_analytics/home", - "text": "Dashboards", + "text": "Dashboard", }, ], ], diff --git a/dashboards-observability/public/components/trace_analytics/components/dashboard/__tests__/dashboard.test.tsx b/dashboards-observability/public/components/trace_analytics/components/dashboard/__tests__/dashboard.test.tsx index 14b67f290..0f5c16918 100644 --- a/dashboards-observability/public/components/trace_analytics/components/dashboard/__tests__/dashboard.test.tsx +++ b/dashboards-observability/public/components/trace_analytics/components/dashboard/__tests__/dashboard.test.tsx @@ -35,7 +35,7 @@ describe('Dashboard component', () => { href: '#/trace_analytics/home', }, { - text: 'Dashboards', + text: 'Dashboard', href: '#/trace_analytics/home', }, ]} @@ -78,7 +78,7 @@ describe('Dashboard component', () => { href: '#/trace_analytics/home', }, { - text: 'Dashboards', + text: 'Dashboard', href: '#/trace_analytics/home', }, ]} diff --git a/dashboards-observability/public/components/trace_analytics/components/dashboard/dashboard.tsx b/dashboards-observability/public/components/trace_analytics/components/dashboard/dashboard.tsx index 0b3d4ba61..c5bf0a501 100644 --- a/dashboards-observability/public/components/trace_analytics/components/dashboard/dashboard.tsx +++ b/dashboards-observability/public/components/trace_analytics/components/dashboard/dashboard.tsx @@ -10,7 +10,7 @@ import { DashboardContent } from './dashboard_content'; export interface DashboardProps extends TraceAnalyticsComponentDeps { childBreadcrumbs: EuiBreadcrumb[]; - page: 'dashboard' | 'traces' | 'services' | 'app'; + page: 'dashboard' | 'app'; } export function Dashboard(props: DashboardProps) { diff --git a/dashboards-observability/public/components/trace_analytics/components/services/services.tsx b/dashboards-observability/public/components/trace_analytics/components/services/services.tsx index 43e6170b7..04c6bad2f 100644 --- a/dashboards-observability/public/components/trace_analytics/components/services/services.tsx +++ b/dashboards-observability/public/components/trace_analytics/components/services/services.tsx @@ -13,7 +13,7 @@ export interface ServicesProps extends TraceAnalyticsComponentDeps { childBreadcrumbs: EuiBreadcrumb[]; nameColumnAction: any; traceColumnAction: any; - page: 'dashboard' | 'traces' | 'services' | 'app'; + page: 'services' | 'app'; } export function Services(props: ServicesProps) { diff --git a/dashboards-observability/public/components/trace_analytics/components/traces/__tests__/__snapshots__/traces.test.tsx.snap b/dashboards-observability/public/components/trace_analytics/components/traces/__tests__/__snapshots__/traces.test.tsx.snap index cab39bbf2..8b5a52f96 100644 --- a/dashboards-observability/public/components/trace_analytics/components/traces/__tests__/__snapshots__/traces.test.tsx.snap +++ b/dashboards-observability/public/components/trace_analytics/components/traces/__tests__/__snapshots__/traces.test.tsx.snap @@ -3,6 +3,18 @@ exports[`Traces component renders empty traces page 1`] = ` - - -
- -
- + +
- -
- - - - -
- + + + +
- - - - -
-
+ > + + + + +
+
+
+
-
- -
-
-
- -
+ +
+
+ - - -
- -
- - } +
+ +
-
+ } > - - - - + - + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="QuickSelectPopover" + isOpen={false} + ownFocus={true} + panelPaddingSize="m" > -
- - - + + + +
-
- - - -
- } - iconType={false} - isCustom={true} - startDateControl={
} + + + +
-
} + iconType={false} + isCustom={true} + startDateControl={
} > - -
- - + + Show dates + + + +
+ + +
-
- -
-
-
- - -
-
- -
+
+
+
+
+
+
+ + - - - - - - - - -
- -
- - + + + +
+ + + + +
+ + - -
- -
- - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={false} - panelPaddingSize="none" - withTitle={true} - > - + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + withTitle={true} > -
- - - + > + + + + + +
-
- - - -
- - -
+ + +
+
+ - - - + Add filter - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={false} - panelPaddingSize="m" - withTitle={true} - > - + + + + Add filter + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={false} + panelPaddingSize="m" + withTitle={true} > -
- - - + + + +
-
- - - - - - - - -
- -
- - - + + + +
+ + + + + +
- + + + +
-
- -
- - -
- - Traces - - - (0) - -
-
-
-
-
-
- - -
- - -
-
- - - Learn more - - } - body={ - - The indices required for trace analytics (otel-v1-apm-span-* and otel-v1-apm-service-map*) do not exist or you do not have permission to access them. - - } - title={ -

- Trace Analytics not set up -

- } + + Traces + + + (0) + +
+ + +
+ +
+ +
+ + +
+
+ + + Learn more + + } + body={ + + The indices required for trace analytics (otel-v1-apm-span-* and otel-v1-apm-service-map*) do not exist or you do not have permission to access them. + + } + title={ +

+ Trace Analytics not set up +

+ } > - - - -

- Trace Analytics not set up -

-
- -
- - -
+ Trace Analytics not set up + + + - -
- The indices required for trace analytics (otel-v1-apm-span-* and otel-v1-apm-service-map*) do not exist or you do not have permission to access them. -
-
-
-
- - - -
- - -
- - - + + +
+ +
+ The indices required for trace analytics (otel-v1-apm-span-* and otel-v1-apm-service-map*) do not exist or you do not have permission to access them. +
+
+
+
+ + + +
+ + +
+ + - - - -
- - -
- - + + +
+
+
+ + +
+ + + `; exports[`Traces component renders traces page 1`] = ` - - -
- -
- + +
- -
- - - - -
- + + + +
- - - - -
-
+ + + + + + +
+
+
+
-
- -
-
-
- -
+ +
+
+ - - -
- -
- - } +
+ +
-
+ } > - - - - + - + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + id="QuickSelectPopover" + isOpen={false} + ownFocus={true} + panelPaddingSize="m" > -
- - - + + + +
-
- - - -
- } - iconType={false} - isCustom={true} - startDateControl={
} + + + +
-
} + iconType={false} + isCustom={true} + startDateControl={
} > - -
- - + + Show dates + + + +
+ + +
-
- -
-
-
- - -
-
- -
+
+
+
+
+
+
+ + - - - - - -
- -
- - -
- - + + + +
+ + + + +
+ + - -
- -
- - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={false} - panelPaddingSize="none" - withTitle={true} - > - + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={false} + panelPaddingSize="none" + withTitle={true} > -
- - - + + + + + + + +
-
- - - -
- - -
+ + +
+
+ - - - + Add filter - - } - closePopover={[Function]} - display="inlineBlock" - hasArrow={true} - isOpen={false} - ownFocus={false} - panelPaddingSize="m" - withTitle={true} - > - + + + + Add filter + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={false} + panelPaddingSize="m" + withTitle={true} > -
- - - + + + +
-
- - - - - - - - - - -
- - - + + + +
+ + + + + +
- + + + +
-
- -
- - -
- - Traces - - - (0) - -
-
-
-
-
-
- - -
- - -
-
- + + Traces + + + (0) + +
+ + +
+ +
+
- - No data matches the selected filter. Clear the filter and/or increase the time range to see more results. - - } - title={ -

- No matches -

- } + -
+ + + +
+ + + No data matches the selected filter. Clear the filter and/or increase the time range to see more results. + + } + title={ +

+ No matches +

+ } > - - - -

- No matches -

-
- -
- - -
+ No matches + + + - -
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - -
-
- -
- - -
- - +
+ + +
+ +
+ No data matches the selected filter. Clear the filter and/or increase the time range to see more results. +
+
+
+
+ + +
+ + +
+ + +
+ + + `; diff --git a/dashboards-observability/public/components/trace_analytics/components/traces/__tests__/__snapshots__/traces_table.test.tsx.snap b/dashboards-observability/public/components/trace_analytics/components/traces/__tests__/__snapshots__/traces_table.test.tsx.snap index c694215ef..7a68b18cd 100644 --- a/dashboards-observability/public/components/trace_analytics/components/traces/__tests__/__snapshots__/traces_table.test.tsx.snap +++ b/dashboards-observability/public/components/trace_analytics/components/traces/__tests__/__snapshots__/traces_table.test.tsx.snap @@ -6,6 +6,7 @@ exports[`Traces table component renders empty traces table message 1`] = ` items={Array []} loading={false} refresh={[MockFunction]} + traceIdColumnAction={[Function]} >
diff --git a/dashboards-observability/public/components/trace_analytics/components/traces/__tests__/traces.test.tsx b/dashboards-observability/public/components/trace_analytics/components/traces/__tests__/traces.test.tsx index 363d7851e..a48f54f0c 100644 --- a/dashboards-observability/public/components/trace_analytics/components/traces/__tests__/traces.test.tsx +++ b/dashboards-observability/public/components/trace_analytics/components/traces/__tests__/traces.test.tsx @@ -18,11 +18,25 @@ describe('Traces component', () => { const setFilters = jest.fn(); const setStartTime = jest.fn(); const setEndTime = jest.fn(); + const traceIdColumnAction = (item: any) => + location.assign(`#/trace_analytics/traces/${encodeURIComponent(item)}`); + const childBreadcrumbs = [ + { + text: 'Trace analytics', + href: '#/trace_analytics/home', + }, + { + text: 'Traces', + href: '#/trace_analytics/traces', + }, + ]; const wrapper = mount( { const setFilters = jest.fn(); const setStartTime = jest.fn(); const setEndTime = jest.fn(); + const traceIdColumnAction = (item: any) => + location.assign(`#/trace_analytics/traces/${encodeURIComponent(item)}`); + const childBreadcrumbs = [ + { + text: 'Trace analytics', + href: '#/trace_analytics/home', + }, + { + text: 'Traces', + href: '#/trace_analytics/traces', + }, + ]; const wrapper = mount( { it('renders empty traces table message', () => { const refresh = jest.fn(); + const traceIdColumnAction = (item: any) => + location.assign(`#/trace_analytics/traces/${encodeURIComponent(item)}`); const noIndicesTable = mount( - + ); expect(noIndicesTable).toMatchSnapshot(); const emptyTable = mount( - + ); expect(emptyTable).toMatchSnapshot(); }); @@ -37,9 +51,17 @@ describe('Traces table component', () => { actions: '#', }, ]; + const traceIdColumnAction = (item: any) => + location.assign(`#/trace_analytics/traces/${encodeURIComponent(item)}`); const refresh = jest.fn(); const wrapper = mount( - + ); expect(wrapper).toMatchSnapshot(); diff --git a/dashboards-observability/public/components/trace_analytics/components/traces/traces.tsx b/dashboards-observability/public/components/trace_analytics/components/traces/traces.tsx index 6841d068e..9fc2b262d 100644 --- a/dashboards-observability/public/components/trace_analytics/components/traces/traces.tsx +++ b/dashboards-observability/public/components/trace_analytics/components/traces/traces.tsx @@ -2,119 +2,25 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ -/* eslint-disable react-hooks/exhaustive-deps */ -import { EuiSpacer, EuiTitle, PropertySort } from '@elastic/eui'; -import React, { useEffect, useState } from 'react'; +import { EuiBreadcrumb, EuiTitle } from '@elastic/eui'; +import React from 'react'; import { TraceAnalyticsComponentDeps } from '../../home'; -import { handleTracesRequest } from '../../requests/traces_request_handler'; -import { getValidFilterFields } from '../common/filters/filter_helpers'; -import { filtersToDsl } from '../common/helper_functions'; -import { SearchBar } from '../common/search_bar'; -import { TracesTable } from './traces_table'; +import { TracesContent } from './traces_content'; -interface TracesProps extends TraceAnalyticsComponentDeps { - appId?: string; - appName?: string; +export interface TracesProps extends TraceAnalyticsComponentDeps { page: 'traces' | 'app'; - openTraceFlyout?: (traceId: string) => void; - switchToEditViz?: any; + childBreadcrumbs: EuiBreadcrumb[]; + traceIdColumnAction: any; } export function Traces(props: TracesProps) { - const { appId, appName, parentBreadcrumbs, page, openTraceFlyout, switchToEditViz } = props; - const [tableItems, setTableItems] = useState([]); - const [redirect, setRedirect] = useState(true); - const [loading, setLoading] = useState(false); - const appTraces = page === 'app'; - - const breadCrumbs = appTraces - ? [ - { - text: 'Application analytics', - href: '#/application_analytics', - }, - { - text: `${appName}`, - href: `#/application_analytics/${appId}`, - }, - ] - : [ - { - text: 'Trace analytics', - href: '#/trace_analytics/home', - }, - { - text: 'Traces', - href: '#/trace_analytics/traces', - }, - ]; - - useEffect(() => { - props.chrome.setBreadcrumbs([...parentBreadcrumbs, ...breadCrumbs]); - const validFilters = getValidFilterFields('traces'); - props.setFilters([ - ...props.filters.map((filter) => ({ - ...filter, - locked: validFilters.indexOf(filter.field) === -1, - })), - ]); - setRedirect(false); - if (appTraces) { - switchToEditViz(''); - } - }, []); - - useEffect(() => { - if (!redirect && props.indicesExist) refresh(); - }, [props.filters, props.appConfigs]); - - const refresh = async (sort?: PropertySort) => { - setLoading(true); - const DSL = filtersToDsl( - props.filters, - props.query, - props.startTime, - props.endTime, - props.page, - appTraces ? props.appConfigs : [] - ); - const timeFilterDSL = filtersToDsl([], '', props.startTime, props.endTime, props.page); - await handleTracesRequest(props.http, DSL, timeFilterDSL, tableItems, setTableItems, sort); - setLoading(false); - }; - return ( <> - {appTraces ? ( - - ) : ( - -

Traces

-
- )} - - - + +

Traces

+
+ ); } diff --git a/dashboards-observability/public/components/trace_analytics/components/traces/traces_content.tsx b/dashboards-observability/public/components/trace_analytics/components/traces/traces_content.tsx new file mode 100644 index 000000000..b71a24334 --- /dev/null +++ b/dashboards-observability/public/components/trace_analytics/components/traces/traces_content.tsx @@ -0,0 +1,88 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +/* eslint-disable react-hooks/exhaustive-deps */ + +import { EuiSpacer, PropertySort } from '@elastic/eui'; +import React, { useEffect, useState } from 'react'; +import { handleTracesRequest } from '../../requests/traces_request_handler'; +import { getValidFilterFields } from '../common/filters/filter_helpers'; +import { filtersToDsl } from '../common/helper_functions'; +import { SearchBar } from '../common/search_bar'; +import { TracesProps } from './traces'; +import { TracesTable } from './traces_table'; + +export function TracesContent(props: TracesProps) { + const { + page, + http, + chrome, + query, + filters, + appConfigs, + startTime, + endTime, + indicesExist, + parentBreadcrumbs, + childBreadcrumbs, + traceIdColumnAction, + setQuery, + setFilters, + setStartTime, + setEndTime, + } = props; + const [tableItems, setTableItems] = useState([]); + const [redirect, setRedirect] = useState(true); + const [loading, setLoading] = useState(false); + + useEffect(() => { + chrome.setBreadcrumbs([...parentBreadcrumbs, ...childBreadcrumbs]); + const validFilters = getValidFilterFields('traces'); + setFilters([ + ...filters.map((filter) => ({ + ...filter, + locked: validFilters.indexOf(filter.field) === -1, + })), + ]); + setRedirect(false); + }, []); + + useEffect(() => { + if (!redirect && indicesExist) refresh(); + }, [filters, appConfigs]); + + const refresh = async (sort?: PropertySort) => { + setLoading(true); + const DSL = filtersToDsl(filters, query, startTime, endTime, page, appConfigs); + const timeFilterDSL = filtersToDsl([], '', startTime, endTime, page); + await handleTracesRequest(http, DSL, timeFilterDSL, tableItems, setTableItems, sort); + setLoading(false); + }; + + return ( + <> + + + + + ); +} diff --git a/dashboards-observability/public/components/trace_analytics/components/traces/traces_table.tsx b/dashboards-observability/public/components/trace_analytics/components/traces/traces_table.tsx index 535bb4875..40f139606 100644 --- a/dashboards-observability/public/components/trace_analytics/components/traces/traces_table.tsx +++ b/dashboards-observability/public/components/trace_analytics/components/traces/traces_table.tsx @@ -27,14 +27,16 @@ import { PanelTitle, } from '../common/helper_functions'; -export function TracesTable(props: { +interface TracesTableProps { items: any[]; refresh: (sort?: PropertySort) => void; indicesExist: boolean; loading: boolean; - page?: string; - openTraceFlyout?: any; -}) { + traceIdColumnAction: any; +} + +export function TracesTable(props: TracesTableProps) { + const { items, refresh, indicesExist, loading, traceIdColumnAction } = props; const renderTitleBar = (totalItems?: number) => { return ( @@ -57,23 +59,13 @@ export function TracesTable(props: { render: (item) => ( - {props.page === 'app' ? ( - props.openTraceFlyout(item)}> - {item.length < 24 ? ( - item - ) : ( -
{_.truncate(item, { length: 24 })}
- )} -
- ) : ( - - {item.length < 24 ? ( - item - ) : ( -
{_.truncate(item, { length: 24 })}
- )} -
- )} + traceIdColumnAction(item)}> + {item.length < 24 ? ( + item + ) : ( +
{_.truncate(item, { length: 24 })}
+ )} +
@@ -155,10 +147,10 @@ export function TracesTable(props: { render: (item) => (item === 0 || item ? item : '-'), }, ] as Array>, - [props.items] + [items] ); - const titleBar = useMemo(() => renderTitleBar(props.items?.length), [props.items]); + const titleBar = useMemo(() => renderTitleBar(items?.length), [items]); const [sorting, setSorting] = useState<{ sort: PropertySort }>({ sort: { @@ -167,31 +159,32 @@ export function TracesTable(props: { }, }); - const onTableChange = async ({ page, sort }: { page: any; sort: any }) => { + const onTableChange = async ({ currPage, sort }: { currPage: any; sort: any }) => { if (typeof sort?.field !== 'string') return; // maps table column key to DSL aggregation name - const field = { + const fieldMappings = { trace_id: '_key', trace_group: null, latency: 'latency', percentile_in_trace_group: null, error_count: 'error_count', last_updated: 'last_updated', - }[sort.field]; - if (!field || props.items?.length < TRACES_MAX_NUM) { + }; + const field = fieldMappings[sort.field as keyof typeof fieldMappings]; + if (!field || items?.length < TRACES_MAX_NUM) { setSorting({ sort }); return; } // using await when sorting the default sorted field leads to a bug in UI if (sort.field === 'trace_id') { - props.refresh({ ...sort, field }); + refresh({ ...sort, field }); setSorting({ sort }); return; } - await props.refresh({ ...sort, field }); + await refresh({ ...sort, field }); setSorting({ sort }); }; @@ -201,10 +194,10 @@ export function TracesTable(props: { {titleBar} - {props.items?.length > 0 ? ( + {items?.length > 0 ? ( - ) : props.indicesExist ? ( + ) : indicesExist ? ( ) : ( diff --git a/dashboards-observability/public/components/trace_analytics/home.tsx b/dashboards-observability/public/components/trace_analytics/home.tsx index 7ec82aac2..ab0487436 100644 --- a/dashboards-observability/public/components/trace_analytics/home.tsx +++ b/dashboards-observability/public/components/trace_analytics/home.tsx @@ -73,7 +73,7 @@ export const Home = (props: HomeProps) => { href: '#/trace_analytics/home', }, { - text: 'Dashboards', + text: 'Dashboard', href: '#/trace_analytics/home', }, ]; @@ -89,11 +89,25 @@ export const Home = (props: HomeProps) => { }, ]; + const traceBreadcrumbs = [ + { + text: 'Trace analytics', + href: '#/trace_analytics/home', + }, + { + text: 'Traces', + href: '#/trace_analytics/traces', + }, + ]; + const nameColumnAction = (item: any) => location.assign(`#/trace_analytics/services/${encodeURIComponent(item)}`); const traceColumnAction = () => location.assign('#/trace_analytics/traces'); + const traceIdColumnAction = (item: any) => + location.assign(`#/trace_analytics/traces/${encodeURIComponent(item)}`); + const commonProps: TraceAnalyticsComponentDeps = { parentBreadcrumbs: props.parentBreadcrumbs, http: props.http, @@ -126,7 +140,12 @@ export const Home = (props: HomeProps) => { path="/trace_analytics/traces" render={(routerProps) => ( - + )} />