From ee141c4765c342de5acfb9e5e76f18108d977bcb Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Mon, 3 Feb 2025 20:44:07 +0000 Subject: [PATCH] [Manual Backport 2.x] [Bug] Add loading status to all pages in traces and services pages (#2336) * [Bug] Add loading status to all pages in traces and services pages (#2325) * add loading status to trace content Signed-off-by: Shenoy Pratik * add loading status to trace view and services pages Signed-off-by: Shenoy Pratik * update unit tests and remove console.log Signed-off-by: Shenoy Pratik * resolve comments Signed-off-by: Shenoy Pratik * update cypress tests Signed-off-by: Shenoy Pratik * add loader for span flyout and fix trace tests Signed-off-by: Shenoy Pratik --------- Signed-off-by: Shenoy Pratik Co-authored-by: Adam Tackett <105462877+TackAdam@users.noreply.github.com> (cherry picked from commit ca79a8f092301c7ae6cf36cd17dda2ca242a910d) * add app analytic flyout snapshot Signed-off-by: Shenoy Pratik --------- Signed-off-by: Shenoy Pratik (cherry picked from commit 036a4f9ce86c2f55629ae3980625837e2836359f) Signed-off-by: github-actions[bot] --- .../app_analytics_test/app_analytics.spec.js | 186 +- .../trace_analytics_dashboard.spec.js | 70 +- .../trace_analytics_services.spec.js | 14 +- .../trace_analytics_traces.spec.js | 23 +- .../__snapshots__/create.test.tsx.snap | 8646 ++++++++++++++--- .../__snapshots__/flyout.test.tsx.snap | 401 +- .../service_config.test.tsx.snap | 268 +- .../__snapshots__/trace_config.test.tsx.snap | 3309 ++++++- .../config_components/service_config.tsx | 7 +- .../service_detail_flyout.tsx | 13 +- .../common/plots/error_rate_plt.tsx | 14 +- .../common/plots/latency_trend_plt.tsx | 24 +- .../components/common/plots/service_map.tsx | 11 +- .../common/plots/throughput_plt.tsx | 21 +- .../shared_components/custom_datagrid.tsx | 15 +- .../__snapshots__/dashboard.test.tsx.snap | 5001 ++++++++-- .../dashboard/dashboard_content.tsx | 84 +- .../components/dashboard/dashboard_table.tsx | 4 +- .../dashboard/top_error_rates_table.tsx | 5 +- .../components/dashboard/top_groups_page.tsx | 8 +- .../dashboard/top_latency_table.tsx | 5 +- .../__snapshots__/service_view.test.tsx.snap | 1 + .../__snapshots__/services.test.tsx.snap | 4186 ++++++-- .../components/services/service_metrics.tsx | 14 +- .../components/services/service_view.tsx | 197 +- .../components/services/services_content.tsx | 42 +- .../components/services/services_table.tsx | 4 +- .../span_detail_flyout.test.tsx.snap | 261 +- .../span_detail_panel.test.tsx.snap | 1 + .../span_detail_table.test.tsx.snap | 955 +- .../__snapshots__/trace_view.test.tsx.snap | 2 + .../__tests__/span_detail_panel.test.tsx | 79 +- .../__tests__/span_detail_table.test.tsx | 134 +- .../traces/service_breakdown_panel.tsx | 27 +- .../components/traces/services_list.tsx | 3 + .../components/traces/span_detail_flyout.tsx | 46 +- .../components/traces/span_detail_panel.tsx | 55 +- .../components/traces/span_detail_table.tsx | 35 +- .../components/traces/trace_view.tsx | 223 +- .../components/traces/traces_content.tsx | 82 +- public/components/trace_analytics/index.scss | 10 +- .../requests/services_request_handler.ts | 4 +- .../requests/traces_request_handler.ts | 18 +- 43 files changed, 19690 insertions(+), 4818 deletions(-) diff --git a/.cypress/integration/app_analytics_test/app_analytics.spec.js b/.cypress/integration/app_analytics_test/app_analytics.spec.js index 9d847e1bd8..5fd11999c6 100644 --- a/.cypress/integration/app_analytics_test/app_analytics.spec.js +++ b/.cypress/integration/app_analytics_test/app_analytics.spec.js @@ -32,10 +32,10 @@ import { newName, TYPING_DELAY, timeoutDelay, - loadAllData + loadAllData, } from '../../utils/app_constants'; -suppressResizeObserverIssue();//needs to be in file once +suppressResizeObserverIssue(); //needs to be in file once describe('Creating application', () => { before(() => { @@ -45,7 +45,7 @@ describe('Creating application', () => { beforeEach(() => { moveToCreatePage(); }); - + it('Suggests correct autocompletion', () => { cy.get('[data-test-subj="createPageTitle"').should('contain', 'Create application'); cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); @@ -68,21 +68,33 @@ describe('Creating application', () => { cy.focused().type('opensearch'); cy.get('[data-test-subj="searchAutocompleteTextArea"]').click(); cy.get('.aa-Item').contains('opensearch_dashboards_sample_data_flights').click(); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').should('contain', 'source = opensearch_dashboards_sample_data_flights '); + cy.get('[data-test-subj="searchAutocompleteTextArea"]').should( + 'contain', + 'source = opensearch_dashboards_sample_data_flights ' + ); cy.focused().type('{downArrow}'); cy.focused().type('{enter}'); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').should('contain', 'source = opensearch_dashboards_sample_data_flights, '); + cy.get('[data-test-subj="searchAutocompleteTextArea"]').should( + 'contain', + 'source = opensearch_dashboards_sample_data_flights, ' + ); cy.focused().type('opensearch'); cy.get('[data-test-subj="searchAutocompleteTextArea"]').click(); cy.get('.aa-Item').contains('opensearch_dashboards_sample_data_logs').click(); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').should('contain', 'source = opensearch_dashboards_sample_data_flights,opensearch_dashboards_sample_data_logs '); + cy.get('[data-test-subj="searchAutocompleteTextArea"]').should( + 'contain', + 'source = opensearch_dashboards_sample_data_flights,opensearch_dashboards_sample_data_logs ' + ); }); it('Creates an application and redirects to application', () => { expectMessageOnHover('createButton', 'Name is required.'); cy.get('[data-test-subj="nameFormRow"]').type(nameOne); cy.get('[data-test-subj="descriptionFormRow"]').type('This application is for testing.'); - expectMessageOnHover('createButton', 'Provide at least one log source, service, entity or trace group.'); + expectMessageOnHover( + 'createButton', + 'Provide at least one log source, service, entity or trace group.' + ); cy.get('[data-test-subj="servicesEntitiesAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="servicesEntitiesComboBox"]').click(); cy.focused().type('{downArrow}'); @@ -92,7 +104,9 @@ describe('Creating application', () => { cy.get('[data-test-subj="createButton"]').should('not.be.disabled'); cy.get('[data-test-subj="createAndSetButton"]').should('be.disabled'); expectMessageOnHover('createAndSetButton', 'Log source is required to set availability.'); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').click().type(' ' + baseQuery); + cy.get('[data-test-subj="searchAutocompleteTextArea"]') + .click() + .type(' ' + baseQuery); cy.get('[data-test-subj="traceGroupsAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="traceGroupsComboBox"]').scrollIntoView().type('http'); cy.get('.euiFilterSelectItem').contains(trace_one).trigger('mouseover').click(); @@ -116,8 +130,7 @@ describe('Creating application', () => { cy.get('[data-test-subj="descriptionFormRow"]').type(description); cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').click(); - cy - .get('[data-test-subj="searchAutocompleteTextArea"]') + cy.get('[data-test-subj="searchAutocompleteTextArea"]') .click() .type(' ' + baseQuery); cy.get('[data-test-subj="servicesEntitiesAccordion"]').trigger('mouseover').click(); @@ -132,7 +145,9 @@ describe('Creating application', () => { cy.get('[data-test-subj="traceGroupsCountBadge"]').should('contain', '2'); cy.reload(); cy.get('[data-test-subj="nameFormRow"]').find('.euiFieldText').should('contain.value', nameOne); - cy.get('[data-test-subj="descriptionFormRow"]').find('.euiFieldText').should('contain.value', description); + cy.get('[data-test-subj="descriptionFormRow"]') + .find('.euiFieldText') + .should('contain.value', description); cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').should('contain.value', baseQuery); cy.get('[data-test-subj="servicesEntitiesCountBadge"]').should('contain', '1'); @@ -142,7 +157,9 @@ describe('Creating application', () => { it('Shows clear modals before clearing', () => { cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="clearLogSourceButton"]').should('be.disabled'); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').click().type(' ' + baseQuery); + cy.get('[data-test-subj="searchAutocompleteTextArea"]') + .click() + .type(' ' + baseQuery); cy.get('[data-test-subj="clearLogSourceButton"]').click(); cy.get('.euiButton--danger').contains('Clear').click(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').should('contain.value', ''); @@ -167,7 +184,9 @@ describe('Creating application', () => { it('Saves time range for each application', () => { cy.get('[data-test-subj="nameFormRow"]').type(nameTwo); cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').click().type(' ' +baseQuery); + cy.get('[data-test-subj="searchAutocompleteTextArea"]') + .click() + .type(' ' + baseQuery); cy.get('[data-test-subj="createButton"]').should('not.be.disabled'); cy.get('[data-test-subj="createButton"]').click(); cy.get('.euiTableRow').should('have.length.lessThan', 1); @@ -200,7 +219,7 @@ describe('Setting availability', () => { cy.get('[data-test-subj="nameFormRow"]').type(nameThree); cy.get('[data-test-subj="logSourceAccordion"]').trigger('mouseover').click(); cy.get('[data-test-subj="searchAutocompleteTextArea"]').click(); - cy.focused().type(' source = ',); + cy.focused().type(' source = '); cy.focused().type('{enter}'); cy.get('[data-test-subj="createAndSetButton"]').click({ force: true }); cy.get('.euiTableRow').should('have.length.lessThan', 1); @@ -211,7 +230,10 @@ describe('Setting availability', () => { cy.get('[data-test-subj="setAvailabilityHomePageLink"]').first().click(); cy.get('[data-test-subj="applicationTitle"]').should('contain', nameThree); cy.get('.euiTab-isSelected[id="app-analytics-log"]').should('exist', { timeout: timeoutDelay }); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').should('contain.value', availability_default); + cy.get('[data-test-subj="searchAutocompleteTextArea"]').should( + 'contain.value', + availability_default + ); cy.get('[id="explorerPlotComponent"]').should('exist'); cy.get('.euiTab-isSelected[id="availability-panel"]').should('exist'); cy.get('.euiBreadcrumb[href="#/"]').click(); @@ -220,7 +242,10 @@ describe('Setting availability', () => { cy.get('[data-test-subj="app-analytics-configTab"]').click(); cy.get('[data-test-subj="setAvailabilityConfigLink"]').click(); cy.get('.euiTab-isSelected[id="app-analytics-log"]').should('exist', { timeout: timeoutDelay }); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').should('contain.value', availability_default); + cy.get('[data-test-subj="searchAutocompleteTextArea"]').should( + 'contain.value', + availability_default + ); cy.get('[id="explorerPlotComponent"]').should('exist'); cy.get('.euiTab-isSelected[id="availability-panel"]').should('exist'); }); @@ -257,15 +282,19 @@ describe('Viewing application', () => { changeTimeTo24('years'); cy.get('[data-test-subj="app-analytics-traceTab"]').click(); cy.get('[data-test-subj="trace-groups-service-operation-accordian"]').click(); - cy.get('[data-test-subj="dashboardTable"]').first().within(($table) => { - cy.get('.plot-container').should('have.length.at.least', 1); - }) + cy.get('[data-test-subj="dashboardTable"]') + .first() + .within(($table) => { + cy.get('.plot-container').should('have.length.at.least', 1); + }); }); it('Adds filter when Trace group name is clicked', () => { cy.get('[data-test-subj="app-analytics-traceTab"]').click(); cy.get('[data-test-subj="trace-groups-service-operation-accordian"]').click(); - cy.get('[data-test-subj="dashboard-table-trace-group-name-button"]').contains('client_create_order').click(); + cy.get('[data-test-subj="dashboard-table-trace-group-name-button"]') + .contains('client_create_order') + .click(); cy.get('[data-test-subj="client_create_orderFilterBadge"]').should('exist'); cy.get('[data-test-subj="filterBadge"]').eq(0).click(); cy.get('[data-test-subj="deleteFilterIcon"]').click(); @@ -279,8 +308,8 @@ describe('Viewing application', () => { cy.get('[data-test-subj="Number of connected servicesDescriptionList"]').should('contain', '3'); cy.get('[data-text="Errors"]').eq(1).click(); // Selecting errors tab within flyout cy.get('.ytitle').contains('Error rate').should('exist'); - cy.get('[data-test-subj="dataGridRowCell"]').eq(0).click({force: true}); // absolutely doesn't click no matter what unless theres a double click - cy.get('button[data-test-subj="spanId-link"]').eq(0).click({force: true}); + cy.get('[data-test-subj="dataGridRowCell"]').eq(0).click({ force: true }); // absolutely doesn't click no matter what unless theres a double click + cy.get('button[data-test-subj="spanId-link"]').eq(0).click({ force: true }); cy.get('[data-test-subj="spanDetailFlyout"]').contains('Span detail').should('be.visible'); cy.get('[data-test-subj="ServiceDescriptionList"]').should('contain', 'authentication'); cy.get('[data-test-subj="euiFlyoutCloseButton"]').click(); @@ -297,9 +326,11 @@ describe('Viewing application', () => { }); cy.get('[data-test-subj="euiFlyoutCloseButton"]').click(); cy.get('[data-test-subj="traceDetailFlyout"]').should('not.exist'); - cy.get('[data-test-subj="superDatePickerShowDatesButton"]').click();//added to replace wait + cy.get('[data-test-subj="superDatePickerShowDatesButton"]').click(); //added to replace wait cy.get('[title="03f9c770db5ee2f1caac0afc36db49ba"]').click(); + cy.get('.panel-title-count').contains('(11)').should('exist'); cy.get('[data-text="Span list"]').click(); + cy.get('[data-test-subj="dataGridRowCell"]').contains('d67c5bb617ba9203').should('exist'); cy.get('[data-test-subj="dataGridRowCell"]').contains('d67c5bb617ba9203').click(); cy.get('[data-test-subj="spanDetailFlyout"]').should('be.visible'); cy.get('[data-test-subj="euiFlyoutCloseButton"]').click(); @@ -327,7 +358,9 @@ describe('Viewing application', () => { cy.get('[data-test-subj="app-analytics-logTab"]').click(); cy.get('.euiBadge[title="Base Query"]').should('exist'); cy.get('.euiBadge[title="Base Query"]').trigger('mouseover'); - cy.get('.euiToolTipPopover').contains('source = opensearch_dashboards_sample_data_flights').should('exist'); + cy.get('.euiToolTipPopover') + .contains('source = opensearch_dashboards_sample_data_flights') + .should('exist'); }); it('Saves visualization #1 to panel', () => { @@ -338,7 +371,9 @@ describe('Viewing application', () => { cy.get('[data-test-subj="searchAutocompleteTextArea"]').click(); cy.get('.aa-List').find('.aa-Item').should('have.length', 11); cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus(); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').type(' ' + query_one, {delay: TYPING_DELAY}) + cy.get('[data-test-subj="searchAutocompleteTextArea"]').type(' ' + query_one, { + delay: TYPING_DELAY, + }); changeTimeTo24('months'); cy.get('[data-test-subj="main-content-visTab"]').click(); cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); @@ -355,13 +390,15 @@ describe('Viewing application', () => { cy.get('[data-test-subj="app-analytics-panelTab"]').click(); cy.get('[aria-label="actionMenuButton"]').click(); cy.get('[data-test-subj="editVizContextMenuItem"]').click(); - changeTimeTo24('months');//Ensure time is set + changeTimeTo24('months'); //Ensure time is set cy.get('[data-test-subj="superDatePickerShowDatesButton"]').should('contain', 'Last 24 months'); cy.get('[data-test-subj="superDatePickerApplyTimeButton"]').click(); cy.get('.euiTab[id="availability-panel"]').click(); cy.get('[data-test-subj="comboBoxInput"]').click(); - cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Time series').click({ force: true }); + cy.get('[data-test-subj="comboBoxOptionsList "] button span') + .contains('Time series') + .click({ force: true }); cy.focused().type('{enter}'); cy.get('[data-test-subj="addAvailabilityButton"]').click(); @@ -385,7 +422,9 @@ describe('Viewing application', () => { cy.get('.textpoint').contains('Available').should('exist'); cy.get('.euiBreadcrumb[href="#/"]').click(); cy.get('[data-test-subj="AvailableAvailabilityBadge"]').should('contain', 'Available'); - cy.get('[data-test-subj="AvailableAvailabilityBadge"][style="background-color: rgb(84, 179, 153); color: rgb(0, 0, 0);"]').should('exist'); + cy.get( + '[data-test-subj="AvailableAvailabilityBadge"][style="background-color: rgb(84, 179, 153); color: rgb(0, 0, 0);"]' + ).should('exist'); }); it('Saves visualization #2 to panel with availability level', () => { @@ -394,12 +433,16 @@ describe('Viewing application', () => { cy.get('[data-test-subj="superDatePickerApplyTimeButton"]').click(); cy.get('[id="explorerPlotComponent"]', { timeout: timeoutDelay }).should('exist'); cy.get('[data-test-subj="searchAutocompleteTextArea"]').focus(); - cy.get('[data-test-subj="searchAutocompleteTextArea"]').type(' ' + query_two, {delay: TYPING_DELAY}) + cy.get('[data-test-subj="searchAutocompleteTextArea"]').type(' ' + query_two, { + delay: TYPING_DELAY, + }); cy.get('[data-test-subj="superDatePickerApplyTimeButton"]').click(); cy.get('[data-test-subj="main-content-visTab"]').click(); cy.get('.euiTab[id="availability-panel"]').click(); cy.get('[data-test-subj="comboBoxInput"]').click(); - cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Time series').click({ force: true }); + cy.get('[data-test-subj="comboBoxOptionsList "] button span') + .contains('Time series') + .click({ force: true }); cy.get('[data-test-subj="addAvailabilityButton"]').click(); cy.get('[data-test-subj="euiColorPickerAnchor"]').click(); cy.get('[aria-label="Select #9170B8 as the color"]').click(); @@ -421,7 +464,9 @@ describe('Viewing application', () => { cy.get('[data-test-subj="app-analytics-panelTab"]').click(); cy.get('[id="explorerPlotComponent"]').should('have.length', 2); moveToHomePage(); - cy.get('[data-test-subj="SuperAvailabilityBadge"][style="background-color: rgb(145, 112, 184); color: rgb(0, 0, 0);"]').should('contain', 'Super'); + cy.get( + '[data-test-subj="SuperAvailabilityBadge"][style="background-color: rgb(145, 112, 184); color: rgb(0, 0, 0);"]' + ).should('contain', 'Super'); }); it('Configuration tab shows details', () => { @@ -432,10 +477,9 @@ describe('Viewing application', () => { cy.get('option').should('have.length', 2); }); - it('Changes availability visualization', () => { cy.intercept('PUT', `**/api/observability/application`).as('selectUpdate'); - cy.intercept('GET', `**/api/observability/operational_panels/panels/**`).as('loadingPanels') + cy.intercept('GET', `**/api/observability/operational_panels/panels/**`).as('loadingPanels'); cy.get('[data-test-subj="app-analytics-configTab"]').click(); cy.get('select').select(visOneName); cy.wait('@selectUpdate'); @@ -443,48 +487,49 @@ describe('Viewing application', () => { moveToHomePage(); cy.wait('@loadingPanels'); cy.reload(); - cy.get('[data-test-subj="AvailableAvailabilityBadge"][style="background-color: rgb(84, 179, 153); color: rgb(0, 0, 0);"]').should('contain', 'Available'); + cy.get( + '[data-test-subj="AvailableAvailabilityBadge"][style="background-color: rgb(84, 179, 153); color: rgb(0, 0, 0);"]' + ).should('contain', 'Available'); moveToApplication(nameOne); cy.get('[data-test-subj="app-analytics-configTab"]').click(); cy.get('select').find('option:selected').should('have.text', visOneName); - }) + }); }); describe('Separate from other plugins', () => { it('Hides application visualizations in Event Analytics', () => { - cy.visit(`${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/event_analytics`); + cy.visit( + `${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/event_analytics` + ); // When there are saved queries or visualizations there are two buttons cy.get('body').then(($body) => { if ($body.find('.euiButton').length == 2) { - cy.get('input.euiFieldSearch').type(visOneName, {delay: TYPING_DELAY}); - + cy.get('input.euiFieldSearch').type(visOneName, { delay: TYPING_DELAY }); + cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); - cy.get('input.euiFieldSearch').clear().type(visTwoName, {delay: TYPING_DELAY}); - + cy.get('input.euiFieldSearch').clear().type(visTwoName, { delay: TYPING_DELAY }); + cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); cy.get('[class="euiFormControlLayoutClearButton"]').click(); - + cy.get('[data-test-subj="tablePaginationPopoverButton"]').click(); cy.get('.euiContextMenuItem__text').contains('50 rows').click(); cy.get('.euiCheckbox__input[data-test-subj="checkboxSelectAll"]').click(); - + cy.get('[data-test-subj="eventHomeAction"]').click(); - + cy.get('[data-test-subj="eventHomeAction__delete"]').click(); - + cy.get('[data-test-subj="popoverModal__deleteButton"]').should('be.disabled'); cy.get('[data-test-subj="popoverModal__deleteTextInput"]').type('delete'); cy.get('[data-test-subj="popoverModal__deleteButton"]').should('not.be.disabled'); cy.get('[data-test-subj="popoverModal__deleteButton"]').click(); - } - }) + }); }); it('Hides application visualizations in Operational Panels', () => { - cy.visit( - `${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/` - ); + cy.visit(`${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/`); cy.get('.euiButtonContent').contains('Add samples').click(); cy.get('[data-test-subj="confirmModalConfirmButton"]', { timeout: timeoutDelay }).click(); cy.get('.euiLink').contains('[Logs] Web traffic Panel').first().click(); @@ -495,20 +540,28 @@ describe('Separate from other plugins', () => { }); it('Hides application panels in Operational Panels', () => { - cy.visit( - `${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/` - ); - cy.get('[data-test-subj="operationalPanelSearchBar"]', { timeout: timeoutDelay }).type(`${nameOne}'s Panel`, {delay: TYPING_DELAY}); + cy.visit(`${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/`); + cy.get('[data-test-subj="operationalPanelSearchBar"]', { + timeout: timeoutDelay, + }).type(`${nameOne}'s Panel`, { delay: TYPING_DELAY }); cy.get('.euiTableCellContent__text').contains('No items found').should('exist'); cy.get('.euiFormControlLayoutClearButton').click(); - cy.get('[data-test-subj="operationalPanelSearchBar"]').type('[Logs] Web traffic Panel', {delay: TYPING_DELAY}); - cy.get('.euiTableRow').first().within(($row) => { - cy.get('.euiCheckbox').click(); + cy.get('[data-test-subj="operationalPanelSearchBar"]').type('[Logs] Web traffic Panel', { + delay: TYPING_DELAY, }); + cy.get('.euiTableRow') + .first() + .within(($row) => { + cy.get('.euiCheckbox').click(); + }); cy.get('[data-test-subj="operationalPanelsActionsButton"]', { timeout: timeoutDelay }).click(); cy.get('[data-test-subj="deleteContextMenuItem"]', { timeout: timeoutDelay }).click(); - cy.get('[data-test-subj="popoverModal__deleteTextInput"]', { timeout: timeoutDelay }).type('delete'); - cy.get('[data-test-subj="popoverModal__deleteButton"]', { timeout: timeoutDelay }).should('not.be.disabled'); + cy.get('[data-test-subj="popoverModal__deleteTextInput"]', { timeout: timeoutDelay }).type( + 'delete' + ); + cy.get('[data-test-subj="popoverModal__deleteButton"]', { timeout: timeoutDelay }).should( + 'not.be.disabled' + ); cy.get('[data-test-subj="popoverModal__deleteButton"]', { timeout: timeoutDelay }).click(); }); }); @@ -541,16 +594,17 @@ describe('Editing application', () => { }); }); - describe('Application Analytics home page', () => { beforeEach(() => { moveToHomePage(); - }) + }); it('Show correct information in table', () => { cy.get(`[data-test-subj="${nameOne}ApplicationLink"]`).should('exist'); cy.get('[data-test-subj="appAnalytics__compositionColumn"]').should('contain', composition); - cy.get('[data-test-subj="AvailableAvailabilityBadge"][style="background-color: rgb(84, 179, 153); color: rgb(0, 0, 0);"]').should('contain', 'Available') + cy.get( + '[data-test-subj="AvailableAvailabilityBadge"][style="background-color: rgb(84, 179, 153); color: rgb(0, 0, 0);"]' + ).should('contain', 'Available'); }); it('Renames application', () => { @@ -558,9 +612,11 @@ describe('Application Analytics home page', () => { cy.get('input[type="text"]').clear().click().type(newName); cy.get('.euiButton__text').contains('Rename').click(); cy.get('.euiToast').contains(`Application successfully renamed to "${newName}"`); - cy.get('.euiTableRow').first().within(($row) => { - cy.get('.euiLink').contains(newName).should('exist'); - }); + cy.get('.euiTableRow') + .first() + .within(($row) => { + cy.get('.euiLink').contains(newName).should('exist'); + }); }); it('Deletes application', () => { @@ -577,7 +633,7 @@ describe('Application Analytics home page', () => { cy.get('[data-test-subj="deleteApplication"]').click(); cy.get('[data-test-subj="popoverModal__deleteTextInput"]').type('delete'); cy.get('[data-test-subj="popoverModal__deleteButton"').click(); - cy.get('.euiFieldSearch').clear() + cy.get('.euiFieldSearch').clear(); cy.get('[data-test-subj="applicationHomePageTitle"]').contains('(0)'); }); }); diff --git a/.cypress/integration/trace_analytics_test/trace_analytics_dashboard.spec.js b/.cypress/integration/trace_analytics_test/trace_analytics_dashboard.spec.js index 579162d799..634188cd6c 100644 --- a/.cypress/integration/trace_analytics_test/trace_analytics_dashboard.spec.js +++ b/.cypress/integration/trace_analytics_test/trace_analytics_dashboard.spec.js @@ -8,7 +8,7 @@ import { testDataSet, setTimeFilter, jaegerTestDataSet } from '../../utils/constants'; import { suppressResizeObserverIssue } from '../../utils/constants'; -suppressResizeObserverIssue();//needs to be in file once +suppressResizeObserverIssue(); //needs to be in file once describe('Dump test data', () => { it('Indexes test data', () => { @@ -102,8 +102,8 @@ describe('Testing dashboard table', () => { }); it('Adds the percentile filters', () => { - cy.get('[data-test-subj="dashboardTable"]').should('be.visible'); + cy.contains('client_create_order').should('exist'); cy.get('[data-test-subj="dashboard-table-percentile-button-2"]').click(); cy.contains('Latency percentile within trace group: >= 95th').should('exist'); @@ -171,7 +171,7 @@ describe('Testing plots', () => { }); }); -describe('Latency by trace group table', () =>{ +describe('Latency by trace group table', () => { beforeEach(() => { cy.visit('app/observability-traces#/', { onBeforeLoad: (win) => { @@ -191,35 +191,59 @@ describe('Latency by trace group table', () =>{ cy.get('[data-test-subj="tableHeaderCell_dashboard_error_rate_4"]').should('exist'); cy.get('[data-test-subj="tableHeaderCell_dashboard_traces_5"]').should('exist'); cy.get('[data-test-subj="tablePaginationPopoverButton"]').eq(1).click(); - cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiContextMenu__icon').eq(0).should('exist').click(); + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiContextMenu__icon') + .eq(0) + .should('exist') + .click(); cy.get('[data-test-subj="pagination-button-next"]').eq(1).should('exist').click(); - cy.get('button[data-test-subj="dashboard-table-trace-group-name-button"]').contains('mysql').should('exist'); + cy.get('button[data-test-subj="dashboard-table-trace-group-name-button"]') + .contains('mysql') + .should('exist'); }); it('Sorts the Latency by trace group table', () => { cy.get('span[title*="Trace group name"]').click(); - cy.get('[data-test-subj="dashboard-table-trace-group-name-button"]').eq(0).contains('/**').should('exist'); + cy.get('[data-test-subj="dashboard-table-trace-group-name-button"]') + .eq(0) + .contains('/**') + .should('exist'); }); it('Verify tooltips in Latency by trace group table', () => { cy.get('.euiIcon.euiIcon--small.euiIcon--subdued.eui-alignTop').eq(0).trigger('mouseover'); - cy.contains('Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation.').should('be.visible'); + cy.contains( + 'Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation.' + ).should('be.visible'); cy.get('.euiIcon.euiIcon--small.euiIcon--subdued.eui-alignTop').eq(1).trigger('mouseover'); - cy.contains('Range of latencies for traces within a trace group in the selected time range.').should('be.visible'); + cy.contains( + 'Range of latencies for traces within a trace group in the selected time range.' + ).should('be.visible'); cy.get('.euiIcon.euiIcon--small.euiIcon--subdued.eui-alignTop').eq(2).trigger('mouseover'); - cy.contains('Average latency of traces within a trace group in the selected time range.').should('be.visible'); + cy.contains( + 'Average latency of traces within a trace group in the selected time range.' + ).should('be.visible'); cy.get('.euiIcon.euiIcon--small.euiIcon--subdued.eui-alignTop').eq(3).trigger('mouseover'); - cy.contains('24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group.').should('be.visible'); + cy.contains( + '24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group.' + ).should('be.visible'); cy.get('.euiIcon.euiIcon--small.euiIcon--subdued.eui-alignTop').eq(4).trigger('mouseover'); - cy.contains('Error rate based on count of trace errors within a trace group in the selected time range.').should('be.visible'); + cy.contains( + 'Error rate based on count of trace errors within a trace group in the selected time range.' + ).should('be.visible'); cy.get('.euiIcon.euiIcon--small.euiIcon--subdued.eui-alignTop').eq(5).trigger('mouseover'); - cy.contains('Count of traces with unique trace identifiers in the selected time range.').should('be.visible'); + cy.contains('Count of traces with unique trace identifiers in the selected time range.').should( + 'be.visible' + ); }); it('Verify Search engine on Trace dashboard', () => { cy.get('.euiFieldSearch.euiFieldSearch--fullWidth').click().type('client_pay_order'); cy.get('[data-test-subj="superDatePickerApplyTimeButton"]').click(); - cy.get('.euiTableCellContent.euiTableCellContent--alignRight.euiTableCellContent--overflowingContent').contains('211.04').should('exist'); + cy.get( + '.euiTableCellContent.euiTableCellContent--alignRight.euiTableCellContent--overflowingContent' + ) + .contains('211.04') + .should('exist'); cy.get('button[data-test-subj="dashboard-table-trace-group-name-button"]').eq(0).click(); cy.get('.euiBadge.euiBadge--hollow.euiBadge--iconRight.globalFilterItem').click(); cy.get('.euiIcon.euiIcon--medium.euiContextMenu__arrow').click(); @@ -232,14 +256,16 @@ describe('Latency by trace group table', () =>{ cy.get('.euiContextMenuItem__text').eq(1).contains('Include results').click(); cy.get('.euiBadge.euiBadge--hollow.euiBadge--iconRight.globalFilterItem').click(); cy.get('.euiContextMenuItem__text').eq(2).contains('Temporarily disable').click(); - cy.get('.euiBadge.euiBadge--iconRight.globalFilterItem.globalFilterItem-isDisabled').should('exist').click(); + cy.get('.euiBadge.euiBadge--iconRight.globalFilterItem.globalFilterItem-isDisabled') + .should('exist') + .click(); cy.get('.euiContextMenuItem__text').eq(2).contains('Re-enable').click(); cy.get('.euiBadge.euiBadge--hollow.euiBadge--iconRight.globalFilterItem').click(); cy.get('.euiContextMenuItem__text').eq(3).contains('Delete').click(); }); }); -describe('Testing filters on trace analytics page', { scrollBehavior: false }, () =>{ +describe('Testing filters on trace analytics page', { scrollBehavior: false }, () => { beforeEach(() => { cy.visit('app/observability-traces#/', { onBeforeLoad: (win) => { @@ -249,7 +275,7 @@ describe('Testing filters on trace analytics page', { scrollBehavior: false }, ( setTimeFilter(); }); - it('Verify Change all filters', () =>{ + it('Verify Change all filters', () => { cy.get('[data-test-subj="global-filter-button"]').click(); cy.get('.euiContextMenuPanelTitle').contains('Change all filters').should('exist'); cy.get('.euiContextMenuItem__text').eq(1).contains('Enable all'); @@ -257,7 +283,7 @@ describe('Testing filters on trace analytics page', { scrollBehavior: false }, ( cy.get('.euiContextMenuItem__text').eq(3).contains('Invert inclusion'); cy.get('.euiContextMenuItem__text').eq(4).contains('Invert enabled/disabled'); cy.get('.euiContextMenuItem__text').eq(5).contains('Remove all'); - }) + }); it('Verify Add filter section', () => { cy.get('[data-test-subj="global-filter-button"]').click(); @@ -271,7 +297,7 @@ describe('Testing filters on trace analytics page', { scrollBehavior: false }, ( cy.get('.euiIcon.euiIcon--medium.euiContextMenu__arrow').click(); cy.get('[data-test-subj="filter-popover-cancel-button"]').contains('Cancel').click(); cy.get('.euiIcon.euiIcon--small.euiIcon--inherit.euiBadge__icon').click(); - }) + }); }); describe('Dump jaeger test data', () => { @@ -360,9 +386,9 @@ describe('Testing switch mode to jaeger', () => { cy.get('[data-test-subj="dashboard-table-traces-button"]').contains('7').click(); cy.contains(' (7)').should('exist'); - cy.get("[data-test-subj='filterBadge']").eq(0).contains('process.serviceName: redis') - cy.get("[data-test-subj='filterBadge']").eq(1).contains('operationName: GetDriver'); - }) + cy.get("[data-test-subj='filterBadge']").eq(0).contains('process.serviceName: redis'); + cy.get("[data-test-subj='filterBadge']").eq(1).contains('operationName: GetDriver'); + }); it('Switches to throughput mode and verifies columns and data', () => { cy.get("[data-test-subj='throughput-toggle']").click(); @@ -375,4 +401,4 @@ describe('Testing switch mode to jaeger', () => { cy.contains('Error rate').should('exist'); cy.contains('Traces').should('exist'); }); -}); \ No newline at end of file +}); diff --git a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js index 72031fdbe6..a39d39e734 100644 --- a/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js +++ b/.cypress/integration/trace_analytics_test/trace_analytics_services.spec.js @@ -324,17 +324,7 @@ describe('Testing traces Spans table verify table headers functionality', () => .click(); cy.get('[data-test-subj="dataGridColumnSortingPopoverColumnSelection"]').click(); cy.get('[data-test-subj="dataGridColumnSortingPopoverColumnSelection-spanId').click(); - cy.get('[data-test-subj="dataGridColumnSortingPopoverColumnSelection-parentSpanId"]').click(); - cy.get('[data-test-subj="dataGridColumnSortingPopoverColumnSelection-traceId"]').click(); - cy.get('[data-test-subj="dataGridColumnSortingPopoverColumnSelection-traceGroup').click(); - cy.get( - '[data-test-subj="dataGridColumnSortingPopoverColumnSelection-durationInNanos"]' - ).click(); - cy.get('[data-test-subj="dataGridColumnSortingPopoverColumnSelection-startTime"]').click(); - cy.get('[data-test-subj="dataGridColumnSortingPopoverColumnSelection-endTime').click(); - cy.get('[data-test-subj="dataGridColumnSortingPopoverColumnSelection-status.code"]').click(); - cy.get('[data-test-subj="dataGridColumnSortingPopoverColumnSelection"]').click(); - cy.get('[data-test-subj="dataGridColumnSortingButton"]').should('have.text', '8 fields sorted'); + cy.get('[data-test-subj="dataGridColumnSortingButton"]').should('have.text', '1 fields sorted'); cy.get('[data-test-subj="dataGridColumnSortingButton"]').should('exist').click(); }); }); @@ -371,6 +361,8 @@ describe('Testing traces Spans table and verify columns functionality', () => { .should('exist'); count_table_row(1); cy.get('[aria-label="remove current filter"]').click(); + verify_traces_spans_data_grid_cols_exists(); + cy.contains('277a5934acf55dcf').should('exist'); cy.get('.panel-title-count').contains('8').should('exist'); count_table_row(8); }); diff --git a/.cypress/integration/trace_analytics_test/trace_analytics_traces.spec.js b/.cypress/integration/trace_analytics_test/trace_analytics_traces.spec.js index e0b93930e1..b2c8faee29 100644 --- a/.cypress/integration/trace_analytics_test/trace_analytics_traces.spec.js +++ b/.cypress/integration/trace_analytics_test/trace_analytics_traces.spec.js @@ -72,7 +72,7 @@ describe('Testing trace view', () => { setTimeFilter(); cy.get('input[type="search"]').focus().type(`${TRACE_ID}`); cy.get('[data-test-subj="superDatePickerApplyTimeButton"]').click(); - cy.get('.euiTableRow').should('have.length.lessThan', 3);//Replaces wait + cy.get('.euiTableRow').should('have.length.lessThan', 3); //Replaces wait cy.get('[data-test-subj="trace-link"]').eq(0).click(); }); @@ -95,7 +95,8 @@ describe('Testing trace view', () => { cy.get('.euiTitle').contains('Logs').should('exist'); }); - it('Renders data grid, flyout and filters', () => { + it.only('Renders data grid, flyout and filters', () => { + cy.get('.panel-title-count').contains('(11)').should('exist'); cy.get('.euiButton__text[title="Span list"]').click({ force: true }); cy.contains('2 columns hidden').should('exist'); @@ -148,7 +149,7 @@ describe('Testing traces table', () => { cy.get('.euiContextMenuItem__text').contains('15 rows').click(); let expected_row_count = 15; cy.get('.euiTable--auto') - .find("tr") + .find('tr') .then((row) => { let total = row.length - 1; expect(total).to.equal(expected_row_count); @@ -174,7 +175,7 @@ describe('Testing traces tree view', () => { it('Verifies tree view and table toggle functionality with expand/collapse logic', () => { cy.get('.euiButtonGroup').contains('Tree view').click(); cy.contains('Expand all').should('exist'); - cy.contains("Collapse all").should('exist') + cy.contains('Collapse all').should('exist'); //Waiting time for render to complete cy.get("[data-test-subj='treeExpandAll']").click(); cy.get("[data-test-subj='treeCollapseAll']").click(); @@ -204,7 +205,7 @@ describe('Testing traces tree view', () => { it('Verifies tree view expand arrow functionality', () => { cy.get('.euiButtonGroup').contains('Tree view').click(); cy.contains('Expand all').should('exist'); - cy.contains("Collapse all").should('exist') + cy.contains('Collapse all').should('exist'); // Waiting time for render to complete cy.get("[data-test-subj='treeExpandAll']").click(); cy.get("[data-test-subj='treeCollapseAll']").click(); @@ -227,15 +228,13 @@ describe('Testing traces tree view', () => { it('Verifies span flyout', () => { cy.get('.euiButtonGroup').contains('Tree view').click(); cy.contains('Expand all').should('exist'); - cy.contains("Collapse all").should('exist') + cy.contains('Collapse all').should('exist'); // Waiting time for render to complete cy.get("[data-test-subj='treeExpandAll']").click(); cy.get("[data-test-subj='treeCollapseAll']").click(); // Open flyout for a span - cy.get("[data-test-subj='spanId-flyout-button']") - .contains(SPAN_ID_TREE_VIEW) - .click() + cy.get("[data-test-subj='spanId-flyout-button']").contains(SPAN_ID_TREE_VIEW).click(); cy.contains('Span detail').should('exist'); cy.contains('Span attributes').should('exist'); }); @@ -243,7 +242,7 @@ describe('Testing traces tree view', () => { it('Handles toggling between full screen and regular modes', () => { cy.get('.euiButtonGroup').contains('Tree view').click(); cy.contains('Expand all').should('exist'); - cy.contains("Collapse all").should('exist') + cy.contains('Collapse all').should('exist'); // Waiting time for render to complete cy.get("[data-test-subj='treeExpandAll']").click(); cy.get("[data-test-subj='treeCollapseAll']").click(); @@ -280,7 +279,7 @@ describe('Testing switch mode to jaeger', () => { it('Verifies Trace View', () => { cy.contains('08ee9fd9bf964384').click(); - cy.contains("Time spent by service").should('exist'); + cy.contains('Time spent by service').should('exist'); cy.get("[data-test-subj='span-gantt-chart-panel']").should('exist'); - }) + }); }); diff --git a/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap b/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap index 2785161b1a..751ffbf316 100644 --- a/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap +++ b/public/components/application_analytics/__tests__/__snapshots__/create.test.tsx.snap @@ -735,39 +735,31 @@ Object { class="euiSpacer euiSpacer--l" />
-
-

- No matches -

-
-
-
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
+
- +
+ class="euiFlexItem euiFlexItem--flexGrowZero" + > +
+
-
-

- No matches -

- -
+
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. +
+
+
+
+
+ +
+
+
+
- + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+ 24-hour duration trend + + +
+
+   +
+
+
+
+
+ + + +
+
+ + No items found + +
+
+
-
@@ -2021,39 +2383,31 @@ Object { class="euiSpacer euiSpacer--l" />
-
-

- No matches -

-
-
-
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
+
- +
+ class="euiFlexItem euiFlexItem--flexGrowZero" + > +
+
-
-

- No matches -

- -
+
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. +
+
+
+
+
+ +
+
+
+
- -
-
-
-
-
-
-
-
-
-
-
-
- -
-
- -
+
+
+
+
+
+ + + +
+
+
+ +
+
+ + +
+
+
+ - - -
-
- - - - - - -
-
-
- -
-
- - + + + + + + + + + + + +
+ 24-hour duration trend + + +
+
+   +
+
+
+
+ + + + + + + + + + + + +
+ + No items found + +
+ + + + +
+
+
+ + + + + + +
+
+
+ +
+
+ + +
+
+
+ - - -
-
- - - - - - -
-
-
- -
-
- -
+
+
+ + + + + + +
+
+
+ +
+
+ + +
+
+
+ - - -
-
- - - - - - -
-
-
- -
-
- - + + + + + + + + + + + +
+ 24-hour duration trend + + +
+
+   +
+
+
+
+ + + + + + + + + + + + +
+ + No items found + +
+ + + + +
+
+
+ + + + + + +
+
+
+ +
+
+ + +
+
+
+ - - -
-
- - - - - - -
-
-
- -
-
- -
+
+
+ + + + + + +
+
+
+ +
+
+ + +
+
+
+ - - -
-
- - - - - - -
-
-
- -
-
- - + + + + + + + + + + + +
+ 24-hour duration trend + + +
+
+   +
+
+
+
+ + + + + + + + + + + + +
+ + No items found + +
+ + + + +
+
+
+ + + + + + +
+
+
+ +
+
+ + +
+
+
+ - - -
-
- - - - - - -
-
-
- -
-
- - + + + + + + + + + + + +
+ 24-hour duration trend + + +
+
+   +
+
+
+
+ + + + + + + + + + + + +
+ + No items found + +
+ + + + +
+
+
+ + + + + + +
+
+
+ +
+
+ + +
+
+
+ - - -
-
- - - - - - -
-
-
- -
-
- -
+
+
+ + + + + + +
+
+
+ +
+
+ + +
+
+
+ - - -
-
- - - - - - -
-
-
- -
-
- - + + + + + + + + + + + +
+ 24-hour duration trend + + +
+
+   +
+
+
+
+ + + + + + + + + + + + +
+ + No items found + +
+ + + + +
+
+
+ + + + + + +
+
+
+ +
+
+ + +
+
+
+ - - -
-
- - - - - - -
-
-
- -
-
- - + + + + + + + + + + + +
+ 24-hour duration trend + + +
+
+   +
+
+
+
+ + + + + + + + + + + + +
+ + No items found + +
+ + + + +
+
+
+ + + + + + +
+
+
+ +
+
+ + +
+
+
+ - - -
-
- - - - - - -
-
-
- -
-
- -
+
+
+ + + + + + +
+
+
+ +
+
+ + +
+
+
+ - - -
-
- - - - - - -
-
-
- -
-
- - + + + + + + + + + + + +
+ 24-hour duration trend + + +
+
+   +
+
+
+
+ + + + + + + + + + + + +
+ + No items found + +
+ + + + +
+
+
+ + + + + + +
+
+
+ +
+
+ + +
+
+
+ +
+ Select one or multiple trace groups, or type a custom one +
+ + +
+
+
+
+
+ + Latency by trace group + + + (0) + +
+
+
+ +
+
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+ 24-hour duration trend + + +
+
+   +
+
+
+
+
+ + + +
- - - - - - -
- Select one or multiple trace groups, or type a custom one -
- - -
-
-
-
-
- - Latency by trace group - - - (0) - -
-
-
- -
-
-
- + + No items found + +
+
-
-
-
-
-
-
-

- No matches -

- -
-
-
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
@@ -17854,39 +22920,31 @@ Object { class="euiSpacer euiSpacer--l" />
-
-

- No matches -

-
-
-
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
+
- +
+ class="euiFlexItem euiFlexItem--flexGrowZero" + > +
+
- ■ - - >= 95 percentile -
- + ■ + + >= 95 percentile +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+
+
+
+
+ + + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+ 24-hour duration trend + + +
+
+   +
+
+
+
+
+ + + +
+
+ + No items found + +
+
-
-
-
-
-
-
-

- No matches -

- -
-
-
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
@@ -19140,39 +24568,31 @@ Object { class="euiSpacer euiSpacer--l" />
-
-

- No matches -

-
-
-
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
+
- +
+ class="euiFlexItem euiFlexItem--flexGrowZero" + > +
+
-
-

- No matches -

- -
+
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. +
+
+
+
+
+ +
+
+
+
- + + + + + + + + + + + + + + + + +
+
+ + + + + + + + + +
+ 24-hour duration trend + + +
+
+   +
+
+
+
+
+ + + +
+
+ + No items found + +
+
+
-
diff --git a/public/components/application_analytics/__tests__/__snapshots__/flyout.test.tsx.snap b/public/components/application_analytics/__tests__/__snapshots__/flyout.test.tsx.snap index 874e18e430..354ce94eee 100644 --- a/public/components/application_analytics/__tests__/__snapshots__/flyout.test.tsx.snap +++ b/public/components/application_analytics/__tests__/__snapshots__/flyout.test.tsx.snap @@ -888,6 +888,7 @@ exports[`Trace Detail Render Flyout component render trace detail 1`] = ` >
- + - + - +
@@ -1177,282 +1166,30 @@ exports[`Trace Detail Render Flyout component render trace detail 1`] = `
- -
-
- -
- - -
- - -
- - -
- - -
- - -
- + + + + + + +
diff --git a/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap b/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap index 04212cded9..276df00044 100644 --- a/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap +++ b/public/components/application_analytics/__tests__/__snapshots__/service_config.test.tsx.snap @@ -671,6 +671,7 @@ exports[`Service Config component renders empty service config 1`] = ` -
- - -
- - - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - + +
+
- - +
- - -
+ className="euiFlexItem euiFlexItem--flexGrowZero" + > + +
+ +
+
+
+
-
- - -
- - - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - + +
+
- - +
- - -
+ className="euiFlexItem euiFlexItem--flexGrowZero" + > + +
+ +
+
+
+
- + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Trace group name + + +
+
+   +
+
+ , + "render": [Function], + "sortable": true, + }, + Object { + "align": "center", + "field": "dashboard_latency_variance", + "name": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + "render": [Function], + "sortable": [Function], + "width": "300px", + }, + Object { + "align": "right", + "dataType": "number", + "field": "dashboard_average_latency", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "24_hour_latency_trend", + "name": + 24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group. + + } + delay="regular" + position="top" + > + +
+ 24-hour duration trend + + +
+
+   +
+
+
, + "render": [Function], + "sortable": false, + }, + Object { + "align": "right", + "field": "dashboard_error_rate", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "dashboard_traces", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + ] + } + data-test-subj="dashboardTable" + items={Array []} + loading={true} + onTableChange={[Function]} + pagination={ + Object { + "initialPageSize": 10, + "pageSizeOptions": Array [ + 5, + 10, + 15, + ], + } + } + responsive={true} + sorting={ + Object { + "sort": Object { + "direction": "desc", + "field": "dashboard_latency_variance", + }, + } + } + tableLayout="auto" > - -
- - - No data matches the selected filter. Clear the filter and/or increase the time range to see more results. - + + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Trace group name + + +
+
+   +
+
+ , + "render": [Function], + "sortable": true, + }, + Object { + "align": "center", + "field": "dashboard_latency_variance", + "name": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + "render": [Function], + "sortable": [Function], + "width": "300px", + }, + Object { + "align": "right", + "dataType": "number", + "field": "dashboard_average_latency", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "24_hour_latency_trend", + "name": + 24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group. + + } + delay="regular" + position="top" + > + +
+ 24-hour duration trend + + +
+
+   +
+
+
, + "render": [Function], + "sortable": false, + }, + Object { + "align": "right", + "field": "dashboard_error_rate", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "dashboard_traces", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + ] + } + data-test-subj="dashboardTable" + items={Array []} + loading={true} + noItemsMessage="No items found" + onChange={[Function]} + pagination={ + Object { + "hidePerPageOptions": undefined, + "pageIndex": 0, + "pageSize": 10, + "pageSizeOptions": Array [ + 5, + 10, + 15, + ], + "totalItemCount": 0, + } } - title={ -

- No matches -

+ responsive={true} + sorting={ + Object { + "allowNeutralSort": true, + "sort": Object { + "direction": "desc", + "field": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + }, + } } + tableLayout="auto" >
- -

- No matches -

-
- - - + +
-
- - -
- -
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - +
+ + +
+ + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Trace group name + + +
+
+   +
+
+ , + "onSort": [Function], + }, + Object { + "isSortAscending": false, + "isSorted": true, + "key": "_data_s_dashboard_latency_variance_1", + "name": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_average_latency_2", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_error_rate_4", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_traces_5", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "onSort": [Function], + }, + ] + } + > +
+ + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
+
+ + + +
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + 24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group. + + } + delay="regular" + position="top" + > + +
+ 24-hour duration trend + + + + + + + + +
+
+   +
+
+
+
+
+
+
+
+
+ + + +
+
+ + No items found + +
+
+
+
-
- -
- - + +
@@ -1783,82 +3307,1607 @@ exports[`Trace Config component renders with one trace selected 1`] = ` className="euiHorizontalRule euiHorizontalRule--full" /> - + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Trace group name + + +
+
+   +
+
+ , + "render": [Function], + "sortable": true, + }, + Object { + "align": "center", + "field": "dashboard_latency_variance", + "name": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + "render": [Function], + "sortable": [Function], + "width": "300px", + }, + Object { + "align": "right", + "dataType": "number", + "field": "dashboard_average_latency", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "24_hour_latency_trend", + "name": + 24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group. + + } + delay="regular" + position="top" + > + +
+ 24-hour duration trend + + +
+
+   +
+
+
, + "render": [Function], + "sortable": false, + }, + Object { + "align": "right", + "field": "dashboard_error_rate", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "dashboard_traces", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + ] + } + data-test-subj="dashboardTable" + items={Array []} + loading={true} + onTableChange={[Function]} + pagination={ + Object { + "initialPageSize": 10, + "pageSizeOptions": Array [ + 5, + 10, + 15, + ], + } + } + responsive={true} + sorting={ + Object { + "sort": Object { + "direction": "desc", + "field": "dashboard_latency_variance", + }, + } + } + tableLayout="auto" > - -
- - - No data matches the selected filter. Clear the filter and/or increase the time range to see more results. - + + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Trace group name + + +
+
+   +
+
+ , + "render": [Function], + "sortable": true, + }, + Object { + "align": "center", + "field": "dashboard_latency_variance", + "name": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + "render": [Function], + "sortable": [Function], + "width": "300px", + }, + Object { + "align": "right", + "dataType": "number", + "field": "dashboard_average_latency", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "24_hour_latency_trend", + "name": + 24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group. + + } + delay="regular" + position="top" + > + +
+ 24-hour duration trend + + +
+
+   +
+
+
, + "render": [Function], + "sortable": false, + }, + Object { + "align": "right", + "field": "dashboard_error_rate", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "dashboard_traces", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + ] + } + data-test-subj="dashboardTable" + items={Array []} + loading={true} + noItemsMessage="No items found" + onChange={[Function]} + pagination={ + Object { + "hidePerPageOptions": undefined, + "pageIndex": 0, + "pageSize": 10, + "pageSizeOptions": Array [ + 5, + 10, + 15, + ], + "totalItemCount": 0, + } } - title={ -

- No matches -

+ responsive={true} + sorting={ + Object { + "allowNeutralSort": true, + "sort": Object { + "direction": "desc", + "field": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + }, + } } + tableLayout="auto" >
- -

- No matches -

-
- - - + +
-
- - -
- -
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - +
+ + +
+ + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Trace group name + + +
+
+   +
+
+ , + "onSort": [Function], + }, + Object { + "isSortAscending": false, + "isSorted": true, + "key": "_data_s_dashboard_latency_variance_1", + "name": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_average_latency_2", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_error_rate_4", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_traces_5", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "onSort": [Function], + }, + ] + } + > +
+ + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
+
+ + + +
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + 24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group. + + } + delay="regular" + position="top" + > + +
+ 24-hour duration trend + + + + + + + + +
+
+   +
+
+
+
+
+
+
+
+
+ + + +
+
+ + No items found + +
+
+
+
-
- -
- - + +
diff --git a/public/components/application_analytics/components/config_components/service_config.tsx b/public/components/application_analytics/components/config_components/service_config.tsx index f1dd2230e7..b220ecf511 100644 --- a/public/components/application_analytics/components/config_components/service_config.tsx +++ b/public/components/application_analytics/components/config_components/service_config.tsx @@ -47,9 +47,13 @@ export const ServiceConfig = (props: ServiceConfigProps) => { >('latency'); const [isModalVisible, setIsModalVisible] = useState(false); const [modalLayout, setModalLayout] = useState(); + const [isServicesDataLoading, setIsServicesDataLoading] = useState(false); useEffect(() => { - handleServiceMapRequest(http, dslService, mode, '', setServiceMap); + setIsServicesDataLoading(true); + handleServiceMapRequest(http, dslService, mode, '', setServiceMap).finally(() => + setIsServicesDataLoading(false) + ); }, []); useEffect(() => { @@ -181,6 +185,7 @@ export const ServiceConfig = (props: ServiceConfigProps) => { ('latency'); + const [isServicesDataLoading, setIsServicesDataLoading] = useState(false); const renderContent = useMemo(() => { if (!serviceName) return '-'; @@ -96,6 +97,7 @@ export function ServiceDetailFlyout(props: ServiceFlyoutProps) { ); - }, [serviceName, fields, serviceMap, DSL, serviceMapIdSelected]); + }, [isServicesDataLoading, serviceName, fields, serviceMap, DSL, serviceMapIdSelected]); useEffect(() => { const serviceDSL = filtersToDsl( @@ -130,8 +132,13 @@ export function ServiceDetailFlyout(props: ServiceFlyoutProps) { 'app', appConfigs ); - handleServiceViewRequest(serviceName, http, serviceDSL, setFields, mode); - handleServiceMapRequest(http, serviceDSL, mode, '', setServiceMap, serviceName); + + setIsServicesDataLoading(true); + Promise.all([ + handleServiceViewRequest(serviceName, http, serviceDSL, setFields, mode), + handleServiceMapRequest(http, serviceDSL, mode, '', setServiceMap), + ]).finally(() => setIsServicesDataLoading(false)); + const spanDSL = filtersToDsl(mode, filters, query, startTime, endTime, 'app', appConfigs); spanDSL.query.bool.filter.push({ term: { diff --git a/public/components/trace_analytics/components/common/plots/error_rate_plt.tsx b/public/components/trace_analytics/components/common/plots/error_rate_plt.tsx index 1d6f6dd879..0b81af6864 100644 --- a/public/components/trace_analytics/components/common/plots/error_rate_plt.tsx +++ b/public/components/trace_analytics/components/common/plots/error_rate_plt.tsx @@ -8,6 +8,7 @@ import { EuiButtonGroupOptionProps, EuiFlexGroup, EuiHorizontalRule, + EuiLoadingChart, EuiPanel, } from '@elastic/eui'; import moment from 'moment'; @@ -112,6 +113,7 @@ export function ErrorRatePlt(props: { setIdSelected: (mode: string) => void; idSelected: string; toggleButtons: EuiButtonGroupOptionProps[]; + isErrorRateTrendLoading: boolean; }) { const onClick = (event: any) => { if (!event?.points) return; @@ -136,8 +138,16 @@ export function ErrorRatePlt(props: { color="text" /> - - + {props.isErrorRateTrendLoading ? ( +
+ +
+ ) : ( + <> + + + + )} ); diff --git a/public/components/trace_analytics/components/common/plots/latency_trend_plt.tsx b/public/components/trace_analytics/components/common/plots/latency_trend_plt.tsx index 8ad89b52e2..999cb27199 100644 --- a/public/components/trace_analytics/components/common/plots/latency_trend_plt.tsx +++ b/public/components/trace_analytics/components/common/plots/latency_trend_plt.tsx @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { EuiFlexGroup, EuiHorizontalRule, EuiPanel } from '@elastic/eui'; +import { EuiFlexGroup, EuiHorizontalRule, EuiLoadingChart, EuiPanel } from '@elastic/eui'; import round from 'lodash/round'; import React, { useMemo } from 'react'; import { Plt } from '../../../../visualizations/plotly/plot'; @@ -100,14 +100,30 @@ export function LatencyPlt(props: { data: Plotly.Data[]; isPanel?: boolean }) { ); } -export function LatencyPltPanel(props: { data: Plotly.Data[]; isPanel?: boolean }) { +export function LatencyPltPanel(props: { + data: Plotly.Data[]; + isLatencyTrendLoading: boolean; + isPanel?: boolean; +}) { return ( - - {props.data ? : } + {props.isLatencyTrendLoading ? ( +
+ +
+ ) : ( + <> + + {props.data ? ( + + ) : ( + + )} + + )}
); } diff --git a/public/components/trace_analytics/components/common/plots/service_map.tsx b/public/components/trace_analytics/components/common/plots/service_map.tsx index a70cfd899d..73f35b76a4 100644 --- a/public/components/trace_analytics/components/common/plots/service_map.tsx +++ b/public/components/trace_analytics/components/common/plots/service_map.tsx @@ -52,6 +52,7 @@ export interface ServiceObject { export function ServiceMap({ serviceMap, + isServicesDataLoading, idSelected, setIdSelected, addFilter, @@ -66,6 +67,7 @@ export function ServiceMap({ hideSearchBar = false, }: { serviceMap: ServiceObject; + isServicesDataLoading: boolean; idSelected: 'latency' | 'error_rate' | 'throughput'; setIdSelected: (newId: 'latency' | 'error_rate' | 'throughput') => void; addFilter?: (filter: FilterType) => void; @@ -513,7 +515,7 @@ export function ServiceMap({ )} - {Object.keys(serviceMap).length > 0 ? ( + {Object.keys(serviceMap).length > 0 || isLoading || isServicesDataLoading ? (
@@ -530,7 +532,8 @@ export function ServiceMap({ }} /> )} - {isLoading && ( + + {(isLoading || isServicesDataLoading) && (
)} + {selectedNodeDetails && (
+ {(page !== 'traces' || idSelected) && ( diff --git a/public/components/trace_analytics/components/common/plots/throughput_plt.tsx b/public/components/trace_analytics/components/common/plots/throughput_plt.tsx index 77b7bd5c8f..0c1fba6e21 100644 --- a/public/components/trace_analytics/components/common/plots/throughput_plt.tsx +++ b/public/components/trace_analytics/components/common/plots/throughput_plt.tsx @@ -3,7 +3,13 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { EuiButtonGroup, EuiFlexGroup, EuiHorizontalRule, EuiPanel } from '@elastic/eui'; +import { + EuiButtonGroup, + EuiFlexGroup, + EuiHorizontalRule, + EuiLoadingChart, + EuiPanel, +} from '@elastic/eui'; import moment from 'moment'; import React, { useMemo } from 'react'; import { Plt } from '../../../../visualizations/plotly/plot'; @@ -102,6 +108,7 @@ export function ThroughputPlt(props: { setIdSelected: (mode: string) => void; idSelected: string; toggleButtons: any[]; + isThroughputTrendLoading: boolean; }) { const onClick = (event) => { if (!event?.points) return; @@ -126,8 +133,16 @@ export function ThroughputPlt(props: { color="text" /> - - + {props.isThroughputTrendLoading ? ( +
+ +
+ ) : ( + <> + + + + )} ); diff --git a/public/components/trace_analytics/components/common/shared_components/custom_datagrid.tsx b/public/components/trace_analytics/components/common/shared_components/custom_datagrid.tsx index 95f7c66204..21db35da19 100644 --- a/public/components/trace_analytics/components/common/shared_components/custom_datagrid.tsx +++ b/public/components/trace_analytics/components/common/shared_components/custom_datagrid.tsx @@ -3,16 +3,17 @@ * SPDX-License-Identifier: Apache-2.0 */ -import React, { useState, useMemo } from 'react'; import { - EuiDataGrid, EuiButtonEmpty, + EuiButtonIcon, + EuiDataGrid, EuiDataGridColumn, EuiDataGridSorting, + EuiLoadingContent, EuiOverlayMask, - EuiButtonIcon, } from '@elastic/eui'; import { i18n } from '@osd/i18n'; +import React, { useMemo, useState } from 'react'; import { NoMatchMessage } from '../helper_functions'; interface FullScreenWrapperProps { @@ -71,6 +72,7 @@ interface RenderCustomDataGridParams { noMatchMessageSize?: string; defaultHeight?: string; visibleColumns?: string[]; + isTableDataLoading?: boolean; } export const RenderCustomDataGrid: React.FC = ({ @@ -85,6 +87,7 @@ export const RenderCustomDataGrid: React.FC = ({ noMatchMessageSize = 'xl', defaultHeight = '500px', visibleColumns, + isTableDataLoading, }) => { const [localVisibleColumns, setLocalVisibleColumns] = useState( visibleColumns ?? columns.map((col) => col.id) @@ -129,7 +132,11 @@ export const RenderCustomDataGrid: React.FC = ({ [] ); - return ( + return isTableDataLoading ? ( +
+ +
+ ) : ( <> setIsFullScreen(false)}>
diff --git a/public/components/trace_analytics/components/dashboard/__tests__/__snapshots__/dashboard.test.tsx.snap b/public/components/trace_analytics/components/dashboard/__tests__/__snapshots__/dashboard.test.tsx.snap index f0ff25358c..f61c20b661 100644 --- a/public/components/trace_analytics/components/dashboard/__tests__/__snapshots__/dashboard.test.tsx.snap +++ b/public/components/trace_analytics/components/dashboard/__tests__/__snapshots__/dashboard.test.tsx.snap @@ -796,82 +796,1607 @@ exports[`Dashboard component renders dashboard 1`] = ` className="euiHorizontalRule euiHorizontalRule--full" /> - + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Trace group name + + +
+
+   +
+
+ , + "render": [Function], + "sortable": true, + }, + Object { + "align": "center", + "field": "dashboard_latency_variance", + "name": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + "render": [Function], + "sortable": [Function], + "width": "300px", + }, + Object { + "align": "right", + "dataType": "number", + "field": "dashboard_average_latency", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "24_hour_latency_trend", + "name": + 24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group. + + } + delay="regular" + position="top" + > + +
+ 24-hour duration trend + + +
+
+   +
+
+
, + "render": [Function], + "sortable": false, + }, + Object { + "align": "right", + "field": "dashboard_error_rate", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "dashboard_traces", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + ] + } + data-test-subj="dashboardTable" + items={Array []} + loading={true} + onTableChange={[Function]} + pagination={ + Object { + "initialPageSize": 10, + "pageSizeOptions": Array [ + 5, + 10, + 15, + ], + } + } + responsive={true} + sorting={ + Object { + "sort": Object { + "direction": "desc", + "field": "dashboard_latency_variance", + }, + } + } + tableLayout="auto" > - -
- - - No data matches the selected filter. Clear the filter and/or increase the time range to see more results. - + + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Trace group name + + +
+
+   +
+
+ , + "render": [Function], + "sortable": true, + }, + Object { + "align": "center", + "field": "dashboard_latency_variance", + "name": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + "render": [Function], + "sortable": [Function], + "width": "300px", + }, + Object { + "align": "right", + "dataType": "number", + "field": "dashboard_average_latency", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "24_hour_latency_trend", + "name": + 24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group. + + } + delay="regular" + position="top" + > + +
+ 24-hour duration trend + + +
+
+   +
+
+
, + "render": [Function], + "sortable": false, + }, + Object { + "align": "right", + "field": "dashboard_error_rate", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "dashboard_traces", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + ] } - title={ -

- No matches -

+ data-test-subj="dashboardTable" + items={Array []} + loading={true} + noItemsMessage="No items found" + onChange={[Function]} + pagination={ + Object { + "hidePerPageOptions": undefined, + "pageIndex": 0, + "pageSize": 10, + "pageSizeOptions": Array [ + 5, + 10, + 15, + ], + "totalItemCount": 0, + } + } + responsive={true} + sorting={ + Object { + "allowNeutralSort": true, + "sort": Object { + "direction": "desc", + "field": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + }, + } } + tableLayout="auto" >
- -

- No matches -

-
- - - + +
-
- - -
- -
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - +
+ + +
+ + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Trace group name + + +
+
+   +
+
+ , + "onSort": [Function], + }, + Object { + "isSortAscending": false, + "isSorted": true, + "key": "_data_s_dashboard_latency_variance_1", + "name": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_average_latency_2", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_error_rate_4", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_traces_5", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "onSort": [Function], + }, + ] + } + > +
+ + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
+
+ + + +
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + 24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group. + + } + delay="regular" + position="top" + > + +
+ 24-hour duration trend + + + + + + + + +
+
+   +
+
+
+
+
+
+
+
+
+ + + +
+
+ + No items found + +
+
+
+
-
- -
- - + +
@@ -901,6 +2426,7 @@ exports[`Dashboard component renders dashboard 1`] = ` className="euiFlexItem" >
- -
-
- - - -
- - - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - -
- - -
+ + - - - + + +
@@ -1078,6 +2534,7 @@ exports[`Dashboard component renders dashboard 1`] = ` className="euiFlexItem" >
- -
-
- - - - -
- - - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - -
- - -
+ + - - - + + +
@@ -2057,82 +3443,1606 @@ exports[`Dashboard component renders empty dashboard 1`] = ` className="euiHorizontalRule euiHorizontalRule--full" /> - + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Trace group name + + +
+
+   +
+
+ , + "render": [Function], + "sortable": true, + }, + Object { + "align": "center", + "field": "dashboard_latency_variance", + "name": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + "render": [Function], + "sortable": [Function], + "width": "300px", + }, + Object { + "align": "right", + "dataType": "number", + "field": "dashboard_average_latency", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "24_hour_latency_trend", + "name": + 24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group. + + } + delay="regular" + position="top" + > + +
+ 24-hour duration trend + + +
+
+   +
+
+
, + "render": [Function], + "sortable": false, + }, + Object { + "align": "right", + "field": "dashboard_error_rate", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "dashboard_traces", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + ] + } + data-test-subj="dashboardTable" + items={Array []} + loading={true} + onTableChange={[Function]} + pagination={ + Object { + "initialPageSize": 10, + "pageSizeOptions": Array [ + 5, + 10, + 15, + ], + } + } + responsive={true} + sorting={ + Object { + "sort": Object { + "direction": "desc", + "field": "dashboard_latency_variance", + }, + } + } + tableLayout="auto" > - -
- - - No data matches the selected filter. Clear the filter and/or increase the time range to see more results. - + + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Trace group name + + +
+
+   +
+
+ , + "render": [Function], + "sortable": true, + }, + Object { + "align": "center", + "field": "dashboard_latency_variance", + "name": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + "render": [Function], + "sortable": [Function], + "width": "300px", + }, + Object { + "align": "right", + "dataType": "number", + "field": "dashboard_average_latency", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "24_hour_latency_trend", + "name": + 24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group. + + } + delay="regular" + position="top" + > + +
+ 24-hour duration trend + + +
+
+   +
+
+
, + "render": [Function], + "sortable": false, + }, + Object { + "align": "right", + "field": "dashboard_error_rate", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "dashboard_traces", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + ] } - title={ -

- No matches -

+ data-test-subj="dashboardTable" + items={Array []} + loading={true} + noItemsMessage="No items found" + onChange={[Function]} + pagination={ + Object { + "hidePerPageOptions": undefined, + "pageIndex": 0, + "pageSize": 10, + "pageSizeOptions": Array [ + 5, + 10, + 15, + ], + "totalItemCount": 0, + } + } + responsive={true} + sorting={ + Object { + "allowNeutralSort": true, + "sort": Object { + "direction": "desc", + "field": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + }, + } } + tableLayout="auto" >
- -

- No matches -

-
- - - + +
-
- - -
- -
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - +
+ + +
+ + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Trace group name + + +
+
+   +
+
+ , + "onSort": [Function], + }, + Object { + "isSortAscending": false, + "isSorted": true, + "key": "_data_s_dashboard_latency_variance_1", + "name": + + Range of latencies for traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + + Duration variance (ms) + + + + + + + + , + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_average_latency_2", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_error_rate_4", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_traces_5", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "onSort": [Function], + }, + ] + } + > +
+ + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
+
+ + + +
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + + + 24 hour time series view of hourly average, hourly percentile, and hourly range of latency for traces within a trace group. + + } + delay="regular" + position="top" + > + +
+ 24-hour duration trend + + + + + + + + +
+
+   +
+
+
+
+
+
+
+
+
+ + + +
+
+ + No items found + +
+
+
+
-
- -
- - + +
@@ -2162,6 +5072,7 @@ exports[`Dashboard component renders empty dashboard 1`] = ` className="euiFlexItem" >
- -
-
- - - -
- - - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - -
- - -
+ + - - - + + +
@@ -2339,6 +5180,7 @@ exports[`Dashboard component renders empty dashboard 1`] = ` className="euiFlexItem" >
- -
-
- - - - -
- - - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - -
- - + + -
+ - - - + + + + +
@@ -3167,6 +5938,8 @@ exports[`Dashboard component renders empty jaeger dashboard 1`] = ` addFilters={[Function]} addPercentileFilter={[Function]} filters={Array []} + isErrorRateTrendLoading={true} + isThroughputTrendLoading={true} jaegerErrorRatePltItems={ Object { "fixedInterval": "1h", @@ -3196,6 +5969,7 @@ exports[`Dashboard component renders empty jaeger dashboard 1`] = `
- -
-
- - - -
- - - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - -
- - -
+ + - - - + + +
@@ -3570,6 +6274,8 @@ exports[`Dashboard component renders empty jaeger dashboard 1`] = ` addFilters={[Function]} addPercentileFilter={[Function]} filters={Array []} + isErrorRateTrendLoading={true} + isThroughputTrendLoading={true} items={Array []} jaegerErrorRatePltItems={ Object { @@ -3650,82 +6356,1087 @@ exports[`Dashboard component renders empty jaeger dashboard 1`] = ` className="euiHorizontalRule euiHorizontalRule--full" /> - + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Service and Operation Name + + +
+
+   +
+
+ , + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "dataType": "number", + "field": "dashboard_average_latency", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "dashboard_error_rate", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "render": [Function], + "sortable": false, + }, + Object { + "align": "right", + "field": "dashboard_traces", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + ] + } + data-test-subj="dashboardTable" + items={Array []} + loading={true} + onTableChange={[Function]} + pagination={ + Object { + "initialPageSize": 5, + "pageSizeOptions": Array [ + 5, + ], + } + } + responsive={true} + sorting={ + Object { + "sort": Object { + "direction": "desc", + "field": "dashboard_latency_variance", + }, + } + } + tableLayout="auto" > - -
- - - No data matches the selected filter. Clear the filter and/or increase the time range to see more results. - + + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Service and Operation Name + + +
+
+   +
+
+ , + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "dataType": "number", + "field": "dashboard_average_latency", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + Object { + "align": "right", + "field": "dashboard_error_rate", + "name": + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + +
+
+   +
+
+
, + "render": [Function], + "sortable": false, + }, + Object { + "align": "right", + "field": "dashboard_traces", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "render": [Function], + "sortable": true, + }, + ] + } + data-test-subj="dashboardTable" + items={Array []} + loading={true} + noItemsMessage="No items found" + onChange={[Function]} + pagination={ + Object { + "hidePerPageOptions": undefined, + "pageIndex": 0, + "pageSize": 5, + "pageSizeOptions": Array [ + 5, + ], + "totalItemCount": 0, + } } - title={ -

- No matches -

+ responsive={true} + sorting={ + Object { + "allowNeutralSort": true, + "sort": undefined, + } } + tableLayout="auto" >
- -

- No matches -

-
- - - + +
-
- - -
- -
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - +
+ + +
+ + Traces of all requests that share a common API and operation at the start of distributed tracing instrumentation. + + } + delay="regular" + position="top" + > + +
+ Service and Operation Name + + +
+
+   +
+
+ , + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_average_latency_1", + "name": + Average latency of traces within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Average duration (ms) + + +
+
+   +
+
+
, + "onSort": [Function], + }, + Object { + "isSortAscending": undefined, + "isSorted": false, + "key": "_data_s_dashboard_traces_3", + "name": + Count of traces with unique trace identifiers in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Traces + + +
+
+   +
+
+
, + "onSort": [Function], + }, + ] + } + > +
+ + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
+
+ + + +
+
+
+
+
+
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+ + + + + + + + + + + Error rate based on count of trace errors within a trace group in the selected time range. + + } + delay="regular" + position="top" + > + +
+ Error rate + + + + + + + + +
+
+   +
+
+
+
+
+
+
+
+
+ +
+
+ + No items found + +
+
+
+
-
- -
- - + +
diff --git a/public/components/trace_analytics/components/dashboard/dashboard_content.tsx b/public/components/trace_analytics/components/dashboard/dashboard_content.tsx index 22bac8ebf1..24a89ae9d7 100644 --- a/public/components/trace_analytics/components/dashboard/dashboard_content.tsx +++ b/public/components/trace_analytics/components/dashboard/dashboard_content.tsx @@ -64,10 +64,12 @@ export function DashboardContent(props: DashboardProps) { const [percentileMap, setPercentileMap] = useState<{ [traceGroup: string]: number[] }>({}); const [filteredService, setFilteredService] = useState(''); const [redirect, setRedirect] = useState(true); - const [loading, setLoading] = useState(false); + const [isTraceGroupTableLoading, setIsTraceGroupTableLoading] = useState(false); const [showTimeoutToast, setShowTimeoutToast] = useState(false); const { setToast } = useToast(); const isNavGroupEnabled = coreRefs?.chrome?.navGroup.getNavGroupEnabled(); + const [isErrorRateTrendLoading, setIsErrorRateTrendLoading] = useState(false); + const [isThroughputTrendLoading, setIsThroughputTrendLoading] = useState(false); useEffect(() => { if (showTimeoutToast === true && (!toasts || toasts.length === 0)) { @@ -117,7 +119,6 @@ export function DashboardContent(props: DashboardProps) { ]); const refresh = async () => { - setLoading(true); const DSL = filtersToDsl( mode, filters, @@ -150,42 +151,35 @@ export function DashboardContent(props: DashboardProps) { appConfigs ); const fixedInterval = minFixedInterval(startTime, endTime); + setIsTraceGroupTableLoading(true); if (mode === 'jaeger') { - handleJaegerDashboardRequest( - http, - DSL, - timeFilterDSL, - latencyTrendDSL, - tableItems, - setJaegerTableItems, - mode, - () => setShowTimeoutToast(true), - // () => { - // if (toasts.length === 0) { - // setToast!('Query took too long to execute.', 'danger', 'Reduce time range or filter your data. If issue persists, consider increasing your cluster size.'); - // } - // }, - dataSourceMDSId[0].id, - setPercentileMap - ).finally(() => setLoading(false)); - handleJaegerErrorDashboardRequest( - http, - DSL, - timeFilterDSL, - latencyTrendDSL, - tableItems, - setJaegerErrorTableItems, - mode, - () => setShowTimeoutToast(true), - // () => { - // if (toasts.length === 0) { - // setToast!('Query took too long to execute.', 'danger', 'Reduce time range or filter your data. If issue persists, consider increasing your cluster size.'); - // } - // }, - dataSourceMDSId[0].id, - setPercentileMap - ).finally(() => setLoading(false)); - } else if (mode === 'data_prepper' || mode === 'custom_data_prepper') { + Promise.all([ + handleJaegerDashboardRequest( + http, + DSL, + timeFilterDSL, + latencyTrendDSL, + tableItems, + setJaegerTableItems, + mode, + () => setShowTimeoutToast(true), + dataSourceMDSId[0].id, + setPercentileMap + ), + handleJaegerErrorDashboardRequest( + http, + DSL, + timeFilterDSL, + latencyTrendDSL, + tableItems, + setJaegerErrorTableItems, + mode, + () => setShowTimeoutToast(true), + dataSourceMDSId[0].id, + setPercentileMap + ), + ]).finally(() => setIsTraceGroupTableLoading(false)); + } else { handleDashboardRequest( http, DSL, @@ -197,7 +191,7 @@ export function DashboardContent(props: DashboardProps) { () => setShowTimeoutToast(true), dataSourceMDSId[0].id, setPercentileMap - ).then(() => setLoading(false)); + ).finally(() => setIsTraceGroupTableLoading(false)); // service map should not be filtered by service name (https://github.com/opensearch-project/observability/issues/442) const serviceMapDSL = _.cloneDeep(DSL); serviceMapDSL.query.bool.must = serviceMapDSL.query.bool.must.filter( @@ -205,6 +199,7 @@ export function DashboardContent(props: DashboardProps) { ); } + setIsThroughputTrendLoading(true); handleDashboardThroughputPltRequest( http, DSL, @@ -213,8 +208,9 @@ export function DashboardContent(props: DashboardProps) { setThroughputPltItems, mode, dataSourceMDSId[0].id - ); + ).finally(() => setIsThroughputTrendLoading(false)); + setIsErrorRateTrendLoading(true); handleDashboardErrorRatePltRequest( http, DSL, @@ -223,7 +219,7 @@ export function DashboardContent(props: DashboardProps) { setErrorRatePltItems, mode, dataSourceMDSId[0].id - ); + ).finally(() => setIsErrorRateTrendLoading(false)); }; const addFilter = (filter: FilterType) => { @@ -298,7 +294,7 @@ export function DashboardContent(props: DashboardProps) { addFilter={addFilter} addPercentileFilter={addPercentileFilter} setRedirect={setRedirect} - loading={loading} + loading={isTraceGroupTableLoading} page={page} /> @@ -310,6 +306,7 @@ export function DashboardContent(props: DashboardProps) { items={errorRatePltItems} setStartTime={setStartTime} setEndTime={setEndTime} + isErrorRateTrendLoading={isErrorRateTrendLoading} /> @@ -317,6 +314,7 @@ export function DashboardContent(props: DashboardProps) { items={throughputPltItems} setStartTime={setStartTime} setEndTime={setEndTime} + isThroughputTrendLoading={isThroughputTrendLoading} /> @@ -330,7 +328,9 @@ export function DashboardContent(props: DashboardProps) { addFilters={addFilters} addPercentileFilter={addPercentileFilter} setRedirect={setRedirect} - loading={loading} + loading={isTraceGroupTableLoading} + isErrorRateTrendLoading={isErrorRateTrendLoading} + isThroughputTrendLoading={isThroughputTrendLoading} page={page} throughPutItems={throughputPltItems} jaegerErrorRatePltItems={errorRatePltItems} diff --git a/public/components/trace_analytics/components/dashboard/dashboard_table.tsx b/public/components/trace_analytics/components/dashboard/dashboard_table.tsx index 4386221dcd..3b446bb6af 100644 --- a/public/components/trace_analytics/components/dashboard/dashboard_table.tsx +++ b/public/components/trace_analytics/components/dashboard/dashboard_table.tsx @@ -390,7 +390,7 @@ export function DashboardTable(props: { }, }); - const onTableChange = async ({ page, sort }: CriteriaWithPagination) => { + const onTableChange = async ({ _page, sort }: CriteriaWithPagination) => { if (typeof sort?.field !== 'string') return; setSorting({ sort } as { sort: PropertySort }); }; @@ -401,7 +401,7 @@ export function DashboardTable(props: { {titleBar} - {props.items?.length > 0 ? ( + {props.items?.length > 0 || props.loading ? ( ) => { + const onTableChange = async ({ _page, sort }: CriteriaWithPagination) => { if (typeof sort?.field !== 'string') return; setSorting({ sort } as { sort: PropertySort }); }; @@ -250,7 +249,7 @@ export function ErrorRatesTable(props: { {titleBar} - {props.items?.length > 0 ? ( + {props.items?.length > 0 || props.loading ? ( void; setEndTime: (time: string) => void; + isErrorRateTrendLoading: boolean; + isThroughputTrendLoading: boolean; }) { const toggleButtons = [ { id: 'error_rate', label: 'Errors', - 'data-test-subj': 'errors-toggle' + 'data-test-subj': 'errors-toggle', }, { id: 'throughput', label: 'Request rate', - 'data-test-subj': 'throughput-toggle' + 'data-test-subj': 'throughput-toggle', }, ]; const [idSelected, setIdSelected] = useState('error_rate'); @@ -51,6 +53,7 @@ export function TopGroupsPage(props: { setIdSelected={(mode: string) => setIdSelected(mode)} idSelected={idSelected} toggleButtons={toggleButtons} + isErrorRateTrendLoading={props.isErrorRateTrendLoading} /> setIdSelected(mode)} idSelected={idSelected} toggleButtons={toggleButtons} + isThrouputTrendLoading={props.isThroughputTrendLoading} /> ) => { + const onTableChange = async ({ _page, sort }: CriteriaWithPagination) => { if (typeof sort?.field !== 'string') return; setSorting({ sort } as { sort: PropertySort }); }; @@ -385,7 +384,7 @@ export function LatencyTable(props: { {titleBar} - {props.items?.length > 0 ? ( + {props.items?.length > 0 || props.loading ? ( - - -
- - - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - -
- - -
- - -
- - - -
- - - -
- - -
- - Service map - -
-
-
- -
- - -
- - - Select metric for service map display - -
- - -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + +
+ +
+
+ + +
+
+ + + + + + + + + + + + + + + + + + + + Actions + + + + + +
+
+ + No items found + +
+
+
+
+
+ + +
+ + + +
+ + + +
+ + +
+ + Service map + +
+
+
+ +
+ + +
+ + + Select metric for service map display + + +
+ + + + + + -
- - -
- - - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - + +
+
- - +
- - -
+ className="euiFlexItem euiFlexItem--flexGrowZero" + > + +
+ +
+
+
+
+ +
+ + + + + + + +
+
+
+
+
+ +
+ + +
+ + +
+
+ + +
+
+ +
- +
- - - - + +
+
+ +
+ +
+ +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + +
+ +
+
+ + +
+
+ + + + + + + + + + + + + + + + Actions + + + + + +
+
- - Filter services - + No items found - - - - - -
- - - + +
+
- -
- - -
- - -
-
- - -
- - - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- -
- - -
- - + +
@@ -5637,242 +7387,1206 @@ exports[`Services component renders services page 1`] = ` -
+ +
+ + +
+ + Services + + + (0) + +
+
+
+
+
+ +
+ +
+ +
+ + + + + + + +
+
+ +
+ + + +
+
+
+
+
+
+
+
+ +
+ + +
+
+ + - -
- - -
- - Services - - - (0) - -
-
-
-
-
- -
- +
+
- +
- - + + +
+ +
+ +
+ + +
+ + +
+ +
+ + + + } + closePopover={[Function]} + display="inlineBlock" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelPaddingSize="none" + > +
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + - - - - - - - -
- - + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ +
+
+ + +
+ +
+
+ + +
+
+ + + + + + + + + + + - Show 24 hour trends + + + + Actions + + + - - - - - - - - + +
+
+ + No items found + +
+
+
- -
- - -
- - -
-
- - -
- - - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- -
- - -
- - + +
@@ -5888,6 +8602,7 @@ exports[`Services component renders services page 1`] = ` currService="" filters={Array []} idSelected="latency" + isServicesDataLoading={true} mode="data_prepper" page="services" serviceMap={Object {}} @@ -6495,90 +9210,77 @@ exports[`Services component renders services page 1`] = ` className="euiSpacer euiSpacer--l" />
-
- - -
- - - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - + +
+
- - +
- - -
+ className="euiFlexItem euiFlexItem--flexGrowZero" + > + +
+ +
+
+
+
{ const [trends, setTrends] = useState({}); + const [isTrendsDataLoading, setIsTrendsDataLoading] = useState(false); const { http } = coreRefs; const serviceFilter = [ @@ -45,14 +46,15 @@ export const ServiceMetrics = ({ ]; const fetchMetrics = async () => { - await handleServiceTrendsRequest( + setIsTrendsDataLoading(true); + handleServiceTrendsRequest( http, '1h', setTrends, mode, serviceFilter, dataSourceMDSId[0].id - ); + ).finally(() => setIsTrendsDataLoading(false)); }; useEffect(() => { @@ -75,6 +77,7 @@ export const ServiceMetrics = ({ }} setStartTime={setStartTime} setEndTime={setEndTime} + isThroughputTrendLoading={isTrendsDataLoading} /> @@ -86,10 +89,15 @@ export const ServiceMetrics = ({ }} setStartTime={setStartTime} setEndTime={setEndTime} + isErrorRateTrendLoading={isTrendsDataLoading} /> - + diff --git a/public/components/trace_analytics/components/services/service_view.tsx b/public/components/trace_analytics/components/services/service_view.tsx index 9d8b04368e..a616b5e79b 100644 --- a/public/components/trace_analytics/components/services/service_view.tsx +++ b/public/components/trace_analytics/components/services/service_view.tsx @@ -17,6 +17,7 @@ import { EuiHorizontalRule, EuiI18nNumber, EuiLink, + EuiLoadingContent, EuiPage, EuiPageBody, EuiPanel, @@ -25,7 +26,7 @@ import { EuiSpacer, EuiText, } from '@elastic/eui'; -import _ from 'lodash'; +import round from 'lodash/round'; import React, { useEffect, useMemo, useState } from 'react'; import { useLocation } from 'react-router-dom'; import { DataSourceManagementPluginSetup } from '../../../../../../../src/plugins/data_source_management/public'; @@ -78,6 +79,8 @@ export function ServiceView(props: ServiceViewProps) { const [actionsMenuPopover, setActionsMenuPopover] = useState(false); const [serviceId, setServiceId] = useState(null); const location = useLocation(); + const [isServiceOverviewLoading, setIsServiceOverviewLoading] = useState(false); + const [isServicesDataLoading, setIsServicesDataLoading] = useState(false); useEffect(() => { try { @@ -99,6 +102,8 @@ export function ServiceView(props: ServiceViewProps) { processTimeStamp(props.startTime, mode), processTimeStamp(props.endTime, mode) ); + + setIsServiceOverviewLoading(true); handleServiceViewRequest( props.serviceName, props.http, @@ -106,16 +111,17 @@ export function ServiceView(props: ServiceViewProps) { setFields, mode, props.dataSourceMDSId[0].id - ); + ).finally(() => setIsServiceOverviewLoading(false)); + if (mode === 'data_prepper' || mode === 'custom_data_prepper') { + setIsServicesDataLoading(true); handleServiceMapRequest( props.http, DSL, mode, props.dataSourceMDSId[0].id, - setServiceMap, - props.serviceName - ); + setServiceMap + ).finally(() => setIsServicesDataLoading(false)); } }; @@ -307,101 +313,113 @@ export function ServiceView(props: ServiceViewProps) { <> - - - - - - Name - - {props.serviceName || '-'} - - - {mode === 'data_prepper' || mode === 'custom_data_prepper' ? ( - - Number of connected services - - {fields.number_of_connected_services !== undefined - ? fields.number_of_connected_services - : 0} - - - ) : ( - - )} - {mode === 'data_prepper' || mode === 'custom_data_prepper' ? ( - - Connected services - - {fields.connected_services && fields.connected_services.length - ? fields.connected_services - .map((service: string) => ( - onClickConnectedService(service)} - key={service} - > - {service} - - )) - .reduce((prev: React.ReactNode, curr: React.ReactNode) => { - return [prev, ', ', curr]; - }) - : '-'} - - - ) : ( - - )} - - - - - - Average duration (ms) - - {fields.average_latency !== undefined ? fields.average_latency : '-'} - - - - Error rate - - {fields.error_rate !== undefined - ? _.round(fields.error_rate, 2).toString() + '%' - : '-'} - - - - Request rate - - {fields.throughput !== undefined ? ( - + {isServiceOverviewLoading ? ( +
+ +
+ ) : ( + <> + + + + + + Name + + {props.serviceName || '-'} + + + {mode === 'data_prepper' || mode === 'custom_data_prepper' ? ( + + Number of connected services + + {fields.number_of_connected_services !== undefined + ? fields.number_of_connected_services + : 0} + + ) : ( - '-' + )} -
-
- - Traces - - {fields.traces === 0 || fields.traces ? ( - - - + {mode === 'data_prepper' || mode === 'custom_data_prepper' ? ( + + Connected services + + {fields.connected_services && fields.connected_services.length + ? fields.connected_services + .map((service: string) => ( + onClickConnectedService(service)} + key={service} + > + {service} + + )) + .reduce((prev: React.ReactNode, curr: React.ReactNode) => { + return [prev, ', ', curr]; + }) + : '-'} + + ) : ( - '-' + )} - +
+
+ + + + Average duration (ms) + + {fields.average_latency !== undefined ? fields.average_latency : '-'} + + + + Error rate + + {fields.error_rate !== undefined + ? round(fields.error_rate, 2).toString() + '%' + : '-'} + + + + Request rate + + {fields.throughput !== undefined ? ( + + ) : ( + '-' + )} + + + + Traces + + {fields.traces === 0 || fields.traces ? ( + + + + ) : ( + '-' + )} + + +
- - - + + + )}
); }; - const overview = useMemo(() => renderOverview(), [fields, props.serviceName]); + const overview = useMemo(() => renderOverview(), [ + fields, + isServiceOverviewLoading, + props.serviceName, + ]); const title = useMemo( () => @@ -544,6 +562,7 @@ export function ServiceView(props: ServiceViewProps) { ('latency'); const [redirect, setRedirect] = useState(true); - const [loading, setLoading] = useState(false); const [filteredService, setFilteredService] = useState(''); const [selectedItems, setSelectedItems] = useState([]); const [isServiceTrendEnabled, setIsServiceTrendEnabled] = useState(false); const [serviceTrends, setServiceTrends] = useState({}); const searchBarRef = useRef<{ updateQuery: (newQuery: string) => void }>(null); + const [isServicesTableDataLoading, setIsServicesTableDataLoading] = useState(false); + const [isServicesDataLoading, setIsServicesDataLoading] = useState(false); useEffect(() => { const isNavGroupEnabled = coreRefs?.chrome?.navGroup.getNavGroupEnabled(); @@ -105,9 +106,8 @@ export function ServicesContent(props: ServicesProps) { props.dataSourceMDSId, ]); - const refresh = async (currService?: string, overrideQuery?: string) => { + const refresh = (currService?: string, overrideQuery?: string) => { const filterQuery = overrideQuery ?? query; - setLoading(true); const DSL = filtersToDsl( mode, filters, @@ -123,29 +123,18 @@ export function ServicesContent(props: ServicesProps) { (must: any) => must?.term?.serviceName == null ); - if (isServiceTrendEnabled) { - await handleServiceTrendsRequest( - http, - '1h', - setServiceTrends, - mode, - [], - dataSourceMDSId[0].id - ); - } - await Promise.all([ - handleServicesRequest(http, DSL, setTableItems, mode, dataSourceMDSId[0].id), - handleServiceMapRequest( - http, - serviceMapDSL, - mode, - dataSourceMDSId[0].id, - setServiceMap, - currService || filteredService - ), - ]); + setIsServicesTableDataLoading(true); + handleServicesRequest(http, DSL, setTableItems, mode, dataSourceMDSId[0].id).finally(() => + setIsServicesTableDataLoading(false) + ); - setLoading(false); + setIsServicesDataLoading(true); + Promise.all([ + handleServiceMapRequest(http, serviceMapDSL, mode, dataSourceMDSId[0].id, setServiceMap), + isServiceTrendEnabled + ? handleServiceTrendsRequest(http, '1h', setServiceTrends, mode, [], dataSourceMDSId[0].id) + : Promise.resolve(null), + ]).finally(() => setIsServicesDataLoading(false)); }; const addFilter = (filter: FilterType) => { @@ -225,7 +214,7 @@ export function ServicesContent(props: ServicesProps) { addFilter={addFilter} setRedirect={setRedirect} mode={mode} - loading={loading} + loading={isServicesTableDataLoading} traceColumnAction={traceColumnAction} setCurrentSelectedService={setCurrentSelectedService} jaegerIndicesExist={jaegerIndicesExist} @@ -244,6 +233,7 @@ export function ServicesContent(props: ServicesProps) { filters={filters} setFilters={setFilters} serviceMap={serviceMap} + isServicesDataLoading={isServicesDataLoading} idSelected={serviceMapIdSelected} setIdSelected={setServiceMapIdSelected} currService={filteredService} diff --git a/public/components/trace_analytics/components/services/services_table.tsx b/public/components/trace_analytics/components/services/services_table.tsx index b5bc98f7d9..e1889e4e9b 100644 --- a/public/components/trace_analytics/components/services/services_table.tsx +++ b/public/components/trace_analytics/components/services/services_table.tsx @@ -21,6 +21,7 @@ import { } from '@elastic/eui'; import { truncate } from 'lodash'; import React, { useMemo } from 'react'; +import { DataSourceOption } from '../../../../../../../src/plugins/data_source_management/public'; import { ServiceTrends, TraceAnalyticsMode } from '../../../../../common/types/trace_analytics'; import { FilterType } from '../common/filters/filters'; import { @@ -30,7 +31,6 @@ import { PanelTitle, } from '../common/helper_functions'; import { ServiceTrendsPlots } from './service_trends_plots'; -import { DataSourceOption } from '../../../../../../../src/plugins/data_source_management/public'; interface ServicesTableProps { items: any[]; @@ -281,7 +281,7 @@ export function ServicesTable(props: ServicesTableProps) { (mode === 'jaeger' && jaegerIndicesExist) ) ? ( - ) : items?.length > 0 ? ( + ) : items?.length > 0 || loading ? ( spec renders the empty component 1`] = `
- - +
+ + + + + + + + + + + + + + + + + +
@@ -295,7 +335,47 @@ exports[` spec renders the empty component 1`] = `
- - +
+ + + + + + + + + + + + + + + + + +
@@ -406,7 +486,47 @@ exports[` spec renders the empty component 1`] = `
- - +
+ + + + + + + + + + + + + + + + + +
@@ -517,7 +637,47 @@ exports[` spec renders the empty component 1`] = `
- - +
+ + + + + + + + + + + + + + + + + +
@@ -616,7 +776,47 @@ exports[` spec renders the empty component 1`] = `
- - +
+ + + + + + + + + + + + + + + + + +
@@ -813,7 +1013,56 @@ exports[` spec renders the empty component 1`] = `
- - +
+ + + + + + + + + + + + + + + + + + + +
diff --git a/public/components/trace_analytics/components/traces/__tests__/__snapshots__/span_detail_panel.test.tsx.snap b/public/components/trace_analytics/components/traces/__tests__/__snapshots__/span_detail_panel.test.tsx.snap index f1478639c4..b5304fe4f4 100644 --- a/public/components/trace_analytics/components/traces/__tests__/__snapshots__/span_detail_panel.test.tsx.snap +++ b/public/components/trace_analytics/components/traces/__tests__/__snapshots__/span_detail_panel.test.tsx.snap @@ -42,6 +42,7 @@ exports[`SpanDetailPanel component renders correctly with default props 1`] = ` > - -
+ - - Full screen - , - ], - "showColumnSelector": true, - "showFullScreenSelector": false, - "showSortSelector": true, - } - } + - - - - -
- -
-
-
- -
- - - - - -
- - - -
- - + + - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - -
- - -
- - + className="euiLoadingContent__singleLineBackground" + /> + + + +
`; exports[`SpanDetailTable renders the jaeger component with data 1`] = `
-
-
-
-
-
-
-
-
-
-

- No matches -

+
-
-
-
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
+ + + + + + + + + + +
-
`; exports[`SpanDetailTableHierarchy renders the component with data 1`] = `
-
-
-
-
-
-
-
-
-
-

- No matches -

+
-
-
-
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
+ + + + + + + + + + +
-
`; @@ -554,396 +223,86 @@ exports[`SpanDetailTableHierarchy renders the empty component 1`] = ` mode="data_prepper" openFlyout={[Function]} > - -
+ - - Full screen - , - - Expand all - , - - Collapse all - , - ], - "showColumnSelector": true, - "showFullScreenSelector": false, - "showSortSelector": false, - } - } + - - - - -
- -
-
-
- -
- - - - - -
- - - -
- - + + - 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 data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
-
-
- - -
- - -
- - + className="euiLoadingContent__singleLineBackground" + /> + + + +
`; exports[`SpanDetailTableHierarchy renders the jaeger component with data 1`] = `
-
-
-
-
-
-
-
-
-
-

- No matches -

+
-
-
-
- No data matches the selected filter. Clear the filter and/or increase the time range to see more results. -
-
+ + + + + + + + + + +
-
`; diff --git a/public/components/trace_analytics/components/traces/__tests__/__snapshots__/trace_view.test.tsx.snap b/public/components/trace_analytics/components/traces/__tests__/__snapshots__/trace_view.test.tsx.snap index 8ad986ec45..de6abbe6c1 100644 --- a/public/components/trace_analytics/components/traces/__tests__/__snapshots__/trace_view.test.tsx.snap +++ b/public/components/trace_analytics/components/traces/__tests__/__snapshots__/trace_view.test.tsx.snap @@ -121,6 +121,7 @@ exports[`Trace view component renders trace view 1`] = ` > @@ -163,6 +164,7 @@ exports[`Trace view component renders trace view 1`] = ` { expect(wrapper).toMatchSnapshot(); }); - it('renders gantt chart and mini-map correctly', () => { + it('displays loading chart initially', () => { const wrapper = mount(); - expect(wrapper.find(Plt)).toHaveLength(2); // Gantt chart and mini-map + expect(wrapper.find('EuiLoadingChart')).toHaveLength(1); }); - it('handles zoom reset button correctly', () => { + it('renders gantt chart and mini-map correctly', async () => { const wrapper = mount(); + expect(wrapper.find('EuiLoadingChart')).toHaveLength(1); // Ensure loading state appears + + await waitFor(() => { + wrapper.update(); + expect(wrapper.find(Plt)).toHaveLength(2); // Gantt chart and mini-map appear + }); + }); + + it('handles zoom reset button correctly', async () => { + const wrapper = mount(); + + await waitFor(() => { + wrapper.update(); + expect(wrapper.find(Plt)).toHaveLength(2); // Gantt chart and mini-map appear + }); // Verify that the reset button is initially disabled let resetButton = wrapper .find(EuiSmallButton) @@ -106,43 +122,48 @@ describe('SpanDetailPanel component', () => { miniMap.simulate('click'); }); - wrapper.update(); - - // Verify that the reset button is now enabled - resetButton = wrapper - .find(EuiSmallButton) - .filterWhere((btn) => btn.text().includes('Reset zoom')); - expect(resetButton.prop('isDisabled')).toBe(false); // Should now be enabled + await waitFor(() => { + wrapper.update(); + resetButton = wrapper + .find(EuiSmallButton) + .filterWhere((btn) => btn.text().includes('Reset zoom')); + expect(resetButton.prop('isDisabled')).toBe(false); // Should now be enabled + }); // Simulate clicking the reset button act(() => { resetButton.prop('onClick')!(); }); - wrapper.update(); - - // Verify that the reset button is disabled again after reset - resetButton = wrapper - .find(EuiSmallButton) - .filterWhere((btn) => btn.text().includes('Reset zoom')); - expect(resetButton.prop('isDisabled')).toBe(true); + await waitFor(() => { + wrapper.update(); + resetButton = wrapper + .find(EuiSmallButton) + .filterWhere((btn) => btn.text().includes('Reset zoom')); + expect(resetButton.prop('isDisabled')).toBe(true); // Should reset + }); }); - it('handles user-defined zoom range via mini-map', () => { + it('handles user-defined zoom range via mini-map', async () => { const wrapper = mount(); + await waitFor(() => { + wrapper.update(); + expect(wrapper.find(Plt)).toHaveLength(2); // Gantt chart and mini-map appear + }); + // Find the mini-map and simulate click const miniMap = wrapper.find('[data-test-subj="mocked-plt"]').at(0); act(() => { miniMap.simulate('click'); }); - wrapper.update(); - - // After zooming, the reset button should be enabled - const resetButton = wrapper - .find(EuiSmallButton) - .filterWhere((btn) => btn.text().includes('Reset zoom')); - expect(resetButton.prop('isDisabled')).toBe(false); + await waitFor(() => { + wrapper.update(); + const resetButton = wrapper + .find(EuiSmallButton) + .filterWhere((btn) => btn.text().includes('Reset zoom')); + expect(resetButton.prop('isDisabled')).toBe(false); + }); }); }); diff --git a/public/components/trace_analytics/components/traces/__tests__/span_detail_table.test.tsx b/public/components/trace_analytics/components/traces/__tests__/span_detail_table.test.tsx index d2cfe70c8b..a2edecddc1 100644 --- a/public/components/trace_analytics/components/traces/__tests__/span_detail_table.test.tsx +++ b/public/components/trace_analytics/components/traces/__tests__/span_detail_table.test.tsx @@ -3,6 +3,11 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { + EuiDataGridColumnVisibility, + EuiDataGridPaginationProps, + EuiDataGridSorting, +} from '@elastic/eui'; import { waitFor } from '@testing-library/react'; import { configure, mount } from 'enzyme'; import Adapter from 'enzyme-adapter-react-16'; @@ -12,11 +17,6 @@ import { act } from 'react-dom/test-utils'; import { HttpResponse } from '../../../../../../../../src/core/public'; import { TEST_JAEGER_SPAN_RESPONSE, TEST_SPAN_RESPONSE } from '../../../../../../test/constants'; import { SpanDetailTable, SpanDetailTableHierarchy } from '../span_detail_table'; -import { - EuiDataGridPaginationProps, - EuiDataGridSorting, - EuiDataGridColumnVisibility, -} from '@elastic/eui'; jest.mock('../../../../../../test/__mocks__/httpClientMock', () => ({ post: jest.fn(), @@ -106,20 +106,28 @@ describe('SpanDetailTable', () => { /> ); - const pagination = wrapper - .find('EuiDataGrid') - .prop('pagination') as EuiDataGridPaginationProps; + expect(wrapper.find('EuiLoadingContent')).toHaveLength(1); // Ensure loading state appears await act(async () => { - pagination.onChangeItemsPerPage!(50); + await waitFor(() => { + wrapper.update(); + // Retrieve initial pagination state + const pagination = wrapper + .find('EuiDataGrid') + .prop('pagination') as EuiDataGridPaginationProps; + + expect(pagination.pageSize).not.toBe(50); // Ensure it starts with a different page size + pagination.onChangeItemsPerPage!(50); + }); }); - wrapper.update(); - - const updatedPagination = wrapper - .find('EuiDataGrid') - .prop('pagination') as EuiDataGridPaginationProps; - expect(updatedPagination.pageSize).toBe(50); + await waitFor(() => { + wrapper.update(); + const updatedPagination = wrapper + .find('EuiDataGrid') + .prop('pagination') as EuiDataGridPaginationProps; + expect(updatedPagination.pageSize).toBe(50); + }); }); it('should handle page changes', async () => { @@ -135,25 +143,30 @@ describe('SpanDetailTable', () => { /> ); - const pagination = wrapper - .find('EuiDataGrid') - .prop('pagination') as EuiDataGridPaginationProps; + expect(wrapper.find('EuiLoadingContent')).toHaveLength(1); // Ensure loading state appears await act(async () => { - pagination.onChangePage!(1); + await waitFor(() => { + wrapper.update(); + const pagination = wrapper + .find('EuiDataGrid') + .prop('pagination') as EuiDataGridPaginationProps; + pagination.onChangePage!(1); + }); }); - wrapper.update(); - - const updatedPagination = wrapper - .find('EuiDataGrid') - .prop('pagination') as EuiDataGridPaginationProps; - expect(updatedPagination.pageIndex).toBe(1); + await waitFor(() => { + wrapper.update(); + const updatedPagination = wrapper + .find('EuiDataGrid') + .prop('pagination') as EuiDataGridPaginationProps; + expect(updatedPagination.pageIndex).toBe(1); + }); }); }); describe('Column visibility', () => { - it('should hide columns specified in hiddenColumns prop', () => { + it('should hide columns specified in hiddenColumns prop', async () => { const wrapper = mount( { /> ); - const columnVisibility = wrapper - .find('EuiDataGrid') - .prop('columnVisibility') as EuiDataGridColumnVisibility; - const visibleColumns = columnVisibility.visibleColumns; - - expect(visibleColumns).not.toContain('spanId'); - expect(visibleColumns).not.toContain('startTime'); + await waitFor(() => { + wrapper.update(); + const columnVisibility = wrapper + .find('EuiDataGrid') + .prop('columnVisibility') as EuiDataGridColumnVisibility; + expect(columnVisibility.visibleColumns).not.toContain('spanId'); + expect(columnVisibility.visibleColumns).not.toContain('startTime'); + }); }); it('should update visible columns when column visibility changes', async () => { @@ -186,20 +200,23 @@ describe('SpanDetailTable', () => { const newVisibleColumns = ['spanId', 'serviceName']; - const columnVisibility = wrapper - .find('EuiDataGrid') - .prop('columnVisibility') as EuiDataGridColumnVisibility; - await act(async () => { - columnVisibility.setVisibleColumns!(newVisibleColumns); + await waitFor(() => { + wrapper.update(); + const columnVisibility = wrapper + .find('EuiDataGrid') + .prop('columnVisibility') as EuiDataGridColumnVisibility; + columnVisibility.setVisibleColumns!(newVisibleColumns); + }); }); - wrapper.update(); - - const updatedColumnVisibility = wrapper - .find('EuiDataGrid') - .prop('columnVisibility') as EuiDataGridColumnVisibility; - expect(updatedColumnVisibility.visibleColumns).toEqual(newVisibleColumns); + await waitFor(() => { + wrapper.update(); + const updatedColumnVisibility = wrapper + .find('EuiDataGrid') + .prop('columnVisibility') as EuiDataGridColumnVisibility; + expect(updatedColumnVisibility.visibleColumns).toEqual(newVisibleColumns); + }); }); }); @@ -215,23 +232,23 @@ describe('SpanDetailTable', () => { /> ); - const newSorting: Array<{ id: string; direction: 'asc' | 'desc' }> = [ - { id: 'startTime', direction: 'desc' }, - ]; - - const sorting = wrapper.find('EuiDataGrid').prop('sorting') as EuiDataGridSorting; - + const newSorting = [{ id: 'startTime', direction: 'desc' }]; await act(async () => { - sorting.onSort!(newSorting); + await waitFor(() => { + wrapper.update(); + const sorting = wrapper.find('EuiDataGrid').prop('sorting') as EuiDataGridSorting; + sorting.onSort!(newSorting); + }); }); - wrapper.update(); - - const updatedSorting = wrapper.find('EuiDataGrid').prop('sorting') as EuiDataGridSorting; - expect(updatedSorting.columns).toEqual(newSorting); + await waitFor(() => { + wrapper.update(); + const updatedSorting = wrapper.find('EuiDataGrid').prop('sorting') as EuiDataGridSorting; + expect(updatedSorting.columns).toEqual(newSorting); + }); }); - it('should disable sorting in Jaeger mode', () => { + it('should disable sorting in Jaeger mode', async () => { const wrapper = mount( { /> ); - expect(wrapper.find('EuiDataGrid').prop('sorting')).toBeUndefined(); + await waitFor(() => { + wrapper.update(); + expect(wrapper.find('EuiDataGrid').prop('sorting')).toBeUndefined(); + }); }); }); }); diff --git a/public/components/trace_analytics/components/traces/service_breakdown_panel.tsx b/public/components/trace_analytics/components/traces/service_breakdown_panel.tsx index c08a59ac0c..d90a9445e1 100644 --- a/public/components/trace_analytics/components/traces/service_breakdown_panel.tsx +++ b/public/components/trace_analytics/components/traces/service_breakdown_panel.tsx @@ -8,6 +8,7 @@ import { EuiFlexItem, EuiHealth, EuiHorizontalRule, + EuiLoadingChart, EuiPanel, EuiSpacer, EuiText, @@ -25,7 +26,7 @@ interface ServiceBreakdownData { }; } -export function ServiceBreakdownPanel(props: { data: ServiceBreakdownData[] }) { +export function ServiceBreakdownPanel(props: { data: ServiceBreakdownData[]; isLoading: boolean }) { const layout = useMemo( () => ({ @@ -81,14 +82,22 @@ export function ServiceBreakdownPanel(props: { data: ServiceBreakdownData[] }) { return ( - - - - {props.data?.length > 0 ? : null} - - {stats} - - + {props.isLoading ? ( +
+ +
+ ) : ( + <> + + + + {props.data?.length > 0 ? : null} + + {stats} + + + + )}
); } \ No newline at end of file diff --git a/public/components/trace_analytics/components/traces/services_list.tsx b/public/components/trace_analytics/components/traces/services_list.tsx index 9254ae9421..7486dbae3d 100644 --- a/public/components/trace_analytics/components/traces/services_list.tsx +++ b/public/components/trace_analytics/components/traces/services_list.tsx @@ -23,6 +23,7 @@ interface ServicesListProps { setFilteredService: React.Dispatch>; filters: FilterType[]; setFilters: (filters: FilterType[]) => void; + isServicesDataLoading: boolean; } export const ServicesList = ({ @@ -32,6 +33,7 @@ export const ServicesList = ({ setFilteredService, filters = [], setFilters, + isServicesDataLoading, }: ServicesListProps) => { const [options, setOptions] = useState>([]); @@ -101,6 +103,7 @@ export const ServicesList = ({ searchable options={options} listProps={{ bordered: true }} + isLoading={isServicesDataLoading} onChange={(newOptions) => { const selectedOption = newOptions.find((option) => option.checked === 'on'); diff --git a/public/components/trace_analytics/components/traces/span_detail_flyout.tsx b/public/components/trace_analytics/components/traces/span_detail_flyout.tsx index 0c8e219d6a..8dcf9ea08b 100644 --- a/public/components/trace_analytics/components/traces/span_detail_flyout.tsx +++ b/public/components/trace_analytics/components/traces/span_detail_flyout.tsx @@ -13,6 +13,7 @@ import { EuiFlyoutBody, EuiFlyoutHeader, EuiHorizontalRule, + EuiLoadingContent, EuiSmallButtonIcon, EuiSpacer, EuiText, @@ -92,9 +93,17 @@ export function SpanDetailFlyout(props: { }) { const { mode } = props; const [span, setSpan] = useState({}); + const [isSpanDataLoading, setIsSpanDataLoading] = useState(false); useEffect(() => { - handleSpansFlyoutRequest(props.http, props.spanId, setSpan, mode, props.dataSourceMDSId); + setIsSpanDataLoading(true); + handleSpansFlyoutRequest( + props.http, + props.spanId, + setSpan, + mode, + props.dataSourceMDSId + ).finally(() => setIsSpanDataLoading(false)); }, [props.spanId]); const getListItem = ( @@ -123,6 +132,13 @@ export function SpanDetailFlyout(props: { }; const renderContent = () => { + if (isSpanDataLoading) { + return ( +
+ +
+ ); + } if (!span || isEmpty(span)) return '-'; const overviewList = [ getListItem( @@ -287,19 +303,21 @@ export function SpanDetailFlyout(props: { Overview - - {overviewList} - - - {eventsComponent} - - Span attributes - {attributesList.length === 0 || attributesList.length ? ( - {` (${attributesList.length})`} - ) : null} - - - {attributesList} + <> + + {overviewList} + + + {eventsComponent} + + Span attributes + {attributesList.length === 0 || attributesList.length ? ( + {` (${attributesList.length})`} + ) : null} + + + {attributesList} + ); }; diff --git a/public/components/trace_analytics/components/traces/span_detail_panel.tsx b/public/components/trace_analytics/components/traces/span_detail_panel.tsx index 2fce91840c..13e8c2b0fd 100644 --- a/public/components/trace_analytics/components/traces/span_detail_panel.tsx +++ b/public/components/trace_analytics/components/traces/span_detail_panel.tsx @@ -10,6 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, + EuiLoadingChart, EuiPanel, EuiSmallButton, EuiSpacer, @@ -20,12 +21,12 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' import useObservable from 'react-use/lib/useObservable'; import { HttpSetup } from '../../../../../../../src/core/public'; import { TraceAnalyticsMode } from '../../../../../common/types/trace_analytics'; +import { coreRefs } from '../../../../framework/core_refs'; import { Plt } from '../../../visualizations/plotly/plot'; import { handleSpansGanttRequest } from '../../requests/traces_request_handler'; import { PanelTitle } from '../common/helper_functions'; import { SpanDetailFlyout } from './span_detail_flyout'; import { SpanDetailTable, SpanDetailTableHierarchy } from './span_detail_table'; -import { coreRefs } from '../../../../framework/core_refs'; export function SpanDetailPanel(props: { http: HttpSetup; @@ -67,6 +68,7 @@ export function SpanDetailPanel(props: { const containerRef = useRef(null); const [availableWidth, setAvailableWidth] = useState(window.innerWidth); const newNavigation = coreRefs?.chrome?.navGroup.getNavGroupEnabled?.(); + const [isGanttChartLoading, setIsGanttChartLoading] = useState(false); const updateAvailableWidth = () => { if (containerRef.current) { @@ -145,7 +147,7 @@ export function SpanDetailPanel(props: { refreshDSL, mode, props.dataSourceMDSId - ); + ).finally(() => setIsGanttChartLoading(false)); }, 150); const spanFiltersToDSL = () => { @@ -196,6 +198,7 @@ export function SpanDetailPanel(props: { }; useEffect(() => { + setIsGanttChartLoading(true); refresh(); }, [props.colorMap, spanFilters]); @@ -471,6 +474,7 @@ export function SpanDetailPanel(props: { )} - - {spanFilters.length > 0 && ( - - - - {renderFilters} - - + {isGanttChartLoading ? ( +
+ +
+ ) : ( + <> + {spanFilters.length > 0 && ( + + + + {renderFilters} + + + )} + + + + {toggleIdSelected === 'timeline' && {miniMap}} + + + {toggleIdSelected === 'timeline' + ? ganttChart + : toggleIdSelected === 'span_list' + ? spanDetailTable + : spanDetailTableHierarchy} + + )} - - - - {toggleIdSelected === 'timeline' && {miniMap}} - - - {toggleIdSelected === 'timeline' - ? ganttChart - : toggleIdSelected === 'span_list' - ? spanDetailTable - : spanDetailTableHierarchy} - {!!currentSpan && ( diff --git a/public/components/trace_analytics/components/traces/span_detail_table.tsx b/public/components/trace_analytics/components/traces/span_detail_table.tsx index 0af8ad92fa..0c454b380a 100644 --- a/public/components/trace_analytics/components/traces/span_detail_table.tsx +++ b/public/components/trace_analytics/components/traces/span_detail_table.tsx @@ -4,10 +4,10 @@ */ /* eslint-disable react-hooks/exhaustive-deps */ -import { EuiDataGridColumn, EuiLink, EuiText, EuiIcon, EuiButtonEmpty } from '@elastic/eui'; +import { EuiButtonEmpty, EuiDataGridColumn, EuiIcon, EuiLink, EuiText } from '@elastic/eui'; import round from 'lodash/round'; import moment from 'moment'; -import React, { useEffect, useMemo, useState, useCallback } from 'react'; +import React, { useCallback, useEffect, useMemo, useState } from 'react'; import { HttpSetup } from '../../../../../../../src/core/public'; import { TRACE_ANALYTICS_DATE_FORMAT } from '../../../../../common/constants/trace_analytics'; import { TraceAnalyticsMode } from '../../../../../common/types/trace_analytics'; @@ -171,27 +171,26 @@ export function SpanDetailTable(props: SpanDetailTableProps) { }); const [items, setItems] = useState([]); const [total, setTotal] = useState(0); + const [isSpansTableDataLoading, setIsSpansTableDataLoading] = useState(false); + const { mode } = props; const fetchData = async () => { + setIsSpansTableDataLoading(true); const spanSearchParams: SpanSearchParams = { from: tableParams.page * tableParams.size, size: tableParams.size, sortingColumns: tableParams.sortingColumns.map(({ id, direction }) => ({ [id]: direction })), }; - try { - await handleSpansRequest( - props.http, - setItems, - setTotal, - spanSearchParams, - props.DSL, - props.mode, - props.dataSourceMDSId - ); - } catch (err) { - console.error('Error fetching spans:', err); - } + handleSpansRequest( + props.http, + setItems, + setTotal, + spanSearchParams, + props.DSL, + props.mode, + props.dataSourceMDSId + ).finally(() => setIsSpansTableDataLoading(false)); }; useEffect(() => { @@ -250,6 +249,7 @@ export function SpanDetailTable(props: SpanDetailTableProps) { noMatchMessageSize: 'xl', visibleColumns, availableWidth: props.availableWidth, + isTableDataLoading: isSpansTableDataLoading, }); } @@ -258,8 +258,10 @@ export function SpanDetailTableHierarchy(props: SpanDetailTableProps) { const [items, setItems] = useState([]); const [_total, setTotal] = useState(0); const [expandedRows, setExpandedRows] = useState(new Set()); + const [isSpansTableDataLoading, setIsSpansTableDataLoading] = useState(false); useEffect(() => { + setIsSpansTableDataLoading(true); const spanSearchParams = { from: 0, size: 10000, @@ -276,7 +278,7 @@ export function SpanDetailTableHierarchy(props: SpanDetailTableProps) { DSL, mode, dataSourceMDSId - ); + ).finally(() => setIsSpansTableDataLoading(false)); }, [DSL, http, mode, dataSourceMDSId]); type SpanMap = Record; @@ -423,5 +425,6 @@ export function SpanDetailTableHierarchy(props: SpanDetailTableProps) { availableWidth, noMatchMessageSize: 'xl', visibleColumns, + isTableDataLoading: isSpansTableDataLoading, }); } diff --git a/public/components/trace_analytics/components/traces/trace_view.tsx b/public/components/trace_analytics/components/traces/trace_view.tsx index 1ff7cbb1c3..3275c87a7a 100644 --- a/public/components/trace_analytics/components/traces/trace_view.tsx +++ b/public/components/trace_analytics/components/traces/trace_view.tsx @@ -10,6 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiHorizontalRule, + EuiLoadingContent, EuiPage, EuiPageBody, EuiPanel, @@ -65,105 +66,117 @@ export function TraceView(props: TraceViewProps) { ); }; - const renderOverview = (fields: any) => { + const [fields, setFields] = useState({}); + const [serviceBreakdownData, setServiceBreakdownData] = useState([]); + const [payloadData, setPayloadData] = useState(''); + const [colorMap, setColorMap] = useState({}); + const [ganttData, setGanttData] = useState<{ gantt: any[]; table: any[]; ganttMaxX: number }>({ + gantt: [], + table: [], + ganttMaxX: 0, + }); + const [serviceMap, setServiceMap] = useState({}); + const [traceFilteredServiceMap, setTraceFilteredServiceMap] = useState({}); + const [serviceMapIdSelected, setServiceMapIdSelected] = useState< + 'latency' | 'error_rate' | 'throughput' + >('latency'); + const [isServicesDataLoading, setIsServicesDataLoading] = useState(false); + const [isTraceOverViewLoading, setIsTraceOverViewLoading] = useState(false); + const [isTracePayloadLoading, setTracePayloadLoading] = useState(false); + const [isServicesPieChartLoading, setIsServicesPieChartLoading] = useState(false); + + const renderOverview = (overviewFields: any) => { return ( - - - - - - Trace ID - {fields.trace_id && ( - + {isTraceOverViewLoading ? ( +
+ +
+ ) : ( + <> + + + + + + Trace ID + {overviewFields.trace_id && ( + + + + {overviewFields.trace_id} + + + + + {(copy) => ( + + Click to copy + + )} + + + + )} + + {mode === 'data_prepper' || mode === 'custom_data_prepper' ? ( + Trace group name - {fields.trace_id} + {overviewFields.trace_group || '-'} - - - {(copy) => ( - - Click to copy - - )} - - - - )} - - {mode === 'data_prepper' || mode === 'custom_data_prepper' ? ( - - Trace group name - - {fields.trace_group || '-'} - - - ) : ( -
- )} - - - - - - Latency - - {fields.latency} - + ) : ( +
+ )} + - - Last updated - - {fields.last_updated} - + + + + Latency + + {overviewFields.latency} + + + + Last updated + + {overviewFields.last_updated} + + + - - - - - - Errors - - {fields.error_count == null ? ( - '-' - ) : fields.error_count > 0 ? ( - - Yes + + + + Errors + + {overviewFields.error_count == null ? ( + '-' + ) : fields.error_count > 0 ? ( + + Yes + + ) : ( + 'No' + )} - ) : ( - 'No' - )} - + + - - + + )} ); }; - const [fields, setFields] = useState({}); - const [serviceBreakdownData, setServiceBreakdownData] = useState([]); - const [payloadData, setPayloadData] = useState(''); - const [colorMap, setColorMap] = useState({}); - const [ganttData, setGanttData] = useState<{ gantt: any[]; table: any[]; ganttMaxX: number }>({ - gantt: [], - table: [], - ganttMaxX: 0, - }); - const [serviceMap, setServiceMap] = useState({}); - const [traceFilteredServiceMap, setTraceFilteredServiceMap] = useState({}); - const [serviceMapIdSelected, setServiceMapIdSelected] = useState< - 'latency' | 'error_rate' | 'throughput' - >('latency'); - const refresh = async () => { const DSL = filtersToDsl( mode, @@ -173,6 +186,8 @@ export function TraceView(props: TraceViewProps) { processTimeStamp('now', mode), page ); + + setIsTraceOverViewLoading(true); handleTraceViewRequest( props.traceId, props.http, @@ -180,7 +195,9 @@ export function TraceView(props: TraceViewProps) { setFields, mode, props.dataSourceMDSId[0].id - ); + ).finally(() => setIsTraceOverViewLoading(false)); + + setTracePayloadLoading(true); handlePayloadRequest( props.traceId, props.http, @@ -188,7 +205,9 @@ export function TraceView(props: TraceViewProps) { setPayloadData, mode, props.dataSourceMDSId[0].id - ); + ).finally(() => setTracePayloadLoading(false)); + + setIsServicesPieChartLoading(true); handleServicesPieChartRequest( props.traceId, props.http, @@ -196,8 +215,16 @@ export function TraceView(props: TraceViewProps) { setColorMap, mode, props.dataSourceMDSId[0].id - ); - handleServiceMapRequest(props.http, DSL, mode, props.dataSourceMDSId[0].id, setServiceMap); + ).finally(() => setIsServicesPieChartLoading(false)); + + setIsServicesDataLoading(true); + handleServiceMapRequest( + props.http, + DSL, + mode, + props.dataSourceMDSId[0].id, + setServiceMap + ).finally(() => setIsServicesDataLoading(false)); }; useEffect(() => { @@ -264,7 +291,10 @@ export function TraceView(props: TraceViewProps) { {renderOverview(fields)} - + @@ -291,17 +321,24 @@ export function TraceView(props: TraceViewProps) { - {payloadData.length > 0 ? ( - - {payloadData} - - ) : null} + {isTracePayloadLoading ? ( +
+ +
+ ) : ( + payloadData.length > 0 && ( + + {payloadData} + + ) + )} {mode === 'data_prepper' || mode === 'custom_data_prepper' ? ( ('closed'); const [serviceMap, setServiceMap] = useState({}); const [filteredService, setFilteredService] = useState(''); @@ -142,7 +143,6 @@ export function TracesContent(props: TracesProps) { const refresh = async (sort?: PropertySort, overrideQuery?: string) => { const filterQuery = overrideQuery ?? query; - setLoading(true); const DSL = filtersToDsl( mode, filters, @@ -162,50 +162,54 @@ export function TracesContent(props: TracesProps) { ); const isUnderOneHour = datemath.parse(endTime)?.diff(datemath.parse(startTime), 'hours')! < 1; + setIsTraceTableLoading(true); + if (mode === 'custom_data_prepper') { - // service map should not be filtered by service name + // Remove serviceName filter from service map query const serviceMapDSL = cloneDeep(DSL); serviceMapDSL.query.bool.must = serviceMapDSL.query.bool.must.filter( - (must: any) => must?.term?.serviceName == null + (must: any) => !must?.term?.serviceName ); - if (tracesTableMode !== 'traces') - await handleCustomIndicesTracesRequest( - http, - DSL, - tableItems, - setTableItems, - setColumns, - mode, - props.dataSourceMDSId[0].id, - sort, - tracesTableMode, - isUnderOneHour - ); - else { - await handleTracesRequest( - http, - DSL, - timeFilterDSL, - tableItems, - setTableItems, - mode, - props.dataSourceMDSId[0].id, - sort, - isUnderOneHour - ); - } - await handleServiceMapRequest( + const tracesRequest = + tracesTableMode !== 'traces' + ? handleCustomIndicesTracesRequest( + http, + DSL, + tableItems, + setTableItems, + setColumns, + mode, + props.dataSourceMDSId[0].id, + sort, + tracesTableMode, + isUnderOneHour + ) + : handleTracesRequest( + http, + DSL, + timeFilterDSL, + tableItems, + setTableItems, + mode, + props.dataSourceMDSId[0].id, + sort, + isUnderOneHour + ); + + tracesRequest.finally(() => setIsTraceTableLoading(false)); + + setIsServicesDataLoading(true); + handleServiceMapRequest( http, serviceMapDSL, mode, props.dataSourceMDSId[0].id, setServiceMap, - filteredService, includeMetrics - ); + ).finally(() => setIsServicesDataLoading(false)); } else { - await handleTracesRequest( + handleTracesRequest( http, DSL, timeFilterDSL, @@ -215,10 +219,8 @@ export function TracesContent(props: TracesProps) { props.dataSourceMDSId[0].id, sort, isUnderOneHour - ); + ).finally(() => setIsTraceTableLoading(false)); } - - setLoading(false); }; const dashboardContent = () => { @@ -272,7 +274,7 @@ export function TracesContent(props: TracesProps) { items={tableItems} refresh={refresh} mode={mode} - loading={loading} + loading={isTraceTableLoading} getTraceViewUri={getTraceViewUri} openTraceFlyout={openTraceFlyout} jaegerIndicesExist={jaegerIndicesExist} @@ -285,7 +287,7 @@ export function TracesContent(props: TracesProps) { items={tableItems} refresh={refresh} mode={mode} - loading={loading} + loading={isTraceTableLoading} getTraceViewUri={getTraceViewUri} openTraceFlyout={openTraceFlyout} jaegerIndicesExist={jaegerIndicesExist} @@ -305,6 +307,7 @@ export function TracesContent(props: TracesProps) { filters={filters} setFilters={setFilters} serviceMap={serviceMap} + isServicesDataLoading={isServicesDataLoading} filteredService={filteredService} setFilteredService={setFilteredService} /> @@ -316,6 +319,7 @@ export function TracesContent(props: TracesProps) { filters={filters} setFilters={setFilters} serviceMap={serviceMap} + isServicesDataLoading={isServicesDataLoading} idSelected={serviceMapIdSelected} setIdSelected={setServiceMapIdSelected} page={page} diff --git a/public/components/trace_analytics/index.scss b/public/components/trace_analytics/index.scss index b7596eaf84..95cfcc19a6 100644 --- a/public/components/trace_analytics/index.scss +++ b/public/components/trace_analytics/index.scss @@ -146,4 +146,12 @@ th[data-test-subj^='tableHeaderCell_dashboard_latency_variance'] { .full-screen-content { flex: 1 1 auto; overflow: auto; -} \ No newline at end of file +} + +.center-loading-div { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + min-height: 200px; +} diff --git a/public/components/trace_analytics/requests/services_request_handler.ts b/public/components/trace_analytics/requests/services_request_handler.ts index 9ccefcaa00..d1a5aeffca 100644 --- a/public/components/trace_analytics/requests/services_request_handler.ts +++ b/public/components/trace_analytics/requests/services_request_handler.ts @@ -86,7 +86,6 @@ export const handleServiceMapRequest = async ( mode: TraceAnalyticsMode, dataSourceMDSId?: string, setItems?: any, - currService?: string, includeMetrics = true ) => { let minutesInDateRange: number; @@ -195,7 +194,6 @@ export const handleServiceMapRequest = async ( console.error('Error retrieving service metrics:', error); } } - if (setItems) setItems(map); return map; }; @@ -208,7 +206,7 @@ export const handleServiceViewRequest = ( mode: TraceAnalyticsMode, dataSourceMDSId?: string ) => { - handleDslRequest(http, DSL, getServicesQuery(mode, serviceName), mode, dataSourceMDSId) + return handleDslRequest(http, DSL, getServicesQuery(mode, serviceName), mode, dataSourceMDSId) .then(async (response) => { const bucket = response.aggregations.service.buckets[0]; if (!bucket) return {}; diff --git a/public/components/trace_analytics/requests/traces_request_handler.ts b/public/components/trace_analytics/requests/traces_request_handler.ts index 6706b4fa8f..603f979a58 100644 --- a/public/components/trace_analytics/requests/traces_request_handler.ts +++ b/public/components/trace_analytics/requests/traces_request_handler.ts @@ -14,6 +14,7 @@ import { HttpSetup } from '../../../../../../src/core/public'; import { BarOrientation } from '../../../../common/constants/shared'; import { TRACE_ANALYTICS_DATE_FORMAT } from '../../../../common/constants/trace_analytics'; import { TraceAnalyticsMode, TraceQueryMode } from '../../../../common/types/trace_analytics'; +import { coreRefs } from '../../../../public/framework/core_refs'; import { getTimestampPrecision, microToMilliSec, @@ -31,7 +32,6 @@ import { getTracesQuery, } from './queries/traces_queries'; import { handleDslRequest } from './request_handler'; -import { coreRefs } from '../../../../public/framework/core_refs'; export const handleCustomIndicesTracesRequest = async ( http: HttpSetup, @@ -197,7 +197,7 @@ export const handleTraceViewRequest = ( mode: TraceAnalyticsMode, dataSourceMDSId?: string ) => { - handleDslRequest(http, null, getTracesQuery(mode, traceId), mode, dataSourceMDSId) + return handleDslRequest(http, null, getTracesQuery(mode, traceId), mode, dataSourceMDSId) .then(async (response) => { // Check if the mode hasn't been set first if (mode === 'jaeger' && !response?.aggregations?.service_type?.buckets) { @@ -317,7 +317,13 @@ export const handleSpansGanttRequest = ( mode: TraceAnalyticsMode, dataSourceMDSId?: string ) => { - handleDslRequest(http, spanFiltersDSL, getSpanDetailQuery(mode, traceId), mode, dataSourceMDSId) + return handleDslRequest( + http, + spanFiltersDSL, + getSpanDetailQuery(mode, traceId), + mode, + dataSourceMDSId + ) .then((response) => hitsToSpanDetailData(response.hits.hits, colorMap, mode)) .then((newItems) => setSpanDetailData(newItems)) .catch((error) => { @@ -336,7 +342,7 @@ export const handleSpansFlyoutRequest = ( mode: TraceAnalyticsMode, dataSourceMDSId?: string ) => { - handleDslRequest(http, null, getSpanFlyoutQuery(mode, spanId), mode, dataSourceMDSId) + return handleDslRequest(http, null, getSpanFlyoutQuery(mode, spanId), mode, dataSourceMDSId) .then((response) => { setItems(response?.hits.hits?.[0]?._source); }) @@ -459,7 +465,7 @@ export const handlePayloadRequest = ( mode: TraceAnalyticsMode, dataSourceMDSId?: string ) => { - handleDslRequest(http, null, getPayloadQuery(mode, traceId), mode, dataSourceMDSId) + return handleDslRequest(http, null, getPayloadQuery(mode, traceId), mode, dataSourceMDSId) .then((response) => setPayloadData(JSON.stringify(response.hits.hits, null, 2))) .catch((error) => { console.error('Error in handlePayloadRequest:', error); @@ -479,7 +485,7 @@ export const handleSpansRequest = ( mode: TraceAnalyticsMode, dataSourceMDSId?: string ) => { - handleDslRequest(http, DSL, getSpansQuery(spanSearchParams), mode, dataSourceMDSId) + return handleDslRequest(http, DSL, getSpansQuery(spanSearchParams), mode, dataSourceMDSId) .then((response) => { setItems(response.hits.hits.map((hit: any) => hit._source)); setTotal(response.hits.total?.value || 0);