diff --git a/.cypress/integration/1_event_analytics.spec.js b/.cypress/integration/1_event_analytics.spec.js index 7a3a45352..3f43a6eb7 100644 --- a/.cypress/integration/1_event_analytics.spec.js +++ b/.cypress/integration/1_event_analytics.spec.js @@ -17,10 +17,68 @@ import { landOnEventHome, landOnEventExplorer, landOnEventVisualizations, - landOnPanels + landOnPanels, + renderTreeMapchart, + renderPieChart, + renderLineChartForDataConfig, + renderDataConfig, + aggregationValues, + DataConfigLineChart, + renderAddParent, + renderGaugeChart, + renderAddParent } from '../utils/event_constants'; import { supressResizeObserverIssue } from '../utils/constants'; +const renderHistogramChart = () => { + querySearch(TEST_QUERIES[5].query, TEST_QUERIES[5].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Histogram').type('{enter}'); + cy.wait(delay); + cy.get('g.draglayer.cursor-crosshair').should('exist'); + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Histogram chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for Histogram chart'); + cy.get('.euiIEFlexWrapFix').eq(1).contains('Chart Styles').should('exist'); + cy.get('.euiFormLabel.euiFormRow__label').eq(2).contains('Bucket Size'); + cy.get('.euiFieldNumber').eq(0).type('4'); + cy.get('.euiFormLabel.euiFormRow__label').eq(3).contains('Bucket Offset'); + cy.get('.euiFieldNumber').eq(0).type('6'); +}; + +const vis_name_sub_string = Math.floor(Math.random() * 100); +const saveVisualizationAndVerify = () => { + cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); + cy.get('[data-test-subj="eventExplorer__querySaveComboBox"]').click(); + cy.get('.euiComboBoxOptionsList__rowWrap .euiFilterSelectItem').eq(0).click(); + cy.get( + '.euiPopover__panel .euiFormControlLayoutIcons [data-test-subj="comboBoxToggleListButton"]' + ) + .eq(0) + .click(); + cy.get('.euiPopover__panel input') + .eq(1) + .type(`Test visualization` + vis_name_sub_string); + cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); + cy.wait(delay); + cy.get('.euiHeaderBreadcrumbs a').eq(1).click(); + cy.get('.euiFlexGroup .euiFormControlLayout__childrenWrapper input') + .eq(0) + .type(`Test visualization` + vis_name_sub_string) + .type('{enter}'); + cy.get('.euiBasicTable .euiTableCellContent button').eq(0).click(); +}; +const deleteVisualization = () => { + cy.get('a[href = "#/event_analytics"]').click(); + cy.get('.euiFlexGroup .euiFormControlLayout__childrenWrapper input') + .eq(0) + .type(`Test visualization`) + .type('{enter}'); + cy.get('input[data-test-subj = "checkboxSelectAll"]').click(); + cy.get('.euiButtonContent.euiButtonContent--iconRight.euiButton__content').click(); + cy.get('.euiContextMenuItem .euiContextMenuItem__text').eq(0).click(); + cy.get('input[placeholder = "delete"]').clear().type('delete'); + cy.get('button[data-test-subj = "popoverModal__deleteButton"]').click(); + cy.get('.euiToastHeader').should('exist'); +}; describe('Adding sample data and visualization', () => { it('Adds sample flights data for event analytics', () => { cy.visit(`${Cypress.env('opensearchDashboards')}/app/home#/tutorial_directory/sampleData`); @@ -55,11 +113,14 @@ describe('Search a query on event home', () => { cy.get('[data-test-subj="superDatePickerToggleQuickMenuButton"]').click(); cy.get('[data-test-subj="superDatePickerCommonlyUsed_Year_to date"]').click(); cy.get('[data-test-subj="superDatePickerApplyTimeButton"]').contains('Refresh').click(); - cy.window().its('store').invoke('getState').then((state) => { - expect(Object.values(state.queries)[0]['rawQuery'].trim()).equal(TEST_QUERIES[0].query) - expect(Object.values(state.queries)[0]['selectedDateRange'][0]).equal("now/y"); - expect(Object.values(state.queries)[0]['selectedDateRange'][1]).equal("now"); - }); + cy.window() + .its('store') + .invoke('getState') + .then((state) => { + expect(Object.values(state.queries)[0]['rawQuery'].trim()).equal(TEST_QUERIES[0].query); + expect(Object.values(state.queries)[0]['selectedDateRange'][0]).equal('now/y'); + expect(Object.values(state.queries)[0]['selectedDateRange'][1]).equal('now'); + }); cy.wait(delay); cy.url().should('contain', '#/event_analytics/explorer'); @@ -76,16 +137,26 @@ describe('Open flyout for a data row to see details', () => { it('Should be able to open flyout and see data, json and traces', () => { cy.get('[data-test-subj="docTable"] tbody tr button.euiButtonIcon').first().click(); cy.get('.observability-flyout').should('exist'); - cy.get('.observability-flyout .osdDocViewer .euiTabs span.euiTab__content').contains('JSON').click(); - cy.get('.observability-flyout .osdDocViewer .euiTabs span.euiTab__content').contains('Traces').click(); - cy.get('.observability-flyout .osdDocViewer .euiTabs span.euiTab__content').contains('Table').click(); + cy.get('.observability-flyout .osdDocViewer .euiTabs span.euiTab__content') + .contains('JSON') + .click(); + cy.get('.observability-flyout .osdDocViewer .euiTabs span.euiTab__content') + .contains('Traces') + .click(); + cy.get('.observability-flyout .osdDocViewer .euiTabs span.euiTab__content') + .contains('Table') + .click(); }); it('Should be able to see srrounding docs', () => { cy.get('[data-test-subj="docTable"] tbody tr button.euiButtonIcon').first().click(); cy.get('.observability-flyout').should('exist'); - cy.get('.observability-flyout span.euiButton__text').contains('View surrounding events').click(); - cy.get('.observability-flyout #surroundingFyout').contains('View surrounding events').should('exist'); + cy.get('.observability-flyout span.euiButton__text') + .contains('View surrounding events') + .click(); + cy.get('.observability-flyout #surroundingFyout') + .contains('View surrounding events') + .should('exist'); }); }); @@ -252,7 +323,9 @@ describe('Saves a query on explorer page', () => { cy.get('button[id="main-content-vis"]').contains('Visualizations').click(); cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); cy.wait(delay * 2); - cy.get('[data-test-subj="eventExplorer__querySaveComboBox"] [data-test-subj="comboBoxToggleListButton"]').click(); + cy.get( + '[data-test-subj="eventExplorer__querySaveComboBox"] [data-test-subj="comboBoxToggleListButton"]' + ).click(); cy.get('[data-test-subj="eventExplorer__querySaveName"]').type(SAVE_QUERY2); cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); cy.wait(delay * 2); @@ -282,11 +355,15 @@ describe('Saves a query on explorer page', () => { cy.get('button[id="main-content-vis"]').contains('Visualizations').click(); cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); cy.wait(delay * 2); - cy.get('[data-test-subj="eventExplorer__querySaveComboBox"] [data-test-subj="comboBoxToggleListButton"]').click(); + cy.get( + '[data-test-subj="eventExplorer__querySaveComboBox"] [data-test-subj="comboBoxToggleListButton"]' + ).click(); cy.get('[data-test-subj="eventExplorer__querySaveName"]').type(SAVE_QUERY3); cy.get('[data-test-subj="eventExplorer__querySaveComboBox"]').type(TESTING_PANEL); cy.get(`input[value="${TESTING_PANEL}"]`).click(); - cy.get('[data-test-subj="eventExplorer__querySaveComboBox"] [data-test-subj="comboBoxToggleListButton"]').click(); + cy.get( + '[data-test-subj="eventExplorer__querySaveComboBox"] [data-test-subj="comboBoxToggleListButton"]' + ).click(); cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); cy.wait(delay); @@ -305,8 +382,11 @@ describe('Override timestamp for an index', () => { cy.wait(delay); cy.get('[data-attr-field="utc_time"] [data-test-subj="eventFields__default-timestamp-mark"') - .contains('Default Timestamp').should('exist'); - cy.get('[data-attr-field="timestamp"] [data-test-subj="eventFields__default-timestamp-mark"').should('not.exist'); + .contains('Default Timestamp') + .should('exist'); + cy.get( + '[data-attr-field="timestamp"] [data-test-subj="eventFields__default-timestamp-mark"' + ).should('not.exist'); }); }); @@ -369,20 +449,40 @@ describe('Click to view field insights', () => { it('Click a numerical field to view field insights', () => { cy.get('[data-test-subj="field-bytes-showDetails"]').click(); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Top values').should('exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Rare values').should('exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Average overtime').should('exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Maximum overtime').should('exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Minimum overtime').should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Top values') + .should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Rare values') + .should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Average overtime') + .should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Maximum overtime') + .should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Minimum overtime') + .should('exist'); }); it('Click a non-numerical field to view insights', () => { cy.get('[data-test-subj="field-host-showDetails"]').click(); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Top values').should('exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Rare values').should('exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Average overtime').should('not.exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Maximum overtime').should('not.exist'); - cy.get('[data-test-subj="sidebarField__fieldInsights"] button').contains('Minimum overtime').should('not.exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Top values') + .should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Rare values') + .should('exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Average overtime') + .should('not.exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Maximum overtime') + .should('not.exist'); + cy.get('[data-test-subj="sidebarField__fieldInsights"] button') + .contains('Minimum overtime') + .should('not.exist'); }); }); @@ -426,8 +526,8 @@ describe('Live tail stop automatically', () => { cy.get('[data-test-subj="eventExplorer__topLevelTabbing"]') .find('button.euiTab') .should('have.length', initialLength + 1); + }); }); -}); it('Click to switch to another tab', () => { cy.get('[data-test-subj="eventExplorer__addNewTab"]').click(); @@ -469,7 +569,9 @@ describe('Renders noresult chart', () => { }); it('It should render no result when there is no data', () => { - cy.get('[data-test-subj="vizWorkspace__noData"] p').contains('No results found').should('exist'); + cy.get('[data-test-subj="vizWorkspace__noData"] p') + .contains('No results found') + .should('exist'); }); }); @@ -480,7 +582,9 @@ describe('Renders bar charts', () => { it('Renders vertical bar chart', () => { querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get( + '[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]' + ).click(); cy.wait(delay * 2); cy.get('[data-test-subj="comboBoxOptionsList "] span').contains('Bar').click(); cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').first().click(); @@ -497,12 +601,16 @@ describe('Renders bar charts', () => { cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Group').click(); cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); cy.wait(delay * 2); - cy.get('g.xaxislayer-above > g.xtick text[data-unformatted|="artifacts.opensearch.org"]').should('exist'); + cy.get( + 'g.xaxislayer-above > g.xtick text[data-unformatted|="artifacts.opensearch.org"]' + ).should('exist'); }); it('Renders horiztontal bar chart', () => { querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get( + '[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]' + ).click(); cy.wait(delay * 2); cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Bar').click(); cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').first().click(); @@ -519,7 +627,9 @@ describe('Renders bar charts', () => { cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Group').click(); cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); cy.wait(delay * 2); - cy.get('g.yaxislayer-above > g.ytick text[data-unformatted|="artifacts.opensearch.org"]').should('exist'); + cy.get( + 'g.yaxislayer-above > g.ytick text[data-unformatted|="artifacts.opensearch.org"]' + ).should('exist'); }); }); @@ -530,7 +640,9 @@ describe('Renders line charts', () => { it('Renders line chart with threshold', () => { querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get( + '[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]' + ).click(); cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Line').click(); cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').first().click(); cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('host').click(); @@ -544,7 +656,9 @@ describe('Renders line charts', () => { cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); cy.wait(delay * 2); cy.get('g.text > g.textpoint text[data-unformatted|="Max"]').should('exist'); - cy.get('g.xaxislayer-above > g.xtick text[data-unformatted|="artifacts.opensearch.org"]').should('exist'); + cy.get( + 'g.xaxislayer-above > g.xtick text[data-unformatted|="artifacts.opensearch.org"]' + ).should('exist'); }); }); @@ -555,7 +669,9 @@ describe('Renders pie charts', () => { it('Renders pie chart', () => { querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get( + '[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]' + ).click(); cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Pie').click(); cy.wait(delay); cy.get('g.pielayer').should('exist'); @@ -569,7 +685,9 @@ describe('Renders heatmap chart', () => { it('Renders heatmap chart with different z-axes', () => { querySearch(TEST_QUERIES[4].query, TEST_QUERIES[4].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get( + '[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]' + ).click(); cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Heatmap').click(); cy.wait(delay * 2); cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]').click(); @@ -593,13 +711,17 @@ describe('Renders markdown chart', () => { it('Renders markdown chart with test title', () => { querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); - cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get( + '[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]' + ).click(); cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Text').click(); cy.get('[data-test-subj="workspace__viz_markdown"] h2').contains('Text').should('exist'); cy.get('textarea.euiMarkdownEditorTextArea').type('## testing title'); cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); cy.wait(delay * 2); - cy.get('[data-test-subj="workspace__viz_markdown"] h2').contains('testing title').should('exist'); + cy.get('[data-test-subj="workspace__viz_markdown"] h2') + .contains('testing title') + .should('exist'); }); }); @@ -615,4 +737,302 @@ describe('Renders data view', () => { cy.get('[data-test-subj="workspace__dataTableViewSwitch"]').click(); cy.get('[data-test-subj="workspace__dataTable"]').should('not.exist'); }); -}); \ No newline at end of file +}); + +describe('Renders chart and verify Toast message if X-axis and Y-axis values are empty', () => { + beforeEach(() => { + landOnEventVisualizations(); + }); + it('Renders chart, clear X-axis and Y-axis value and click on Apply button, Toast message should display with error message', () => { + querySearch(TEST_QUERIES[4].query, TEST_QUERIES[4].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Bar') + .type('{enter}'); + cy.wait(delay); + cy.get('#configPanel__value_options [data-test-subj="comboBoxClearButton"]') + .eq(0) + .click({ force: true }); + cy.get('#configPanel__value_options [data-test-subj="comboBoxToggleListButton"]').eq(0).click(); + cy.wait(delay); + cy.get('#configPanel__value_options [data-test-subj="comboBoxClearButton"]').click({ + multiple: true, + }); + cy.get('#configPanel__value_options [data-test-subj="comboBoxToggleListButton"]').eq(1).click(); + cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]') + .eq(0) + .should('have.value', ''); + cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]') + .eq(1) + .should('have.value', ''); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); + cy.get('[data-test-subj="euiToastHeader"]') + .contains('Invalid value options configuration selected.') + .should('exist'); + }); + + it('Renders chart, clear X-axis and Y-axis value and try to save visulization, Toast message should display with error message', () => { + querySearch(TEST_QUERIES[4].query, TEST_QUERIES[4].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Bar') + .type('{enter}'); + cy.wait(delay); + cy.get('#configPanel__value_options [data-test-subj="comboBoxClearButton"]') + .eq(0) + .click({ force: true }); + cy.get('#configPanel__value_options [data-test-subj="comboBoxToggleListButton"]').eq(0).click(); + cy.wait(delay); + cy.get('#configPanel__value_options [data-test-subj="comboBoxClearButton"]').click({ + multiple: true, + }); + cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]') + .eq(0) + .should('have.value', ''); + cy.get('#configPanel__value_options [data-test-subj="comboBoxInput"]') + .eq(1) + .should('have.value', ''); + cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); + cy.get('[data-test-subj="eventExplorer__querySaveComboBox"]').click(); + cy.get('.euiComboBoxOptionsList__rowWrap .euiFilterSelectItem').eq(0).click(); + cy.get( + '.euiPopover__panel .euiFormControlLayoutIcons [data-test-subj="comboBoxToggleListButton"]' + ) + .eq(0) + .click(); + cy.get('.euiPopover__panel input').eq(1).type(`Test visulization_`); + cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); + cy.get('[data-test-subj="euiToastHeader"]') + .contains('Invalid value options configuration selected.') + .should('exist'); + }); +}); + +describe('Render Table View', () => { + beforeEach(() => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); + cy.get('[data-test-subj="workspace__dataTableViewSwitch"]').click(); + }); + + it('Switch visualization for table view and verify table data', () => { + cy.get('.ag-header-cell-text').contains('max(AvgTicketPrice)').should('exist'); + cy.get('.ag-header-cell-text').contains('DestCountry').should('exist'); + cy.get('.ag-header-cell-text').contains('DestCityName').should('exist'); + cy.get('.ag-header-cell-text').contains('Carrier').should('exist'); + }); + + it('Switch visualization for table view and change data table density', () => { + cy.get('.euiButtonEmpty__text').contains('Density').click(); + cy.get('.euiButtonIcon.euiButtonIcon--primary.euiButtonIcon--xSmall').eq(1).click(); + cy.get('.euiButtonIcon.euiButtonIcon--primary.euiButtonIcon--xSmall').eq(2).click(); + }); + + it('Switch visualization for table view and show and hide column', () => { + cy.get('.euiButtonEmpty__text').contains('Columns').click(); + cy.get('.euiSwitch__label').contains('DestCountry').click(); + cy.get('.ag-header-cell-text').contains('DestCountry').should('not.exist'); + cy.get('.euiSwitch__label').contains('Carrier').click(); + cy.get('.ag-header-cell-text').contains('Carrier').should('not.exist'); + cy.get('.euiSwitch__label').contains('DestCountry').click(); + cy.get('.ag-header-cell-text').contains('DestCountry').should('exist'); + }); + + it('Switch visualization for table view and see data in full screen', () => { + cy.get('.ag-header-cell-text').contains('max(AvgTicketPrice)').should('exist'); + cy.get('.ag-header-cell-text').contains('DestCountry').should('exist'); + cy.get('.ag-header-cell-text').contains('DestCityName').should('exist'); + cy.get('.ag-header-cell-text').contains('Carrier').should('exist'); + cy.get('.euiButtonEmpty__text').contains('Full screen').click(); + cy.wait(delay); + cy.get('body').type('{esc}'); + cy.wait(delay); + }); + + it('Switch visualization for table view and sort the column data', () => { + cy.get('.ag-header-cell-text').contains('max(AvgTicketPrice)').click(); + cy.get('.ag-cell-value').contains('125.49737').should('exist'); + cy.get('.ag-header-cell-text').contains('max(AvgTicketPrice)').click(); + cy.get('.ag-cell-value').contains('1199.729').should('exist'); + cy.get('.ag-header-cell-text').contains('DestCountry').click(); + cy.get('.ag-cell-value').contains('AE').should('exist'); + }); + + it('Switch visualization for table view and verify pagination link', () => { + cy.get('[aria-label="Next page"]').click(); + cy.get('.ag-cell-value').contains('Vienna').should('exist'); + cy.get('[aria-label="Previous page"]').click(); + cy.get('.ag-cell-value').contains('Dubai').should('exist'); + cy.get('[aria-label="Page 4"]').contains('4').click(); + cy.get('.ag-cell-value').contains('Edmonton').should('exist'); + }); + it('Switch visualization for table view and rows per page data', () => { + cy.get('.euiButtonEmpty__text').eq('6').click(); + cy.get('.euiContextMenuItem__text').eq(1).click(); + }); +}); + +describe('Render Time series chart/Line chart and verify Data configurations UI ', () => { + it('Render line chart and verify Data Configuration Panel', () => { + renderLineChartForDataConfig(); + DataConfigLineChart(); + }); +}); + +describe('Renders Data Configurations section for Pie chart', () => { + beforeEach(() => { + landOnEventVisualizations(); + }); + + it('Renders Dimensions and Metrics under Data Configurations for Pie chart', () => { + renderPieChart(); + renderDataConfig(); + }); + + it('Validate "Add" and "X" buttons', () => { + renderPieChart(); + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Configurations'); + cy.get('.euiButton.euiButton--primary.euiButton--fullWidth').contains('Add').click(); + cy.get('.euiFormRow__fieldWrapper .euiComboBox').eq(3).click(); + cy.get('.euiComboBoxOption__content').eq(2).click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(4).click(); + cy.get('.euiComboBoxOption__content').eq(1).click(); + cy.get('.euiFieldText[placeholder="Custom label"]').eq(1).type('Demo field'); + cy.get('.euiIcon.euiIcon--medium.euiIcon--danger').eq(1).click(); + cy.get('.euiButton.euiButton--primary.euiButton--fullWidth').contains('Add').should('exist'); + }); + + it('Verify drop down values for Aggregation', () => { + renderPieChart(); + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Configurations'); + cy.get('.euiTitle.euiTitle--xxsmall').eq(1).contains('Dimensions').should('exist'); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(0).contains('Aggregation'); + cy.get('[data-test-subj="comboBoxSearchInput"]').eq(0).click(); + aggregationValues.forEach(function (value){ + cy.get('.euiComboBoxOption__content').contains(value); + }); + }); + + it('Collapsible mode for Data Configuration panel', () => { + renderPieChart(); + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Configurations'); + cy.get('.euiResizableButton.euiResizableButton--horizontal').eq(1).click(); + cy.get('[data-test-subj="panel-1-toggle"]').click(); + cy.get('[class*="euiResizableToggleButton-isCollapsed"]').eq(1).should('exist'); +describe('Renders Histogram chart', () => { + beforeEach(() => { + landOnEventVisualizations(); +}); + +it('Renders Histogram chart and save visualization', () => { + renderHistogramChart(); + cy.get('.euiFlexItem.euiFlexItem--flexGrowZero .euiButton__text').eq(2).click(); + cy.wait(delay); + saveVisualizationAndVerify(); + }); + + it('Delete Visualization for Histogram chart from list of saved Visualizations on Event analytics page', () =>{ + deleteVisualization(); + }) + + it('Renders Histogram chart, add value parameters and verify Reset button click is working', () => { + renderHistogramChart(); + cy.get('[data-test-subj="visualizeEditorResetButton"]').click(); + }); +}); +describe('Render Gauge Chart and verify if data gets render', () => { + it('Render gauge chart and verify by default no data gets render', () => { + renderGaugeChart(); + cy.get('.main-svg').contains('BeatsWest').should('not.exist'); + }); + + it('Render gauge chart and verify data gets render after click on update chart', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.main-svg').contains('BeatsWest').should('exist'); + }); +}); + +describe('Render Gauge Chart and work with chart styles', () => { + it('Render gauge chart and change orientation to vertical', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.euiButton__text').contains('Vertical').click(); + cy.get('.euiButton__text').contains('Preview').click(); + }); + + it('Render gauge chart and change title size then verify the update on chart', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).type('30'); + cy.get('.euiButton__text').contains('Preview').click(); + }); + + it('Render gauge chart and change value size then verify the update on chart', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).type('20'); + cy.get('.euiButton__text').contains('Preview').click(); + }); +}); + +describe('Render Gauge Chart and work with threshold', () => { + it('Render gauge chart and add threshold then verify by default the threshold is not seen', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.euiButton__text').contains('+ Add threshold').click(); + cy.get('[data-test-subj="nameFieldText"]').type('Gauge Threshold'); + cy.get('[data-test-subj="valueFieldNumber"]').eq(2).type('50'); + cy.get('.euiButton__text').contains('Preview').click(); + cy.get('[data-unformatted="Gauge Threshold"]').should('not.be.visible'); + }); + + it('Render gauge chart and add threshold then verify the threshold label are seen after show threshold button enabled ', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.euiButton__text').contains('+ Add threshold').click(); + cy.get('[data-test-subj="nameFieldText"]').type('Gauge Threshold'); + cy.get('[data-test-subj="valueFieldNumber"]').eq(2).type('50'); + cy.get('.euiSwitch__label').contains('Show threshold labels').click(); + cy.get('.euiButton__text').contains('Preview').click(); + cy.get('[data-unformatted="Gauge Threshold"]').should('be.visible'); + }); + + it('Render gauge chart and add threshold then verify the threshold marker are seen after show threshold button enabled ', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.euiButton__text').contains('+ Add threshold').click(); + cy.get('[data-test-subj="nameFieldText"]').type('Gauge Threshold'); + cy.get('[data-test-subj="valueFieldNumber"]').eq(2).type('50'); + cy.get('.euiSwitch__label').contains('Show threshold markers').click(); + cy.get('.euiButton__text').contains('Preview').click(); + cy.get('path[style*="rgb(252, 5, 5)"]').eq(1).should('exist'); + cy.get('.bg-arc').find('path[style*="rgb(252, 5, 5)"]').should('have.length',4); + }); +}); + +describe('Render gauge chart and verify if reset works properly', () => { + it('Render gauge chart with all feild data then click on reset and verify reset works properly', () => { + renderGaugeChart(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('input[placeholder="Title"]').type('Gauge Chart'); + cy.get('textarea[placeholder="Description"]').type('Description For Gauge Chart'); + cy.get('.euiButton__text').contains('Vertical').click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).type('30'); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).type('20'); + cy.get('.euiButton__text').contains('+ Add threshold').click(); + cy.get('[data-test-subj="nameFieldText"]').type('Gauge Threshold'); + cy.get('[data-test-subj="valueFieldNumber"]').eq(2).type('50'); + cy.get('.euiSwitch__label').contains('Show threshold labels').click(); + cy.get('.euiSwitch__label').contains('Show threshold markers').click(); + cy.get('.euiButton__text').contains('Preview').click(); + cy.get('.euiButtonEmpty__text').contains('Reset').click(); + cy.get('input[placeholder="Title"]').should('not.have.value','Gauge Chart'); + cy.get('textarea[placeholder="Description"]').should('not.have.value','Description For Gauge Chart') + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).should('have.value',''); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).should('have.value',''); + cy.get('button.euiSwitch__button[aria-checked="false"]').should('exist').should('have.length',3); + }); +}); diff --git a/.cypress/integration/2_notebooks.spec.js b/.cypress/integration/2_notebooks.spec.js index ca0083249..3ba071215 100644 --- a/.cypress/integration/2_notebooks.spec.js +++ b/.cypress/integration/2_notebooks.spec.js @@ -12,6 +12,8 @@ import { SAMPLE_URL, SQL_QUERY_TEXT, PPL_QUERY_TEXT, + NOTEBOOK_TEXT, + OPENSEARCH_URL, } from '../utils/constants'; import { SAMPLE_PANEL } from '../utils/panel_constants'; @@ -63,6 +65,13 @@ describe('Testing notebooks table', () => { cy.visit(`${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/notebooks`); }); + it('Notebooks table empty state', () => { + cy.get('#notebookArea').contains('Notebooks (0)').should('exist'); + cy.get('.euiTextAlign.euiTextAlign--center').contains('No notebooks'); + cy.get('.euiButton__text').eq(2).contains('Create notebook'); + cy.get('.euiButton__text').eq(3).contains('Add samples'); + }); + it('Displays error toast for invalid notebook name', () => { cy.get('.euiButton__text').contains('Create notebook').click(); cy.wait(delay); @@ -125,6 +134,21 @@ describe('Testing notebooks table', () => { .should('exist'); }); + it('Notebooks table columns headers and pagination', () => { + cy.get('.euiTitle.euiTitle--small').contains('Notebooks').should('exist'); + cy.get('.euiTableCellContent__text[title="Name"]').should('exist'); + cy.get('.euiTableCellContent__text[title="Last updated"]').should('exist'); + cy.get('.euiTableCellContent__text[title="Created"]').should('exist'); + cy.get('[data-test-subj="tablePaginationPopoverButton"]').should('exist'); + }); + + it('"Learn more" link under Notebooks header', () => { + cy.get('.euiTitle.euiTitle--small').contains('Notebooks'); + cy.get('.euiTextColor.euiTextColor--subdued').contains(NOTEBOOK_TEXT); + cy.get('a.euiLink.euiLink--primary').contains('Learn more').click(); + cy.get(`a[href="${OPENSEARCH_URL}"]`).should('exist'); + }); + it('Deletes notebooks', () => { cy.get('.euiCheckbox__input[data-test-subj="checkboxSelectAll"]').click(); cy.wait(delay); @@ -231,6 +255,16 @@ describe('Testing paragraphs', () => { cy.get('.euiTitle').contains('Event analytics').should('exist'); }); + it('Paragraph actions layout', () => { + cy.get('button[data-test-subj="notebook-paragraph-actions-button"]').should('exist').click(); + cy.get('.euiContextMenuPanelTitle').contains('Actions'); + cy.get('.euiContextMenuItem__text').eq(0).contains('Add paragraph to top'); + cy.get('.euiContextMenuItem__text').eq(1).contains('Add paragraph to bottom'); + cy.get('.euiContextMenuItem__text').eq(2).contains('Run all paragraphs'); + cy.get('.euiContextMenuItem__text').eq(3).contains('Clear all outputs'); + cy.get('.euiContextMenuItem__text').eq(4).contains('Delete all paragraphs'); + }); + it('Renders markdown', () => { cy.get('.euiTextArea').should('not.exist'); cy.get(`a[href="${SAMPLE_URL}"]`).should('exist'); diff --git a/.cypress/integration/3_panels.spec.js b/.cypress/integration/3_panels.spec.js index 730dd4040..e7b349633 100644 --- a/.cypress/integration/3_panels.spec.js +++ b/.cypress/integration/3_panels.spec.js @@ -502,37 +502,7 @@ describe('Testing a panel', () => { }); }); -describe('Add samples and clean up all test data', () => { - it('Add sample data', () => { - moveToPanelHome(); - cy.get('.euiButton__text').contains('Actions').trigger('mouseover').click(); - cy.wait(delay); - cy.get('.euiContextMenuItem__text').contains('Add samples').trigger('mouseover').click(); - cy.wait(delay * 3); - cy.get('.euiModalHeader__title[data-test-subj="confirmModalTitleText"]') - .contains('Add samples') - .should('exist'); - cy.wait(delay); - cy.get('.euiButton__text').contains('Yes').trigger('mouseover').click(); - cy.wait(delay * 5); - cy.route2('POST', '/addSamplePanels').as('removePage'); - cy.wait('@removePage').then(() => { - cy.get('.euiTableCellContent').contains(SAMPLE_PANEL).should('exist'); - }); - cy.wait(delay); - }); - - it('Validate sample data', () => { - moveToPanelHome(); - cy.get('.euiTableCellContent').contains(SAMPLE_PANEL).trigger('mouseover').click(); - cy.wait(delay * 3); - cy.get('h1').contains(SAMPLE_PANEL).should('exist'); - cy.wait(delay); - SAMPLE_VISUALIZATIONS_NAMES.forEach((vizName) => - cy.get('h5').contains(vizName).should('exist') - ); - cy.wait(delay); - }); +describe('Clean up all test data', () => { it('Delete visualizations from event analytics', () => { moveToEventsHome(); @@ -572,3 +542,4 @@ describe('Add samples and clean up all test data', () => { cy.get('.euiTextAlign').contains('No Operational Panels').should('exist'); }); }); + diff --git a/.cypress/integration/4_trace_analytics_dashboard.spec.js b/.cypress/integration/4_trace_analytics_dashboard.spec.js index 6e510a8d8..f9bfb5760 100644 --- a/.cypress/integration/4_trace_analytics_dashboard.spec.js +++ b/.cypress/integration/4_trace_analytics_dashboard.spec.js @@ -187,3 +187,108 @@ describe('Testing plots', () => { cy.get('text.annotation-text[data-unformatted="Now: 108"]').should('exist'); }); }); + +describe('Latency by trace group table', () =>{ + beforeEach(() => { + cy.visit('app/observability-dashboards#/trace_analytics/home', { + onBeforeLoad: (win) => { + win.sessionStorage.clear(); + }, + }); + setTimeFilter(); + }); + + it('Verify columns in Latency by trace group table along with pagination functionality', () => { + cy.get('span.panel-title').eq(0).should('exist'); + cy.wait(delay); + cy.get('span[title="Trace group name"]').should('exist'); + cy.get('span[title="Latency variance (ms)"]').should('exist'); + cy.get('span[title="Average latency (ms)"]').should('exist'); + cy.get('span[title="24-hour latency trend"]').should('exist'); + cy.get('span[title="Error rate"] .euiToolTipAnchor').should('exist'); + cy.get('span[title="Traces"] .euiToolTipAnchor').should('exist'); + cy.get('[data-test-subj="tablePaginationPopoverButton"]').click(); + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiContextMenu__icon').eq(0).should('exist').click(); + cy.get('[data-test-subj="pagination-button-next"]').should('exist').click(); + 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.wait(delay); + }); + + it('Verify tooltips in Latency by trace group table', () => { + cy.get('.euiIcon.euiIcon--small.euiIcon--subdued.euiIcon-isLoaded.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.get('.euiIcon.euiIcon--small.euiIcon--subdued.euiIcon-isLoaded.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.get('.euiIcon.euiIcon--small.euiIcon--subdued.euiIcon-isLoaded.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.get('.euiIcon.euiIcon--small.euiIcon--subdued.euiIcon-isLoaded.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.get('.euiIcon.euiIcon--small.euiIcon--subdued.euiIcon-isLoaded.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.get('.euiIcon.euiIcon--small.euiIcon--subdued.euiIcon-isLoaded.eui-alignTop').eq(5).trigger('mouseover'); + 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{enter}'); + cy.wait(delay); + cy.get('.euiTableCellContent.euiTableCellContent--alignRight.euiTableCellContent--overflowingContent').contains('211.04').should('exist'); + cy.get('button[data-test-subj="dashboard-table-trace-group-name-button"]').click(); + cy.get('.euiBadge.euiBadge--hollow.euiBadge--iconRight.globalFilterItem').click(); + cy.get('.euiIcon.euiIcon--medium.euiContextMenu__arrow').click(); + cy.get('.euiContextMenuPanelTitle').contains('Edit filter').should('exist'); + cy.get('.euiButton.euiButton--primary.euiButton--fill').click(); + cy.get('.euiBadge.euiBadge--hollow.euiBadge--iconRight.globalFilterItem').click(); + cy.get('.euiContextMenuItem__text').eq(1).contains('Exclude results').click(); + cy.get('.euiTextColor.euiTextColor--danger').should('exist'); + cy.get('.euiBadge.euiBadge--hollow.euiBadge--iconRight.globalFilterItem').click(); + 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('.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', () =>{ + beforeEach(() => { + cy.visit('app/observability-dashboards#/trace_analytics/home', { + onBeforeLoad: (win) => { + win.sessionStorage.clear(); + }, + }); + setTimeFilter(); + }); + + it('Verify Change all filters', () =>{ + cy.get('.euiButtonIcon.euiButtonIcon--primary.euiButtonIcon--empty.euiButtonIcon--xSmall').click(); + cy.get('.euiContextMenuPanelTitle').contains('Change all filters').should('exist'); + cy.get('.euiContextMenuItem__text').eq(0).contains('Enable all'); + cy.get('.euiContextMenuItem__text').eq(1).contains('Disable all'); + cy.get('.euiContextMenuItem__text').eq(2).contains('Invert inclusion'); + cy.get('.euiContextMenuItem__text').eq(3).contains('Invert enabled/disabled'); + cy.get('.euiContextMenuItem__text').eq(4).contains('Remove all'); + }) + + it('Verify Add filter section', () => { + cy.get('.euiPopover.euiPopover--anchorDownLeft').contains('+ Add filter').click(); + cy.get('.euiPopoverTitle').contains('Add filter').should('exist'); + cy.wait(delay); + cy.get('.euiComboBox__inputWrap.euiComboBox__inputWrap--noWrap').eq(0).trigger('mouseover').click(); + cy.get('.euiComboBoxOption__content').eq(1).click(); + cy.get('.euiComboBox__inputWrap.euiComboBox__inputWrap--noWrap').eq(1).trigger('mouseover').click(); + cy.get('.euiComboBoxOption__content').eq(2).click(); + cy.get('.euiButton.euiButton--primary.euiButton--fill').contains('Save').click(); + cy.get('.euiBadge__content').should('exist').click(); + 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(); + }) +}); diff --git a/.cypress/integration/5_trace_analytics_services.spec.js b/.cypress/integration/5_trace_analytics_services.spec.js index b03e10e64..bd2cd4c41 100644 --- a/.cypress/integration/5_trace_analytics_services.spec.js +++ b/.cypress/integration/5_trace_analytics_services.spec.js @@ -5,7 +5,7 @@ /// -import { delay, SERVICE_NAME, SERVICE_SPAN_ID, setTimeFilter } from '../utils/constants'; +import { delay, SERVICE_NAME, SERVICE_SPAN_ID, setTimeFilter, verify_traces_spans_data_grid_cols_exists, count_table_row } from '../utils/constants'; describe('Testing services table empty state', () => { beforeEach(() => { @@ -46,6 +46,28 @@ describe('Testing services table', () => { cy.contains(' (1)').should('exist'); cy.contains('3.57%').should('exist'); }); + + it('Verify columns in Services table', () => { + cy.get('.euiFlexItem.euiFlexItem--flexGrow10 .panel-title').contains('Services').should('exist'); + cy.get('.euiTableCellContent__text[title="Name"]').should('exist'); + cy.get('.euiTableCellContent__text[title="Average latency (ms)"]').should('exist'); + cy.get('.euiTableCellContent__text[title="Error rate"]').should('exist'); + cy.get('.euiTableCellContent__text[title="Throughput"]').should('exist'); + cy.get('.euiTableCellContent__text[title="No. of connected services"]').should('exist'); + cy.get('.euiTableCellContent__text[title="Connected services"]').should('exist'); + cy.get('.euiTableCellContent__text[title="Traces"]').should('exist'); + cy.get('[data-test-subj="tablePaginationPopoverButton"]').click(); + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiContextMenu__icon').eq(0).should('exist').click(); + cy.get('[data-test-subj="pagination-button-next"]').should('exist').click(); + cy.get('.euiLink.euiLink--primary').contains('order').should('exist'); + }) + + it('Navigate from Services to Traces', () => { + cy.get('.euiTableCellContent__text[title="Traces"]').should('exist'); + cy.contains('74').should('exist').click(); + cy.get('.euiText.euiText--medium .panel-title').should('exist'); + cy.get('.euiBadge__childButton[data-test-subj="filterBadge"]').should('exist'); + }) }); describe('Testing service view empty state', () => { @@ -115,9 +137,143 @@ describe('Testing service view', () => { cy.get('.euiTextColor').contains('Span ID').trigger('mouseover'); cy.get('.euiButtonIcon[aria-label="span-flyout-filter-icon"').click({ force: true }); cy.wait(delay); - cy.get('.euiBadge__text').contains('spanId: ').should('exist'); cy.get('[data-test-subj="euiFlyoutCloseButton"]').click({ force: true }); cy.contains('Spans (1)').should('exist'); }); }); + +describe('Testing Service map', () => { + beforeEach(() => { + cy.visit('app/observability-dashboards#/trace_analytics/services', { + onBeforeLoad: (win) => { + win.sessionStorage.clear(); + }, + }); + setTimeFilter(); + }); + + it('Render Service map', () => { + cy.get('.euiText.euiText--medium .panel-title').contains('Service map'); + cy.get('[data-test-subj="latency"]').should('exist'); + cy.get('.ytitle').contains('Latency (ms)'); + cy.get('[data-text = "Error rate"]').click(); + cy.contains('60%'); + cy.get('[data-text = "Throughput"]').click(); + cy.contains('100'); + cy.get('.euiText.euiText--medium').contains('Focus on').should('exist'); + cy.get('[placeholder="Service name"]').focus().type('database{enter}'); + }) +}); + +describe('Testing traces Spans table verify table headers functionality', () => { + beforeEach(() => { + cy.visit('app/observability-dashboards#/trace_analytics/services', { + onBeforeLoad: (win) => { + win.sessionStorage.clear(); + }, + }); + setTimeFilter(); + }); + + it('Renders the spans table and verify columns headers', () => { + cy.contains(' (8)').should('exist'); + cy.contains('analytics-service, frontend-client, recommendation').should('exist'); + cy.get('.euiLink.euiLink--primary').contains('authentication').should('exist').click(); + cy.get('.panel-title').contains('Spans').should('exist'); + cy.get('.panel-title-count').contains('5').should('exist'); + verify_traces_spans_data_grid_cols_exists(); + }); + + it('Toggle columns and verify the columns hidden text verify rows', () => { + cy.get('.euiLink.euiLink--primary').contains('authentication').should('exist').click(); + cy.get('[data-test-subj = "dataGridColumnSelectorButton"]').click(); + cy.get('.euiSwitch.euiSwitch--compressed.euiSwitch--mini .euiSwitch__button').eq(3).click(); + cy.get('.euiButtonEmpty__text').eq(3).click().should('have.text', '2 columns hidden'); + count_table_row(5); + }); + + it('Show all button Spans table', () => { + cy.get('.euiLink.euiLink--primary').contains('authentication').should('exist').click(); + cy.get('[data-test-subj = "dataGridColumnSelectorButton"]').click(); + cy.get('.euiPopoverFooter .euiFlexItem.euiFlexItem--flexGrowZero').eq(0).should('have.text', 'Show all').click(); + cy.get('.euiDataGrid__focusWrap').click().should('exist'); + verify_traces_spans_data_grid_cols_exists(); + }); + + it('Hide all button Spans table', () => { + cy.get('.euiLink.euiLink--primary').contains('authentication').should('exist').click(); + cy.get('[data-test-subj = "dataGridColumnSelectorButton"]').click(); + cy.get('.euiPopoverFooter .euiFlexItem.euiFlexItem--flexGrowZero').eq(1).should('have.text', 'Hide all').click(); + cy.get('.euiDataGrid__focusWrap').click().should('exist'); + cy.get('[data-test-subj="dataGridColumnSelectorPopover"]').should('have.text', '10 columns hidden'); + }); + + it('Render Spans table and change data table Density', () => { + cy.get('.euiLink.euiLink--primary').contains('authentication').should('exist').click(); + verify_traces_spans_data_grid_cols_exists(); + cy.get('.euiButtonEmpty__text').contains('Density').click(); + cy.get('.euiButtonContent__icon').eq(5).click(); + cy.get('.euiButtonContent__icon').eq(6).click(); + cy.get('.euiButtonContent__icon').eq(7).click(); + }); + + it('Render Spans table and and click on sort', () => { + cy.get('.euiLink.euiLink--primary').contains('authentication').should('exist').click(); + verify_traces_spans_data_grid_cols_exists(); + cy.get('[data-test-subj="dataGridColumnSortingButton"]').contains('Sort fields').should('exist').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('.euiButtonEmpty__text').eq(5).contains('8 fields sorted').should('exist'); + cy.get('[data-test-subj="dataGridColumnSortingPopoverColumnSelection"]').click(); + cy.get('[data-test-subj="dataGridColumnSortingButton"]').should('exist').click(); + }); +}); + + +describe('Testing traces Spans table and verify columns functionality', () => { + beforeEach(() => { + cy.visit('app/observability-dashboards#/trace_analytics/services', { + onBeforeLoad: (win) => { + win.sessionStorage.clear(); + }, + }); + setTimeFilter(); + }); + + it('Renders the spans table and click on first span to verify details', () => { + cy.get('.euiLink.euiLink--primary').contains('authentication').should('exist').click(); + verify_traces_spans_data_grid_cols_exists(); + cy.get('.euiLink--primary').eq(4).click(); + cy.get('[data-test-subj="spanDetailFlyout"] .euiTitle.euiTitle--medium').contains('Span detail').should('exist'); + cy.get('.euiFlyoutBody .panel-title').contains('Overview').should('exist'); + cy.get('.euiTextColor.euiTextColor--subdued').contains('Span ID').should('exist'); + cy.get('.euiDescriptionList__description .euiFlexItem').eq(0).contains('d03fecfa0f55b77c').should('exist'); + cy.get('.euiFlyoutBody__overflowContent .panel-title').contains('Span attributes').should('exist'); + cy.get('.euiDescriptionList__description .euiFlexItem').eq(0).trigger('mouseover').click(); + cy.get('[aria-label="span-flyout-filter-icon"]').click(); + cy.get('.euiFlyout__closeButton.euiFlyout__closeButton--inside').click(); + cy.get('.euiBadge__content .euiBadge__text').contains('spanId: d03fecfa0f55b77c').should('exist'); + count_table_row(1); + cy.get('[aria-label="remove current filter"]').click(); + count_table_row(5); + }); + + it('Render Spans table and verify Column functionality', () => { + cy.get('.euiLink.euiLink--primary').contains('authentication').should('exist').click(); + verify_traces_spans_data_grid_cols_exists(); + cy.get('.euiDataGridHeaderCell__content').contains('Span ID').click(); + cy.get('.euiListGroupItem__label').contains('Hide column').click(); + cy.get('.euiDataGridHeaderCell__content').contains('Trace ID').click(); + cy.get('.euiListGroupItem__label').contains('Sort A-Z').click(); + cy.get('.euiDataGridHeaderCell__content').contains('Trace group').click(); + cy.get('.euiListGroupItem__label').contains('Move left').click(); + }); +}); diff --git a/.cypress/integration/6_trace_analytics_traces.spec.js b/.cypress/integration/6_trace_analytics_traces.spec.js index d8c5735fc..14f71ec10 100644 --- a/.cypress/integration/6_trace_analytics_traces.spec.js +++ b/.cypress/integration/6_trace_analytics_traces.spec.js @@ -113,3 +113,43 @@ describe('Testing trace view', () => { cy.contains('Spans (1)').should('exist'); }); }); + +describe('Testing traces table', () => { + beforeEach(() => { + cy.visit('app/observability-dashboards#/trace_analytics/traces', { + onBeforeLoad: (win) => { + win.sessionStorage.clear(); + }, + }); + setTimeFilter(); + }); + + it('Renders the traces table and verify Table Column, Pagination and Rows Data ', () => { + cy.get('.euiTableCellContent__text').contains('Trace ID').should('exist'); + cy.get('.euiTableCellContent__text').contains('Trace group').should('exist'); + cy.get('.euiTableCellContent__text').contains('Latency (ms)').should('exist'); + cy.get('.euiTableCellContent__text').contains('Percentile in trace group').should('exist'); + cy.get('.euiTableCellContent__text').contains('Errors').should('exist'); + cy.get('.euiTableCellContent__text').contains('Last updated').should('exist'); + cy.get('[data-test-subj="pagination-button-next"]').click(); + cy.contains('client_pay_order').should('exist'); + cy.get('[data-test-subj="pagination-button-previous"]').click(); + cy.contains('224.99').should('exist'); + cy.get('.euiButtonEmpty').contains('5').click(); + cy.contains('690d3c7af1a78cf89c43e...').should('exist'); + cy.contains('5be8370207cbb002a165d...').click(); + cy.contains('client_create_order').should('exist'); + cy.get('path[style*="rgb(116, 146, 231)"]').should('exist'); + cy.go('back'); + cy.wait(delay); + cy.get('.euiButtonEmpty__text').contains('Rows per page').click(); + cy.get('.euiContextMenuItem__text').contains('15 rows').click(); + let expected_row_count=15; + cy.get('.euiTable--auto') + .find("tr") + .then((row) => { + let total=row.length-1; + expect(total).to.equal(expected_row_count); + }); + }); +}); diff --git a/.cypress/integration/VisualizationCharts/10_scatter_chart.spec.js b/.cypress/integration/VisualizationCharts/10_scatter_chart.spec.js new file mode 100644 index 000000000..d84e23b00 --- /dev/null +++ b/.cypress/integration/VisualizationCharts/10_scatter_chart.spec.js @@ -0,0 +1,228 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { + delay, + TEST_QUERIES, + querySearch, + landOnEventVisualizations + } from '../../utils/event_constants'; + + const numberOfWindow = 4; + const legendSize = 20; + const pointSize = 30; + const pointSizeUpdated = 35; + const lineWidth = 7; + const lineWidthUpdated = 9; + const fillOpacity = 10; + const fillOpacityUpdated = 50; + const rotateLevel = 45; + const thresholdValue = 50; + + const renderScatterChart = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[6].query, TEST_QUERIES[6].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('scatter').type('{enter}'); + }; + + describe('Render scatter chart and verify default behaviour ', () => { + beforeEach(() => { + renderScatterChart(); + }); + + it('Render scatter chart and verify by default the data gets render', () => { + cy.get('.xy').should('exist'); + }); + + it('Render scatter chart and verify you see data configuration panel and chart panel', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Dimensions').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Metrics').should('exist'); + cy.get('.euiIEFlexWrapFix').contains('Panel options').click(); + cy.get('.euiIEFlexWrapFix').contains('Legend').click(); + cy.get('.euiIEFlexWrapFix').contains('Chart styles').click(); + cy.get('.euiIEFlexWrapFix').contains('Color theme').click(); + cy.get('.euiIEFlexWrapFix').contains('Thresholds').click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); + + it('Render scatter chart and verify the data configuration panel and chart panel are collapsable', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); + }); + + describe('Render scatter chart for data configuration panel', () => { + beforeEach(() => { + renderScatterChart(); + }); + + it('Render scatter chart and verify data config panel', () => { + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(0).should('contain', 'span(timestamp,1d)'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(1).should('contain', 'count()'); + }); + + it('Render scatter chart and verify data config panel no result found if metric is missing', () => { + cy.get('[data-test-subj="comboBoxClearButton"]').eq(1).click(); + cy.get('.euiTextColor.euiTextColor--subdued').contains('No results found').should('exist'); + cy.get('.euiComboBoxOption__content').contains('count()').click(); + cy.get('.main-svg').contains('No results found').should('not.exist'); + }); + }); + + describe('Render scatter chart for panel options', () => { + beforeEach(() => { + renderScatterChart(); + }); + + it('Render scatter chart and verify the title gets updated according to user input ', () => { + cy.get('input[name="title"]').type("scatter Chart"); + cy.get('textarea[name="description"]').should('exist').click(); + cy.get('.gtitle').contains('scatter Chart').should('exist'); + }); + }); + + describe('Render scatter chart for legend', () => { + beforeEach(() => { + renderScatterChart(); + }); + + it('Render scatter chart and verify legends for Show and Hidden', () => { + cy.get('[data-text="Show"]').should('have.text', 'Show'); + cy.get('[data-text="Show"] [data-test-subj="show"]').should('have.attr', 'checked'); + cy.get('[data-text="Hidden"]').should('have.text', 'Hidden').click(); + cy.get('[data-text="Hidden"] [data-test-subj="hidden"]').should('not.have.attr', 'checked'); + cy.get('[data-unformatted="max(bytes)"]').should('not.exist'); + }); + + it('Render scatter chart and verify legends for position Right and Bottom', () => { + cy.get('[data-text="Right"] [data-test-subj="v"]').should('have.attr', 'checked'); + cy.get('[data-text="Bottom"]').should('have.text', 'Bottom').click(); + cy.get('[data-text="Bottom"] [data-test-subj="h"]').should('not.have.attr', 'checked'); + }); + + it('Render scatter chart and increase Legend Size', () => { + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).click().type(legendSize); + cy.get('textarea[name="description"]').should('exist').click(); + cy.get('.legendtext').should('have.css', 'font-size', '20px'); + }); + }); + + describe('Render scatter chart for Chart Styles ', () => { + beforeEach(() => { + renderScatterChart(); + }); + + it('Render ltime serires and verify chart style of Marker Mode', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('scatter chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for scatter chart with chart style of Points'); + cy.get('[data-text="Marker"]').should('have.text', 'Marker').click(); + cy.get('[data-text="Marker"] [data-test-subj="markers"]').should('have.attr', 'checked'); + + }); + + it('Render scatter chart and verify chart style of Marker Mode with larger Point size', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('scatter chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for scatter chart with chart style of Points'); + cy.get('[data-text="Marker"]').should('have.text', 'Marker').click(); + cy.get('[data-text="Marker"] [data-test-subj="markers"]').should('have.attr', 'checked'); + cy.get('input[type="range"]') + .then($el => $el[0].stepUp(pointSize)) + .trigger('change') + cy.get('.euiRangeSlider').should('have.value', pointSizeUpdated) + + }); + + it('Render scatter chart and verify chart style of Lines+Marker Mode', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('scatter chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for scatter chart with chart style of Lines and Marker'); + cy.get('[data-text="Lines + Markers"]').should('have.text', 'Lines + Markers').click(); + cy.get('[data-text="Lines + Markers"] [data-test-subj="lines+markers"]').should('not.have.attr', 'checked'); + + }); + + it('Render scatter chart and verify chart style of Lines+Marker Mode with Line Width, Fill Opacity and Point Size', () => { + cy.get('#configPanel__panelOptions .euiFieldText').click().type('scatter chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]').click().type('This is the description for scatter chart with chart style of Lines and Marker'); + cy.get('[data-text="Lines + Markers"]').should('have.text', 'Lines + Markers').click(); + cy.get('[data-text="Lines + Markers"] [data-test-subj="lines+markers"]').should('not.have.attr', 'checked'); + cy.get('input[type="range"]').eq(0) + .then($el => $el[0].stepUp(lineWidth)) + .trigger('change') + cy.get('.euiRangeSlider').eq(0).should('have.value', lineWidthUpdated) + cy.get('input[type="range"]').eq(1) + .then($el => $el[0].stepUp(fillOpacity)) + .trigger('change') + cy.get('.euiRangeSlider').eq(1).should('have.value', fillOpacityUpdated) + cy.get('input[type="range"]').eq(2) + .then($el => $el[0].stepUp(pointSize)) + .trigger('change') + cy.get('.euiRangeSlider').eq(2).should('have.value', pointSizeUpdated) + cy.get('input[type="range"]').eq(3) + .then($el => $el[0].stepUp(rotateLevel)) + .trigger('change') + cy.get('.euiRangeSlider').eq(3).should('have.value', rotateLevel) + }); + }); + + describe('Render scatter chart for color theme', () => { + beforeEach(() => { + renderScatterChart(); + }); + + it('Render scatter chart and "Add Color theme"', () => { + cy.get('.euiButton__text').contains('+ Add color theme').click(); + cy.wait(delay); + cy.get('[data-test-subj="comboBoxInput"]').eq(5).click(); + cy.get('.euiComboBoxOption__content').contains('count()').click(); + cy.get('path[style*="rgb(252, 5, 5)"]').should('exist'); + + }); + }); + + describe('Render scatter chart and work with Thresholds', () => { + beforeEach(() => { + renderScatterChart(); + }); + + it('Render scatter chart and add threshold', () => { + cy.get('.euiButton__text').contains('+ Add threshold').click(); + cy.get('[data-test-subj="nameFieldText"]').type('scatter chart Threshold'); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).type(thresholdValue); + cy.get('[data-unformatted="scatter chart Threshold"]').should('be.visible'); + cy.get('path[style*="rgb(252, 5, 5)"]').should('exist'); + }); + }); + + describe('Render scatter chart and verify if reset works properly', () => { + beforeEach(() => { + renderScatterChart(); + }); + + it('Render scatter chart with all feild data then click on reset and verify reset works properly', () => { + cy.get('input[placeholder="Title"]').type('scatter chart'); + cy.get('textarea[placeholder="Description"]').type('Description For scatter chart'); + cy.get('[data-text="Hidden"]').should('have.text', 'Hidden').click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).click().type(legendSize); + cy.get('.euiButton__text').contains('+ Add color theme').click(); + cy.wait(delay); + cy.get('[data-test-subj="comboBoxInput"]').eq(5).click(); + cy.get('.euiComboBoxOption__content').contains('count()').click(); + cy.get('.euiButton__text').contains('+ Add threshold').click(); + cy.get('[data-test-subj="nameFieldText"]').type('scatter chart Threshold'); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).type(thresholdValue); + cy.get('.euiButtonEmpty__text').contains('Reset').click(); + cy.get('input[placeholder="Title"]').should('not.have.value', 'scatter chart'); + cy.get('textarea[placeholder="Description"]').should('not.have.value', 'Description For scatter chart') + cy.get('[data-text="Show"] [data-test-subj="show"]').should('have.attr', 'checked'); + cy.get('[data-text="Right"] [data-test-subj="v"]').should('have.attr', 'checked'); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).should('have.value', ''); + }); + }); + \ No newline at end of file diff --git a/.cypress/integration/VisualizationCharts/11_horizontalBar_chart.spec.js b/.cypress/integration/VisualizationCharts/11_horizontalBar_chart.spec.js new file mode 100644 index 000000000..a93030962 --- /dev/null +++ b/.cypress/integration/VisualizationCharts/11_horizontalBar_chart.spec.js @@ -0,0 +1,244 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { + delay, + TEST_QUERIES, + querySearch, + landOnEventVisualizations, + } from '../../utils/event_constants'; + + const numberOfWindow = 4; + const labelSize = 20; + const rotateLevel = 45; + const groupWidth = 10; + const groupWidthUpdated = 0.8; + const barWidth = 10; + const barWidthUpdated = 80; + const lineWidth = 7; + const lineWidthUpdated = 8; + const fillOpacity = 10; + const fillOpacityUpdated = 90; + const numberOfColor = 24; + + const renderHorizontalBarChart = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[4].query, TEST_QUERIES[4].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Horizontal Bar') + .type('{enter}'); + }; + + describe('Render horizontal bar chart and verify default behaviour ', () => { + beforeEach(() => { + renderHorizontalBarChart(); + }); + + it('Render horizontal bar chart and verify by default the data gets render', () => { + cy.get('.xy').should('exist'); + }); + + it('Render horizontal bar chart and verify you see data configuration panel and chart panel', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Dimensions').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Metrics').should('exist'); + cy.get('.euiIEFlexWrapFix').contains('Panel options').click(); + cy.get('.euiIEFlexWrapFix').contains('Legend').click(); + cy.get('.euiIEFlexWrapFix').contains('Chart styles').click(); + cy.get('.euiIEFlexWrapFix').contains('Color theme').click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); + + it('Render horizontal bar chart and verify the data configuration panel and chart panel are collapsable', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); + }); + + describe('Render horizontal bar chart for data configuration panel', () => { + beforeEach(() => { + renderHorizontalBarChart(); + }); + + it('Render horizontal bar chart and verify data config panel', () => { + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(0).should('contain', 'tags'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(1).should('contain', 'count()'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(2).should('contain', 'avg(bytes)'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(3).should('contain', 'host'); + }); + + it('Render horizontal bar chart and verify data config panel restrict user to select a duplicate field on dimension field', () => { + cy.get('[data-test-subj="comboBoxInput"]').eq(1).click(); + cy.get('[data-test-subj="comboBoxClearButton"]').eq(0).click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(1).click(); + cy.get('.euiComboBoxOption__content').should('have.length', 1); + cy.get('.euiComboBoxOption__content').contains('tags'); + }); + + it('Render horizontal bar chart and verify data config panel Restrict user to select a duplicate field on Metrics field', () => { + cy.get('.euiText.euiText--extraSmall').eq(1).click(); + cy.get('[data-test-subj="comboBoxClearButton"]').eq(1).click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(3).click(); + cy.get('.euiComboBoxOption__content').should('not.contain', 'tags'); + cy.get('.euiComboBoxOption__content').should('have.length', 2); + }); + + it('Render horizontal bar chart and verify data config panel no result found if metric is missing', () => { + cy.get('.euiText.euiText--extraSmall').eq(0).click(); + cy.get('.euiText.euiText--extraSmall').eq(1).click(); + cy.get('[data-test-subj="comboBoxClearButton"]').eq(1).click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(0).click(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.euiTextColor.euiTextColor--subdued').contains('No results found').should('exist'); + cy.get('[data-test-subj="comboBoxInput"]').eq(3).click(); + cy.get('.euiComboBoxOption__content').contains('avg(bytes)').click(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.main-svg').contains('No results found').should('not.exist'); + }); + }); + + describe('Render horizontal bar chart for panel options', () => { + beforeEach(() => { + renderHorizontalBarChart(); + }); + + it('Render horizontal bar chart and verify the title gets updated according to user input ', () => { + cy.get('input[name="title"]').type('horizontal bar chart'); + cy.get('textarea[name="description"]').should('exist').click(); + cy.get('.gtitle').contains('horizontal bar chart').should('exist'); + }); + }); + + describe('Render horizontal bar chart for legend', () => { + beforeEach(() => { + renderHorizontalBarChart(); + }); + + it('Render horizontal bar chart and verify legends for Show and Hidden', () => { + cy.get('[data-text="Show"]').should('have.text', 'Show'); + cy.get('[data-text="Show"] [data-test-subj="show"]').should('have.attr', 'checked'); + cy.get('[data-text="Hidden"]').should('have.text', 'Hidden').click(); + cy.get('[data-text="Hidden"] [data-test-subj="hidden"]').should('not.have.attr', 'checked'); + cy.get('[data-unformatted="max(bytes)"]').should('not.exist'); + }); + + it('Render horizontal bar chart and verify legends for position Right and Bottom', () => { + cy.get('[data-text="Right"]').should('have.text', 'Right'); + cy.get('[data-text="Right"] [data-test-subj="v"]').should('have.attr', 'checked'); + cy.get('[data-text="Bottom"]').should('have.text', 'Bottom').click(); + cy.get('[data-text="Bottom"] [data-test-subj="h"]').should('not.have.attr', 'checked'); + }); + }); + + describe('Render horizontal bar chart for chart style options', () => { + beforeEach(() => { + renderHorizontalBarChart(); + }); + + it('Render horizontal bar chart and increase Label Size ', () => { + cy.get('[data-test-subj="valueFieldNumber"]').click().type(labelSize); + cy.get('textarea[name="description"]').should('exist').click(); + cy.get('[data-unformatted="login"]').should('have.css', 'font-size', '20px'); + }); + + it('Render horizontal bar chart and "Rotate bar labels"', () => { + cy.get('input[type="range"]') + .eq(0) + .then(($el) => $el[0].stepUp(rotateLevel)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(0).should('have.value', rotateLevel); + }); + + it('Render horizontal bar chart and change "Group Width"', () => { + cy.get('input[type="range"]') + .eq(1) + .then(($el) => $el[0].stepUp(groupWidth)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(1).should('have.value', groupWidthUpdated); + }); + + it('Render horizontal bar chart and change "Bar Width"', () => { + cy.get('input[type="range"]') + .eq(2) + .then(($el) => $el[0].stepDown(barWidth)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(4).should('have.value', barWidthUpdated); + }); + + it('Render horizontal bar chart and change "Line Width"', () => { + cy.get('input[type="range"]') + .eq(3) + .then(($el) => $el[0].stepUp(lineWidth)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(3).should('have.value', lineWidthUpdated); + }); + + it('Render horizontal bar chart and change "Fill Opacity"', () => { + cy.get('input[type="range"]') + .eq(4) + .then(($el) => $el[0].stepUp(fillOpacity)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(4).should('have.value', fillOpacityUpdated); + }); + }); + + describe('Render horizontal bar chart for color theme', () => { + beforeEach(() => { + renderHorizontalBarChart(); + }); + + it('Render horizontal bar chart and "Add color theme"', () => { + cy.get('.euiButton__text').contains('+ Add color theme').click(); + cy.wait(delay); + cy.get('[data-test-subj="comboBoxInput"]').eq(9).click(); + cy.get('.euiComboBoxOption__content').contains('avg(bytes)').click(); + cy.get('.point').find('path[style*="rgb(252, 5, 5)"]').should('have.length', numberOfColor); + }); + }); + + describe('Render horizontal bar chart and verify if reset works properly', () => { + beforeEach(() => { + renderHorizontalBarChart(); + }); + + it('Render horizontal bar chart with all feild data then click on reset and verify reset works properly', () => { + cy.get('input[placeholder="Title"]').type('horizontal bar chart'); + cy.get('textarea[placeholder="Description"]').type('Description For horizontal bar chart'); + cy.get('.euiButton__text').contains('Hidden').click(); + cy.get('.euiButton__text').contains('Stack').click(); + cy.get('[data-test-subj="valueFieldNumber"]').click().type(labelSize); + cy.get('input[type="range"]') + .eq(0) + .then(($el) => $el[0].stepUp(rotateLevel)) + .trigger('change'); + cy.get('input[type="range"]') + .eq(1) + .then(($el) => $el[0].stepUp(groupWidth)) + .trigger('change'); + cy.get('input[type="range"]') + .eq(2) + .then(($el) => $el[0].stepDown(barWidth)) + .trigger('change'); + cy.get('input[type="range"]') + .eq(3) + .then(($el) => $el[0].stepUp(lineWidth)) + .trigger('change'); + cy.get('.euiButtonEmpty__text').contains('Reset').click(); + cy.get('input[placeholder="Title"]').should('not.have.value', 'horizontal bar chart'); + cy.get('textarea[placeholder="Description"]').should( + 'not.have.value', + 'Description For horizontal bar chart' + ); + cy.get('[data-text="Show"] [data-test-subj="show"]').should('have.attr', 'checked'); + cy.get('[data-text="Right"] [data-test-subj="v"]').should('have.attr', 'checked'); + cy.get('[data-text="Vertical"] [data-test-subj="v"]').should('have.attr', 'checked'); + cy.get('[data-text="Group"] [data-test-subj="group"]').should('have.attr', 'checked'); + cy.get('[data-test-subj="valueFieldNumber"]').should('have.value', ''); + }); + }); diff --git a/.cypress/integration/VisualizationCharts/12_boxPlot_chart.spec.js b/.cypress/integration/VisualizationCharts/12_boxPlot_chart.spec.js new file mode 100644 index 000000000..d4dae465e --- /dev/null +++ b/.cypress/integration/VisualizationCharts/12_boxPlot_chart.spec.js @@ -0,0 +1,248 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { + delay, + TEST_QUERIES, + querySearch, + landOnEventVisualizations, +} from '../../utils/event_constants'; + +const numberOfWindow = 4; +const labelSize = 20; +const rotateLevel = 45; +const boxSize = 7; +const boxSizeUpdated = 1; +const markerSize = 5; +const markerSizeUpdated = 1; +const jitter = 2; +const jitterUpdated = .1; +const fillOpacity = 10; +const fillOpacityUpdated = 50; +const numberOfColor = 24; + +const renderBoxPlot = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[4].query, TEST_QUERIES[4].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Box plot') + .type('{enter}'); +}; + +describe('Render box plot and verify default behaviour ', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot and verify by default the data gets render', () => { + cy.get('.xy').should('exist'); + }); + + it('Render box plot and verify you see data configuration panel and chart panel', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Dimensions').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Metrics').should('exist'); + cy.get('.euiIEFlexWrapFix').contains('Panel options').click(); + cy.get('.euiIEFlexWrapFix').contains('Legend').click(); + cy.get('.euiIEFlexWrapFix').contains('Chart styles').click(); + cy.get('.euiIEFlexWrapFix').contains('Color Theme').click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); + + it('Render box plot and verify the data configuration panel and chart panel are collapsable', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); +}); + +describe('Render box plot for data configuration panel', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot and verify data config panel', () => { + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(0).should('contain', 'tags'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(1).should('contain', 'count()'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(2).should('contain', 'avg(bytes)'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(3).should('contain', 'host'); + }); + + it('Render box plot and verify data config panel restrict user to select a duplicate field on dimension field', () => { + cy.get('[data-test-subj="comboBoxInput"]').eq(1).click(); + cy.get('[data-test-subj="comboBoxClearButton"]').eq(0).click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(1).click(); + cy.get('.euiComboBoxOption__content').should('have.length', 1); + cy.get('.euiComboBoxOption__content').contains('tags'); + }); + + it('Render box plot and verify data config panel Restrict user to select a duplicate field on Metrics field', () => { + cy.get('.euiText.euiText--extraSmall').eq(1).click(); + cy.get('[data-test-subj="comboBoxClearButton"]').eq(1).click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(3).click(); + cy.get('.euiComboBoxOption__content').should('not.contain', 'tags'); + cy.get('.euiComboBoxOption__content').should('have.length', 2); + }); + + it('Render box plot and verify data config panel no result found if metric is missing', () => { + cy.get('.euiText.euiText--extraSmall').eq(0).click(); + cy.get('.euiText.euiText--extraSmall').eq(1).click(); + cy.get('[data-test-subj="comboBoxClearButton"]').eq(1).click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(0).click(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.euiTextColor.euiTextColor--subdued').contains('No results found').should('exist'); + cy.get('[data-test-subj="comboBoxInput"]').eq(3).click(); + cy.get('.euiComboBoxOption__content').contains('avg(bytes)').click(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.main-svg').contains('No results found').should('not.exist'); + }); +}); + +describe('Render box plot for panel options', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot and verify the title gets updated according to user input ', () => { + cy.get('input[name="title"]').type('box plot'); + cy.get('textarea[name="description"]').should('exist').click(); + cy.get('.gtitle').contains('box plot').should('exist'); + }); +}); + +describe('Render box plot for legend', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot and verify legends for Show and Hidden', () => { + cy.get('[data-text="Show"]').should('have.text', 'Show'); + cy.get('[data-text="Show"] [data-test-subj="show"]').should('have.attr', 'checked'); + cy.get('[data-text="Hidden"]').should('have.text', 'Hidden').click(); + cy.get('[data-text="Hidden"] [data-test-subj="hidden"]').should('not.have.attr', 'checked'); + cy.get('[data-unformatted="max(bytes)"]').should('not.exist'); + }); + + it('Render box plot and verify legends for position Right and Bottom', () => { + cy.get('[data-text="Right"]').should('have.text', 'Right'); + cy.get('[data-text="Right"] [data-test-subj="v"]').should('have.attr', 'checked'); + cy.get('[data-text="Bottom"]').should('have.text', 'Bottom').click(); + cy.get('[data-text="Bottom"] [data-test-subj="h"]').should('not.have.attr', 'checked'); + }); +}); + +describe('Render box plot for chart style options', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot and increase Label Size ', () => { + cy.get('[data-test-subj="valueFieldNumber"]').click().type(labelSize); + cy.get('textarea[name="description"]').should('exist').click(); + cy.get('[data-unformatted="count()"]').should('have.css', 'font-size', '20px'); + }); + + it('Render box plot and "Rotate box labels"', () => { + cy.get('input[type="range"]') + .eq(0) + .then(($el) => $el[0].stepUp(rotateLevel)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(0).should('have.value', rotateLevel); + }); + + it('Render box plot and change "Box gap"', () => { + cy.get('input[type="range"]') + .eq(1) + .then(($el) => $el[0].stepUp(boxSize)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(1).should('have.value', boxSizeUpdated); + }); + + it('Render box plot and change "Marker size"', () => { + cy.get('input[type="range"]') + .eq(2) + .then(($el) => $el[0].stepDown(markerSize)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(2).should('have.value', markerSizeUpdated); + }); + + it('Render box plot and change "Jitter"', () => { + cy.get('input[type="range"]') + .eq(3) + .then(($el) => $el[0].stepDown(jitter)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(3).should('have.value', jitterUpdated); + }); + + it('Render box plot and change "Fill opacity"', () => { + cy.get('input[type="range"]') + .eq(4) + .then(($el) => $el[0].stepUp(fillOpacity)) + .trigger('change'); + cy.get('.euiRangeSlider').eq(4).should('have.value', fillOpacityUpdated); + }); +}); + +describe('Render box plot for color theme', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot and "Add color theme"', () => { + cy.get('.euiButton__text').contains('+ Add color theme').click(); + cy.wait(delay); + cy.get('[data-test-subj="comboBoxInput"]').eq(9).click(); + cy.get('.euiComboBoxOption__content').contains('avg(bytes)').click(); + cy.get('.points').find('path[style*="rgb(252, 5, 5)"]').should('have.length', numberOfColor); + }); +}); + +describe('Render box plot and verify if reset works properly', () => { + beforeEach(() => { + renderBoxPlot(); + }); + + it('Render box plot with all feild data then click on reset and verify reset works properly', () => { + cy.get('input[placeholder="Title"]').type('box plot'); + cy.get('textarea[placeholder="Description"]').type('Description For box plot'); + cy.get('.euiButton__text').contains('Hidden').click(); + cy.get('.euiButton__text').contains('Horizontal').click(); + cy.get('[data-test-subj="valueFieldNumber"]').click().type(labelSize); + cy.get('input[type="range"]') + .eq(0) + .then(($el) => $el[0].stepUp(rotateLevel)) + .trigger('change'); + cy.get('input[type="range"]') + .eq(1) + .then(($el) => $el[0].stepUp(boxSize)) + .trigger('change'); + cy.get('input[type="range"]') + .eq(2) + .then(($el) => $el[0].stepDown(markerSize)) + .trigger('change'); + cy.get('input[type="range"]') + .eq(3) + .then(($el) => $el[0].stepUp(jitter)) + .trigger('change'); + cy.get('input[type="range"]') + .eq(4) + .then(($el) => $el[0].stepUp(fillOpacity)) + .trigger('change'); + cy.get('.euiButtonEmpty__text').contains('Reset').click(); + cy.get('input[placeholder="Title"]').should('not.have.value', 'box plot'); + cy.get('textarea[placeholder="Description"]').should( + 'not.have.value', + 'Description For box plot' + ); + cy.get('[data-text="Show"] [data-test-subj="show"]').should('have.attr', 'checked'); + cy.get('[data-text="Right"] [data-test-subj="v"]').should('have.attr', 'checked'); + cy.get('[data-text="Vertical"] [data-test-subj="v"]').should('have.attr', 'checked'); + cy.get('[data-text="Overlay"] [data-test-subj="overlay"]').should('have.attr', 'checked'); + cy.get('[data-test-subj="valueFieldNumber"]').should('have.value', ''); + }); +}); diff --git a/.cypress/integration/VisualizationCharts/13_logsView.spec.js b/.cypress/integration/VisualizationCharts/13_logsView.spec.js new file mode 100644 index 000000000..542e3d776 --- /dev/null +++ b/.cypress/integration/VisualizationCharts/13_logsView.spec.js @@ -0,0 +1,241 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { + delay, + TEST_QUERIES, + querySearch, + landOnEventVisualizations, + saveVisualizationAndVerify, + deleteVisualization, +} from '../../utils/event_constants'; + +const renderLogsView = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[8].query, TEST_QUERIES[8].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Logs view') + .type('{enter}'); +}; + +const renderLogsViewChart = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[2].query, TEST_QUERIES[2].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Logs view') + .type('{enter}'); +}; + +const fieldName = 'host'; + +describe('Render Logs view and verify default behavior', () => { + beforeEach(() => { + renderLogsView(); + }); + + it('Render Logs view and verify the default data', () => { + cy.get('.logs-view-container').should('exist'); + }); + + it('Render Logs view and verify Data Configuration panel default behavior', () => { + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Columns'); + cy.get('.euiFormLabel.euiFormRow__label').contains('Field'); + cy.get('.euiButton.euiButton--primary.euiButton--fullWidth.euiButton-isDisabled').should( + 'be.disabled' + ); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').should('be.disabled'); + }); + + it('Render Logs view and verify Style section for Logs view', () => { + cy.get('.vis-config-tabs .euiTab__content').contains('Style').should('exist'); + cy.get('.euiAccordion__triggerWrapper').contains('Panel options').should('exist'); + cy.get('#configPanel__panelOptions .euiFormRow__labelWrapper') + .contains('Title') + .should('exist'); + cy.get('#configPanel__panelOptions .euiFormRow__labelWrapper') + .contains('Description') + .should('exist'); + }); + + it('Table view should be enabled for Logs view', () => { + cy.get('.euiSwitch__label').contains('Table view').should('exist'); + cy.get('[data-test-subj="workspace__dataTableViewSwitch"][aria-checked="false"]').click(); + cy.get('.ag-header.ag-pivot-off').should('exist'); + }); + + it('Verify Style section for Logs view', () => { + cy.get('#data-panel').contains('Style').should('exist'); + cy.get('.euiAccordion__button').contains('Panel options').should('exist'); + cy.get('#configPanel__panelOptions').contains('Title').should('exist'); + cy.get('.euiFormHelpText.euiFormRow__text').contains('Name your visualization').should('exist'); + cy.get('#configPanel__panelOptions').contains('Description').should('exist'); + }); + + it('Add and Remove toggle buttons for fields section', () => { + cy.get('#available_fields').contains('Available Fields').should('exist'); + cy.get('[aria-label="Add agent to table"]').should('be.disabled'); + cy.get('#selected_fields').contains('Query fields').should('exist'); + cy.get('[aria-label="Remove clientip from table"]').should('be.disabled'); + }); +}); + +describe('Save and Delete Visualization', () => { + beforeEach(() => { + renderLogsView(); + }); + + it('Render Logs view, Save and Delete Visualization', () => { + saveVisualizationAndVerify(); + deleteVisualization(); + }); +}); + +describe('Render Logs view with no stats section in the query', () => { + beforeEach(() => { + renderLogsViewChart(); + }); + + it('Disabled Table view toogle button', () => { + cy.get('[data-test-subj="workspace__dataTableViewSwitch"]').should('be.disabled'); + }); + + it('Save toast message', () => { + const vis_name_sub_string = Math.floor(Math.random() * 100); + cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); + cy.get('[data-test-subj="eventExplorer__querySaveComboBox"]').click(); + cy.get('.euiComboBoxOptionsList__rowWrap .euiFilterSelectItem').eq(0).click(); + cy.get( + '.euiPopover__panel .euiFormControlLayoutIcons [data-test-subj="comboBoxToggleListButton"]' + ) + .eq(0) + .click(); + cy.get('.euiPopover__panel input') + .eq(1) + .type(`Test visualization` + vis_name_sub_string); + cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); + cy.get('[data-test-subj="euiToastHeader"]') + .contains('There is no query or(and) visualization to save') + .should('exist'); + }); + + it('Verify Logs view details when PPL query does not have stats section ', () => { + cy.get('[data-test-subj="docTable"]').should('exist'); + cy.get('.osdDocTableHeader').contains('Time').should('exist'); + cy.get('.osdDocTableHeader').contains('_source').should('exist'); + }); + + it('Add and Remove toggle buttons for fields section should be enabled', () => { + //Add field + cy.get('[data-test-subj="fieldToggle-agent"]').click(); + cy.get('[data-test-subj="field-agent"]').should('exist'); + //Remove field + cy.get('[data-test-subj="fieldToggle-agent"]').click(); + cy.get('[aria-labelledby="selected_fields"] [data-test-subj="field-agent-showDetails"]').should( + 'not.exist' + ); + }); + + it('Search engine for fields under Visualizations', () => { + cy.get('[data-test-subj="eventExplorer__sidebarSearch"]').should('exist').type(fieldName); + cy.get('[data-test-subj="fieldToggle-host"]').click(); + cy.get('[data-test-subj="fieldList-selected"]').should('exist'); + }); + + it('View surrounding events button enabled', () => { + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + cy.get('.euiButton__text').eq(4).should('not.be.disabled'); + }); +}); + +describe('Event Details overlay', () => { + beforeEach(() => { + renderLogsView(); + }); + + it('Verify Event Details overaly should get opened after clicking on details toggle button', () => { + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + }); + + it('Options in Event Details overlay', () => { + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + cy.get('.euiTabs .euiTab__content').contains('Table'); + cy.get('.table.table-condensed.osdDocViewerTable').should('exist'); + cy.get('.euiTabs .euiTab__content').contains('JSON').click(); + cy.get('.euiCodeBlock__code.json').should('exist'); + cy.get('.euiTabs .euiTab__content').contains('Traces').click(); + cy.get('.euiCallOutHeader__title').contains('No Trace Id found in the event.').should('exist'); + cy.get('.euiLink.euiLink--primary').contains('Trace Analytics').click(); + cy.get('#trace-analytics').contains('Trace Analytics').should('exist'); + cy.get('.euiLink.euiLink--primary').contains('Log Correlation').click(); + cy.get('#log-correlation').contains('Log Correlation').should('exist'); + }); + + it('View surrounding events button disabled', () => { + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + cy.get('.euiButton.euiButton--primary.euiButton-isDisabled.header-button') + .contains('View surrounding events') + .should('be.disabled'); + }); + + it('Event Details overlay resizable and "X" buttons', () => { + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + cy.get('[title="Resize"]').click(); + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + cy.get('[data-test-subj="euiFlyoutCloseButton"]').should('exist').click(); + }); + + it('Table details on Event Details overlay', () => { + cy.get('.euiIcon.euiIcon--medium.euiIcon--inherit.euiButtonIcon__icon') + .eq(2) + .click({ force: true }); + cy.get('#eventsDocFyout').contains('Event Details').should('exist'); + cy.get('[data-test-subj="docTable"]').should('exist'); + }); +}); + +describe('Data Configuration panel when no stats in the query', () => { + beforeEach(() => { + renderLogsViewChart(); + }); + + it('Data Configuration panel default behavior', () => { + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Columns').should('exist'); + cy.get('.euiButton__text').eq(2).contains('Add').should('not.be.disabled'); + cy.get('.euiButton__text').eq(3).contains('Update Chart').should('not.be.disabled'); + }); + + it('Add button in Data Configuration panel', () => { + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Columns').should('exist'); + cy.get('.euiButton__text').eq(2).contains('Add').should('not.be.disabled').click(); + cy.get('.euiFormLabel.euiFormRow__label').contains('Field'); + cy.get('.euiIcon.euiIcon--medium.euiIcon--danger').should('exist'); + cy.get('[data-test-subj="comboBoxToggleListButton"]').eq(0).click(); + cy.get('.euiFlexItem.euiFilterSelectItem__content').eq(1).click(); + cy.get('.euiButton__text').contains('Update Chart').should('not.be.disabled').click(); + cy.get('[data-test-subj="docTable"]').should('exist'); + }); +}); diff --git a/.cypress/integration/VisualizationCharts/13_stats_chart.spec.js b/.cypress/integration/VisualizationCharts/13_stats_chart.spec.js new file mode 100644 index 000000000..43e071f1e --- /dev/null +++ b/.cypress/integration/VisualizationCharts/13_stats_chart.spec.js @@ -0,0 +1,211 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { + delay, + TEST_QUERIES, + querySearch, + landOnEventVisualizations +} from '../../utils/event_constants'; + +const numberOfWindow = 4; +const metricsPrecisionUpdated = 2; +const metricUnit = 'cm' ; +const titleSize = '25.5px'; +const titleSizeUpdated = '40px'; +const valueSize = '60.8px'; +const valueSizeUpdated = '73.0px'; + +const renderStatsChart = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[4].query, TEST_QUERIES[4].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Stats').type('{enter}'); +}; + +describe('Render stats chart and verify default behaviour ', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verify by default the data gets render', () => { + cy.get('.xy').should('exist'); + }); + + it('Render scatter chart and verify you see data configuration panel and chart panel', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Dimensions').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Metrics').should('exist'); + cy.get('.euiIEFlexWrapFix').contains('Panel options').click(); + cy.get('.euiIEFlexWrapFix').contains('Chart styles').click(); + cy.get('.euiIEFlexWrapFix').contains('Thresholds').click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); + + it('Render stats chart and verify the data configuration panel and chart panel are collapsable', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); +}); + +describe('Render stats chart for data configuration panel', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verify data config panel', () => { + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(0).should('contain', 'tags'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(1).should('contain', 'count()'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(2).should('contain', 'avg(bytes)'); + cy.get('.euiComboBoxPill.euiComboBoxPill--plainText').eq(3).should('contain', 'host'); + }); + + it('Render stats chart and verify no result found message if the dimension is removed' , () => { + cy.get('[data-test-subj="comboBoxClearButton"]').eq(0).click(); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); + cy.get('.euiTextColor.euiTextColor--subdued').contains('No results found').should('exist'); + cy.get('[data-test-subj="comboBoxInput"]').eq(1).click(); + cy.get('.euiComboBoxOption__content').click(); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click(); + cy.get('.main-svg').contains('No results found').should('not.exist'); + }); + + it('Render stats chart and verify data config panel no result found if metric is missing', () => { + cy.get('.euiText.euiText--extraSmall').eq(0).click(); + cy.get('.euiText.euiText--extraSmall').eq(1).click(); + cy.get('[data-test-subj="comboBoxClearButton"]').eq(1).click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(0).click(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.euiTextColor.euiTextColor--subdued').contains('No results found').should('exist'); + cy.get('[data-test-subj="comboBoxInput"]').eq(3).click(); + cy.get('.euiComboBoxOption__content').contains('avg(bytes)').click(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.main-svg').contains('No results found').should('not.exist'); + }); +}); + +describe('Render stats chart for panel options', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verify the title gets updated according to user input ', () => { + cy.get('input[name="title"]').type("stats chart"); + cy.get('textarea[name="description"]').should('exist').click(); + cy.get('.gtitle').contains('stats chart').should('exist'); + }); +}); + +describe('Render stats chart verfiy functionality for Tooltip mode', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verfiy the Show and Hidden Tooltip modes', () => { + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(0).should('have.text', 'Show'); + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(1).should('have.text', 'Hidden') + .click(); + }); +}); + +describe('Render stats chart verfiy functionality for Tooltip text', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verfiy the Tootltip text -> All , Dimension , Metric', () => { + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(2).should('have.text', 'All'); + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(3).should('have.text', 'Dimension') + .click(); + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(4).should('have.text', 'Series') + .click(); + }); +}); + +describe('Render stats chart for Chart Styles ', () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verify the various chart type selected', () => { + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(5).should('have.text', 'Auto'); + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(6).should('have.text', 'Horizontal').click(); + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(7).should('have.text', 'Text mode').click(); + }); + + it('Render stats chart and verify the various chart orientation selected', () => { + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(8).should('have.text', 'Auto'); + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(9).should('have.text', 'Horizontal').click(); + cy.get('.euiButton__text.euiButtonGroupButton__textShift').eq(10).should('have.text', 'Vertical').click(); + }); + + it('Render stats chart and verify Metric unit and Metric Precision on chart ', () => { + cy.get('[data-test-subj="valueFieldText"]').click().type(metricUnit); + cy.get('.euiSpacer.euiSpacer--s').eq(12).click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).click().type(metricsPrecisionUpdated); + cy.get('.euiSpacer.euiSpacer--s').eq(12).click(); + }); + + it('Render stats chart and verify behaviour for Title size and Value size on chart ', () => { + cy.get('.annotation-text').eq(0).should('have.css', 'font-size', titleSize); + cy.get('.annotation-text').eq(2).should('have.css', 'font-size', titleSize); + cy.get('.annotation-text').eq(4).should('have.css', 'font-size', titleSize); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).click().type(titleSizeUpdated); + cy.get('.euiSpacer.euiSpacer--s').eq(12).click(); + cy.get('.annotation-text').eq(1).should('have.css', 'font-size', valueSize); + cy.get('.annotation-text').eq(3).should('have.css', 'font-size', valueSize); + cy.get('.annotation-text').eq(5).should('have.css', 'font-size', valueSize); + cy.get('[data-test-subj="valueFieldNumber"]').eq(2).click().type(valueSizeUpdated); + cy.get('.euiSpacer.euiSpacer--s').eq(12).click(); + }); +}); + +describe('Render stats chart and verify the Text Mode options' , () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verify text modes ', () => { + cy.get('[data-text="Names"]').should('have.text', 'Names').click(); + cy.get('[data-text="Values"]').should('have.text', 'Values').click(); + cy.get('[data-text="Values + Names"]').should('have.text', 'Values + Names').click(); + cy.wait(delay); + }); +}); + +describe('Render stats chart and verify the +add threshold button option' , () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and verify the +Add Threshold button for color picker' , () => { + cy.get('[data-test-subj="euiColorPickerAnchor"]').click(); + cy.get('.euiColorPickerSwatch.euiColorPicker__swatchSelect').eq(5).click(); + cy.wait(delay); + }); +}); + +describe('Render stats chart and verify the reset button' , () => { + beforeEach(() => { + renderStatsChart(); + }); + + it('Render stats chart and test the Reset button functionality' , () => { + cy.get('[data-test-subj="valueFieldText"]').click().type(metricUnit); + cy.get('.euiSpacer.euiSpacer--s').eq(12).click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(0).click().type(metricsPrecisionUpdated); + cy.get('.euiSpacer.euiSpacer--s').eq(12).click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(1).click().type(titleSizeUpdated); + cy.get('.euiSpacer.euiSpacer--s').eq(12).click(); + cy.get('[data-test-subj="valueFieldNumber"]').eq(2).click().type(valueSizeUpdated); + cy.get('.euiSpacer.euiSpacer--s').eq(12).click(); + cy.get('[data-test-subj="euiColorPickerAnchor"]').click(); + cy.get('.euiColorPickerSwatch.euiColorPicker__swatchSelect').eq(5).click(); + cy.get('[data-test-subj="visualizeEditorResetButton"]').click(); + }); +}); \ No newline at end of file diff --git a/.cypress/integration/VisualizationCharts/7_pie_chart.spec.js b/.cypress/integration/VisualizationCharts/7_pie_chart.spec.js new file mode 100644 index 000000000..b731db10b --- /dev/null +++ b/.cypress/integration/VisualizationCharts/7_pie_chart.spec.js @@ -0,0 +1,202 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { + delay, + TEST_QUERIES, + querySearch, + landOnEventVisualizations, + renderDataConfig, + saveVisualizationAndVerify, + deleteVisualization, + } from '../../utils/event_constants'; + + const renderPieChart = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[3].query, TEST_QUERIES[3].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Pie').click(); + }; + + const aggregationValues = [ + 'COUNT', + 'SUM', + 'AVERAGE', + 'MAX', + 'MIN', + 'VAR_SAMP', + 'VAR_POP', + 'STDDEV_SAMP', + 'STDDEV_POP', + ]; + + describe.only('Render Pie chart and verify default behavior', () => { + beforeEach(() => { + renderPieChart(); + }); + + it('Render Pie chart and verify the default data', () => { + cy.get('.plot-container.plotly').should('exist'); + }); + + it('Render Pie chart and verify Data Configuration panel default behavior', () => { + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Dimensions').should('exist'); + cy.get('.first-division .euiFormRow__labelWrapper').contains('Aggregation').should('exist'); + cy.get('.first-division .euiFormLabel.euiFormRow__label').contains('Field').should('exist'); + cy.get('.first-division .euiFormLabel.euiFormRow__label') + .contains('Custom label') + .should('exist'); + cy.get('.first-division .euiButton__text').contains('Add').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Metrics').should('exist'); + cy.get('.euiButton__text').contains('Update chart').should('exist'); + }); + + it('Render Pie chart and verify Style section for Pie chart', () => { + cy.get('#data-panel').contains('Style').should('exist'); + cy.get('[aria-controls="configPanel__panelOptions"]').contains('Panel options').should('exist'); + cy.get('[aria-controls="configPanel__legend"]').contains('Legend').should('exist'); + cy.get('.euiForm.visEditorSidebar__form .euiIEFlexWrapFix') + .contains('Tooltip options') + .should('exist'); + cy.get('[aria-controls="configPanel__chartStyles"]').contains('Chart styles').should('exist'); + }); + + it('Options under Legend section', () => { + cy.get('#configPanel__legend').contains('Legend'); + cy.get('.euiTitle.euiTitle--xxsmall').eq(3).contains('Show legend'); + cy.get('[data-text="Show"]').eq(0).contains('Show'); + cy.get('[data-text="Hidden"]').eq(0).contains('Hidden'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Position'); + cy.get('[data-text="Right"]').contains('Right'); + cy.get('[data-text="Bottom"]').contains('Bottom'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Legend size').should('exist'); + }); + + it('Options under Tooltip options section', () => { + cy.get('.euiIEFlexWrapFix').contains('Tooltip options').should('exist'); + cy.get('[data-text="Show"]').eq(1).contains('Show'); + cy.get('[data-text="Hidden"]').eq(1).contains('Hidden'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Tooltip text'); + cy.get('[data-text="All"]').contains('All'); + cy.get('[data-text="Label"]').contains('Label'); + cy.get('[data-text="Value"]').contains('Value'); + cy.get('[data-text="Percent"]').contains('Percent'); + }); + + it('Options under Chart Styles section', () => { + cy.get('.euiIEFlexWrapFix').contains('Chart styles').should('exist'); + cy.get('#configPanel__chartStyles').contains('Mode'); + cy.get('.euiTitle.euiTitle--xxsmall').eq(9).contains('Label size'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Color theme'); + }); + + it('Table view should be enabled for Pie chart', () => { + cy.get('.euiSwitch__label').contains('Table view').should('exist'); + cy.get('[data-test-subj="workspace__dataTableViewSwitch"][aria-checked="false"]').click(); + cy.get('.ag-header.ag-pivot-off').should('exist'); + }); + + it('Render Pie chart and verify legends for Position Right and Bottom', () => { + cy.get('[data-text="Right"]').should('have.text', 'Right'); + cy.get('[data-text="Right"] [data-test-subj="v"]').should('have.attr', 'checked'); + cy.get('[data-text="Bottom"]').should('have.text', 'Bottom').click(); + cy.get('[data-text="Bottom"] [data-test-subj="h"]').should('not.have.attr', 'checked'); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true, multiple: true }); + }); + + it('Render Pie chart and verify legends for Show and Hidden', () => { + cy.get('[data-text="Show"]').eq(0).should('have.text', 'Show'); + cy.get('[data-text="Show"] [data-test-subj="show"]').should('have.attr', 'checked'); + cy.get('[data-text="Hidden"]').eq(0).should('have.text', 'Hidden').click(); + cy.get('[data-text="Hidden"] [data-test-subj="hidden"]').should('not.have.attr', 'checked'); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').click({ force: true, multiple: true }); + }); + }); + + describe('Save and Delete Visualization', () => { + beforeEach(() => { + renderPieChart(); + }); + + it('Render Pie chart, Save and Delete Visualization', () => { + saveVisualizationAndVerify(); + deleteVisualization(); + }); + }); + + describe('Color Theme section', () => { + beforeEach(() => { + renderPieChart(); + }); + + it('Default option in Color Theme', () => { + cy.get('.euiTitle.euiTitle--xxsmall') + .contains('Color Theme') + .scrollIntoView() + .should('be.visible'); + cy.get('.euiSuperSelectControl').contains('Default').should('exist'); + }); + + it('Render pie chart with single color option', () => { + cy.get('.euiTitle.euiTitle--xxsmall') + .contains('Color Theme') + .scrollIntoView() + .should('be.visible'); + cy.get('.euiSuperSelectControl').contains('Default').click(); + cy.get('.euiColorPalettePicker__item').contains('Single Color').click(); + cy.get('[data-test-subj="euiColorPickerAnchor"]').click(); + cy.get('.euiColorPickerSwatch.euiColorPicker__swatchSelect').eq(2).click(); + cy.get('.pielayer').should('exist'); + }); + }); + + describe('Renders Pie chart and Data Configurations section for Pie chart', () => { + beforeEach(() => { + landOnEventVisualizations(); + renderPieChart(); + }); + + it('Renders Dimensions and Metrics under Data Configurations for Pie chart', () => { + renderDataConfig(); + }); + + it('Validate "Add" and "X" buttons', () => { + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Configurations'); + cy.get('.euiText.euiText--extraSmall').eq(0).click(); + cy.get('.euiButton.euiButton--primary.euiButton--fullWidth').contains('Add').click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(2).click(); + cy.get('.euiComboBoxOption__content').eq(0).click(); + cy.get('.euiIcon.euiIcon--medium.euiIcon--danger').eq(1).click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(4).click(); + cy.get('.euiComboBoxOption__content').eq(1).click(); + cy.get('.euiFieldText[placeholder="Custom label"]').eq(1).type('Demo field'); + cy.get('.euiIcon.euiIcon--medium.euiIcon--danger').eq(1).click(); + cy.get('.euiButton.euiButton--primary.euiButton--fullWidth').contains('Add').should('exist'); + }); + + it('Verify drop down values for Aggregation', () => { + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Configurations'); + cy.get('.euiTitle.euiTitle--xxsmall').eq(1).contains('Dimensions').should('exist'); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(0).contains('Aggregation'); + cy.get('.euiText.euiText--extraSmall').eq(0).click(); + cy.get('[data-test-subj="comboBoxSearchInput"]').eq(0).click(); + aggregationValues.forEach(function (value) { + cy.get('.euiComboBoxOption__content').contains(value); + }); + }); + + it('Collapsible mode for Data Configuration panel', () => { + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Configurations'); + cy.get('.euiResizableButton.euiResizableButton--horizontal').eq(1).click(); + cy.get('[data-test-subj="panel-1-toggle"]').click(); + cy.get('[class*="euiResizableToggleButton-isCollapsed"]').eq(1).should('exist'); + cy.get('.euiResizablePanel.euiResizablePanel--middle') + .contains('Data Configurations') + .should('not.be.focused'); + }); + }); + \ No newline at end of file diff --git a/.cypress/integration/VisualizationCharts/9_coordinateMap_chart.spec.js b/.cypress/integration/VisualizationCharts/9_coordinateMap_chart.spec.js new file mode 100644 index 000000000..175011188 --- /dev/null +++ b/.cypress/integration/VisualizationCharts/9_coordinateMap_chart.spec.js @@ -0,0 +1,195 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/// +import { + TEST_QUERIES, + querySearch, + landOnEventVisualizations +} from '../../utils/event_constants'; + +const numberOfWindow = 4; +const labelSize = 20; +const latitudeRangeMin = 40; +const latitudeRangeMax = 70; +const longitudeRangeMin = -130; +const longitudeRangeMax = -55; + +const renderCoordinateMap = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[0].query, TEST_QUERIES[0].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').type('Coordinate Map').type('{enter}'); +}; + +const renderDataForCoordinateMap = () => { + cy.get('[data-test-subj="comboBoxInput"]').eq(1).click(); + cy.get('.euiComboBoxOption__content').contains('DestLocation').click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(3).click(); + cy.get('.euiComboBoxOption__content').contains('AvgTicketPrice').click(); + cy.get('[data-test-subj="comboBoxInput"]').eq(4).click(); + cy.get('.euiComboBoxOption__content').contains('Dest').click(); + cy.get('.euiButton__text').contains('Update chart').click(); + cy.get('.layer.land').should('exist'); + cy.get('stop[stop-color="rgb(5, 10, 172)"]').should('exist'); +} + +describe('Render coordinate map and verify default behaviour ', () => { + beforeEach(() => { + renderCoordinateMap(); + }); + + it('Render coordinate map and verify by default the data dont gets render', () => { + cy.get('.layer.land').should('not.exist'); + cy.get('.euiTextColor.euiTextColor--subdued').contains('No results found').should('exist'); + }); + + it('Render coordinate map and verify you see data configuration panel and chart panel', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Data Configurations').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Dimensions').should('exist'); + cy.get('.euiTitle.euiTitle--xxsmall').contains('Metrics').should('exist'); + cy.get('.euiIEFlexWrapFix').contains('Panel options').click(); + cy.get('.euiIEFlexWrapFix').contains('Text').click(); + cy.get('.euiIEFlexWrapFix').contains('Chart styles').click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); + + it('Render coordinate map and verify the data configuration panel and chart panel are collapsable', () => { + cy.get('.euiPanel.euiPanel--paddingSmall').should('have.length', numberOfWindow); + cy.get('[aria-label="Press to toggle this panel"]').eq(1).click(); + cy.get('[aria-label="Press to toggle this panel"]').eq(2).click(); + }); +}); + +describe('Render coordinate map for data configuration panel', () => { + beforeEach(() => { + renderCoordinateMap(); + }); + + it('Render coordinate map and verify data config panel', () => { + cy.get('[data-test-subj="comboBoxInput"]').eq(1).contains('Select a field').should('exist'); + cy.get('[data-test-subj="comboBoxInput"]').eq(3).contains('Select a field').should('exist'); + cy.get('[data-test-subj="comboBoxInput"]').eq(4).contains('Select a field').should('exist'); + }); + + it('Render coordinate map and verify data gets render only after selecting the field value in data configuration panel', () => { + renderDataForCoordinateMap(); + }); + + it('Render coordinate map and verify data config dimensions panel "Field" contains only location', () => { + cy.get('[data-test-subj="comboBoxInput"]').eq(1).click(); + cy.get('.euiComboBoxOption__content').contains('DestLocation').should('exist'); + cy.get('.euiComboBoxOption__content').contains('OriginLocation').should('exist'); + }); + + it('Render coordinate map and verify data config metrics panel "Field" contains only value which return either integer or float value', () => { + cy.get('[data-test-subj="comboBoxInput"]').eq(3).click(); + cy.get('.euiComboBoxOption__content').contains('AvgTicketPrice').should('exist'); + cy.get('.euiComboBoxOption__content').contains('DistanceMiles').should('exist'); + cy.get('.euiComboBoxOption__content').should('not.contain', 'DestLocation'); + cy.get('.euiComboBoxOption__content').should('have.length', 6); + }); + + it('Render coordinate map and verify data config metric panel "Plot Label" contains a combination of string value and integer value', () => { + cy.get('[data-test-subj="comboBoxInput"]').eq(4).click(); + cy.get('.euiComboBoxOption__content').contains('AvgTicketPrice').should('exist'); + cy.get('.euiComboBoxOption__content').contains('Dest').should('exist'); + cy.get('.euiComboBoxOption__content').contains('DestLocation').should('exist'); + }); +}); + +describe('Render coordinate map for panel options', () => { + beforeEach(() => { + renderCoordinateMap(); + renderDataForCoordinateMap(); + }); + + it('Render coordinate map and verify the title gets updated according to user input ', () => { + cy.get('input[name="title"]').type("coordinate map"); + cy.get('textarea[name="description"]').should('exist').click(); + cy.get('.gtitle').contains('coordinate map').should('exist'); + }); +}); + +describe('Render coordinate map for text', () => { + beforeEach(() => { + renderCoordinateMap(); + renderDataForCoordinateMap(); + }); + + it('Render coordinate map and verify by default the "Show" is enabled for "Show Text"', () => { + cy.get('[data-text="Show"]').should('have.text', 'Show'); + cy.get('[data-text="Show"] [data-test-subj="show"]').should('have.attr', 'checked'); + cy.get('[data-unformatted="Duluth International Airport"]').should('exist'); + }); + + it('Render coordinate map and change "Show Text" to "Hidden"', () => { + cy.get('[data-text="Hidden"]').should('have.text', 'Hidden').click(); + cy.get('[data-text="Hidden"] [data-test-subj="hidden"]').should('not.have.attr', 'checked'); + cy.get('[data-unformatted="Duluth International Airport"]').should('not.exist'); + }); + + it('Render coordinate map and verify by default the "Top" is enabled for "Position"', () => { + cy.get('[data-text="Top"]').should('have.text', 'Top'); + cy.get('[data-text="Top"] [data-test-subj="top center"]').should('have.attr', 'checked'); + }); + + it('Render coordinate map and change "Position" to "Right", "Bottom" and "Left"', () => { + cy.get('[data-text="Right"]').should('have.text', 'Right').click(); + cy.get('[data-text="Right"] [data-test-subj="middle right"]').should('not.have.attr', 'checked'); + cy.get('[data-text="Bottom"]').should('have.text', 'Bottom').click(); + cy.get('[data-text="Bottom"] [data-test-subj="bottom center"]').should('not.have.attr', 'checked'); + cy.get('[data-text="Left"]').should('have.text', 'Left').click(); + cy.get('[data-text="Left"] [data-test-subj="middle left"]').should('not.have.attr', 'checked'); + }); +}); + +describe('Render coordinate map for chart style options', () => { + beforeEach(() => { + renderCoordinateMap(); + renderDataForCoordinateMap(); + }); + + it('Render coordinate map and change Label Size ', () => { + cy.get('[data-test-subj="valueFieldNumber"]').click().type(labelSize); + cy.get('textarea[name="description"]').should('exist').click(); + cy.get('[data-unformatted="Duluth International Airport"]').should('have.css', 'font-size', '20px'); + cy.get('[data-test-subj="valueFieldNumber"]').should('have.value', labelSize) + }); + + it('Render coordinate map and change "Latitude Range"', () => { + cy.get('input[type="number"]').eq(1).should('have.value', latitudeRangeMin); + cy.get('input[type="number"]').eq(2).should('have.value', latitudeRangeMax); + cy.get('input[type="number"]').eq(1).click().clear().type(30); + }); + + it('Render coordinate map and change "Longitude Range"', () => { + cy.get('input[type="number"]').eq(3).should('have.value', longitudeRangeMin); + cy.get('input[type="number"]').eq(4).should('have.value', longitudeRangeMax); + cy.get('input[type="number"]').eq(4).click().clear().type(40); + }); +}); + +describe('Render coordinate map and verify if reset works properly', () => { + beforeEach(() => { + renderCoordinateMap(); + renderDataForCoordinateMap(); + }); + + it('Render coordinate map with all feild data then click on reset and verify reset works properly', () => { + cy.get('input[placeholder="Title"]').type('coordinate map'); + cy.get('textarea[placeholder="Description"]').type('Description For coordinate map'); + cy.get('.euiButton__text').contains('Hidden').click(); + cy.get('.euiButton__text').contains('Right').click(); + cy.get('[data-test-subj="valueFieldNumber"]').click().type(labelSize); + cy.get('.euiButtonEmpty__text').contains('Reset').click(); + cy.get('input[placeholder="Title"]').should('not.have.value', 'coordinate map'); + cy.get('textarea[placeholder="Description"]').should('not.have.value', 'Description For coordinate map'); + cy.get('[data-text="Show"] [data-test-subj="show"]').should('have.attr', 'checked'); + cy.get('[data-text="Top"] [data-test-subj="top center"]').should('have.attr', 'checked'); + cy.get('[data-test-subj="valueFieldNumber"]').should('have.value', ''); + }); +}); diff --git a/.cypress/utils/constants.js b/.cypress/utils/constants.js index 75e343086..d67d3988f 100644 --- a/.cypress/utils/constants.js +++ b/.cypress/utils/constants.js @@ -60,6 +60,8 @@ export const setTimeFilter = (setEndTime = false, refresh = true) => { // notebooks export const TEST_NOTEBOOK = 'Test Notebook'; export const SAMPLE_URL = 'https://github.com/opensearch-project/sql/tree/main/sql-jdbc'; +export const NOTEBOOK_TEXT = 'Use Notebooks to interactively and collaboratively develop rich reports backed by live data. Common use cases for notebooks includes creating postmortem reports, designing run books, building live infrastructure reports, or even documentation.'; +export const OPENSEARCH_URL = 'https://opensearch.org/docs/latest/observability-plugin/notebooks/'; export const MARKDOWN_TEXT = `%md # Heading 1 @@ -100,3 +102,22 @@ export const supressResizeObserverIssue = () => { if (err.message.includes('ResizeObserver loop')) return false; }); }; + +export const verify_traces_spans_data_grid_cols_exists = () => { + cy.get('.euiDataGridHeaderCell__content').contains('Span ID').should('exist'); + cy.get('.euiDataGridHeaderCell__content').contains('Trace ID').should('exist'); + cy.get('.euiDataGridHeaderCell__content').contains('Operation').should('exist'); + cy.get('.euiDataGridHeaderCell__content').contains('Duration').should('exist'); + cy.get('.euiDataGridHeaderCell__content').contains('Start time').should('exist'); + cy.get('.euiDataGridHeaderCell__content').contains('End time').should('exist'); + cy.get('.euiDataGridHeaderCell__content').contains('Errors').should('exist'); +} + +export const count_table_row = (expected_row_count) => { + cy.get('.euiDataGridHeader [role="columnheader"]').then($el => { + let colmun_header_count = Cypress.$($el).length; + let table_grid_cell_count = Cypress.$('[data-test-subj="dataGridRowCell"]').length; + const total_row_count = table_grid_cell_count / colmun_header_count; + expect(total_row_count).to.equal(expected_row_count) + }); +} diff --git a/.cypress/utils/event_constants.js b/.cypress/utils/event_constants.js index 130d32590..9e0f89697 100644 --- a/.cypress/utils/event_constants.js +++ b/.cypress/utils/event_constants.js @@ -3,30 +3,53 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { supressResizeObserverIssue } from './constants' +import { supressResizeObserverIssue } from './constants'; export const delay = 1000; -export const YEAR_TO_DATE_DOM_ID = '[data-test-subj="superDatePickerCommonlyUsed_Year_to date"]' +export const YEAR_TO_DATE_DOM_ID = '[data-test-subj="superDatePickerCommonlyUsed_Year_to date"]'; export const TEST_QUERIES = [ { query: 'source = opensearch_dashboards_sample_data_flights', - dateRangeDOM: YEAR_TO_DATE_DOM_ID + dateRangeDOM: YEAR_TO_DATE_DOM_ID, }, { - query: 'source = opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by Carrier', - dateRangeDOM: YEAR_TO_DATE_DOM_ID + query: + 'source = opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by Carrier', + dateRangeDOM: YEAR_TO_DATE_DOM_ID, }, { - query: 'source = opensearch_dashboards_sample_data_logs' + query: 'source = opensearch_dashboards_sample_data_logs', }, { - query: 'source = opensearch_dashboards_sample_data_logs | stats count() by host', - dateRangeDOM: YEAR_TO_DATE_DOM_ID + query: + 'source=opensearch_dashboards_sample_data_flights | stats max(AvgTicketPrice) by DestCountry, DestCityName, Carrier', + dateRangeDOM: YEAR_TO_DATE_DOM_ID, }, { - query: 'source = opensearch_dashboards_sample_data_logs | stats count(), avg(bytes) by host, tags', - dateRangeDOM: YEAR_TO_DATE_DOM_ID + query: + 'source = opensearch_dashboards_sample_data_logs | stats count(), avg(bytes) by host, tags', + dateRangeDOM: YEAR_TO_DATE_DOM_ID, + }, + { + query: + 'source=opensearch_dashboards_sample_data_flights | stats avg(FlightDelayMin) by DestCountry, DestCityName', + dateRangeDOM: YEAR_TO_DATE_DOM_ID, + }, + { + query: + "source = opensearch_dashboards_sample_data_logs | where response='503' or response='404' | stats count() by span(timestamp,1d)", + dateRangeDOM: YEAR_TO_DATE_DOM_ID, + }, + { + query: + 'source=opensearch_dashboards_sample_data_flights |where FlightDelayMin > 0 | stats sum(FlightDelayMin) as total_delay_min, count() as total_delayed by Carrier |eval avg_delay=total_delay_min / total_delayed | sort - avg_delay', + dateRangeDOM: YEAR_TO_DATE_DOM_ID, + }, + { + query: + 'source = opensearch_dashboards_sample_data_logs | stats count(), max(bytes) by span(timestamp,1d), clientip, host', + dateRangeDOM: YEAR_TO_DATE_DOM_ID, }, ]; @@ -36,6 +59,18 @@ export const SAVE_QUERY2 = 'Mock Flight count by destination'; export const SAVE_QUERY3 = 'Mock Flight count by destination save to panel'; export const SAVE_QUERY4 = 'Mock Flight peek'; +export const aggregationValues = [ + 'COUNT', + 'SUM', + 'AVERAGE', + 'MAX', + 'MIN', + 'VAR_SAMP', + 'VAR_POP', + 'STDDEV_SAMP', + 'STDDEV_POP', +]; + export const querySearch = (query, rangeSelected) => { cy.get('[data-test-subj="searchAutocompleteTextArea"]').type(query); cy.get('[data-test-subj="superDatePickerToggleQuickMenuButton"]').click(); @@ -70,4 +105,146 @@ export const landOnPanels = () => { `${Cypress.env('opensearchDashboards')}/app/observability-dashboards#/operational_panels` ); cy.wait(delay); -}; \ No newline at end of file +}; + +const vis_name_sub_string = Math.floor(Math.random() * 100); +export const saveVisualizationAndVerify = () => { + cy.get('[data-test-subj="eventExplorer__saveManagementPopover"]').click(); + cy.get('[data-test-subj="eventExplorer__querySaveComboBox"]').click(); + cy.get('.euiComboBoxOptionsList__rowWrap .euiFilterSelectItem').eq(0).click(); + cy.get( + '.euiPopover__panel .euiFormControlLayoutIcons [data-test-subj="comboBoxToggleListButton"]' + ) + .eq(0) + .click(); + cy.get('.euiPopover__panel input') + .eq(1) + .type(`Test visualization` + vis_name_sub_string); + cy.get('[data-test-subj="eventExplorer__querySaveConfirm"]').click(); + cy.wait(delay); + cy.get('.euiHeaderBreadcrumbs a').eq(1).click(); + cy.get('.euiFlexGroup .euiFormControlLayout__childrenWrapper input') + .eq(0) + .type(`Test visualization` + vis_name_sub_string) + .type('{enter}'); + cy.get('.euiBasicTable .euiTableCellContent button').eq(0).click(); +}; + +export const deleteVisualization = () => { + cy.get('a[href = "#/event_analytics"]').click(); + cy.get('.euiFlexGroup .euiFormControlLayout__childrenWrapper input') + .eq(0) + .type(`Test visualization` + vis_name_sub_string) + .type('{enter}'); + cy.get('input[data-test-subj = "checkboxSelectAll"]').click(); + cy.get('.euiButtonContent.euiButtonContent--iconRight.euiButton__content').click(); + cy.get('.euiContextMenuItem .euiContextMenuItem__text').eq(0).click(); + cy.get('input[placeholder = "delete"]').clear().type('delete'); + cy.get('button[data-test-subj = "popoverModal__deleteButton"]').click(); + cy.get('.euiToastHeader').should('exist'); +}; + +export const renderTreeMapchart = () => { + querySearch(TEST_QUERIES[7].query, TEST_QUERIES[7].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Tree Map') + .type('{enter}'); + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Tree Map'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]') + .click() + .type('This is the description for Tree Map'); + cy.get('.euiIEFlexWrapFix').eq(1).contains('Treemap').should('exist'); + cy.get('#configPanel__treemap_options').contains('Tiling Algorithm').should('exist'); + cy.get('[data-test-subj = "comboBoxInput"]').eq(3).click(); + cy.get('button[name="Slice Dice"]').click(); +}; + +export const renderPieChart = () => { + querySearch(TEST_QUERIES[5].query, TEST_QUERIES[5].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]').click(); + cy.get('[data-test-subj="comboBoxOptionsList "] button span').contains('Pie').click(); + cy.wait(delay); + cy.get('#configPanel__panelOptions .euiFieldText').click().type('Pie chart'); + cy.get('.euiFlexItem .euiFormRow [placeholder="Description"]') + .click() + .type('This is the description for Pie chart'); + cy.get('[aria-controls="configPanel__legend"]').contains('Legend').should('exist'); + cy.get('#configPanel__legend .euiTitle.euiTitle--xxsmall').eq(0).contains('Show Legend'); + cy.get('span[data-text="Show"]').contains('Show').should('exist'); + cy.get('#configPanel__legend .euiTitle.euiTitle--xxsmall').eq(1).contains('Position'); + cy.get('span[data-text="Right"]').contains('Right').should('exist'); + cy.get('#configPanel__legend .euiTitle.euiTitle--xxsmall').eq(2).contains('Legend Size'); + cy.get('[aria-controls="configPanel__chartStyles"]').contains('Chart Styles').should('exist'); + cy.get('#configPanel__chartStyles .euiTitle.euiTitle--xxsmall').eq(0).contains('Mode').click(); + cy.get( + '#configPanel__chartStyles .euiComboBox__inputWrap.euiComboBox__inputWrap--noWrap.euiComboBox__inputWrap-isClearable' + ).click(); + cy.get('.euiComboBoxOption__content').contains('Donut').click(); + cy.get('#configPanel__chartStyles .euiTitle.euiTitle--xxsmall').eq(1).contains('Label Size'); + cy.get('#configPanel__chartStyles input[type="number"]').click().type('10'); + cy.get('#configPanel__chartStyles .euiTitle.euiTitle--xxsmall').eq(2).contains('Color Theme'); + cy.get('.euiSuperSelectControl').click(); + cy.get('.euiColorPalettePicker__item').eq(1).contains('Single Color').click(); + cy.get('.euiFieldText.euiColorPicker__input.euiFieldText--withIcon').click(); + cy.get('[aria-label="Select #D36086 as the color"]').click(); + cy.get('.visEditorSidebar__controls [data-test-subj="visualizeEditorRenderButton"]') + .contains('Preview') + .click(); + cy.get('.plot-container.plotly').should('exist'); +}; + +export const renderDataConfig = () => { + cy.get('.euiResizablePanel.euiResizablePanel--middle').contains('Data Cofigurations'); + cy.get('.euiTitle.euiTitle--xxsmall').eq(1).contains('Dimensions').should('exist'); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(0).contains('Aggregation'); + cy.get('[data-test-subj="comboBoxSearchInput"]').eq(0).click(); + cy.get('.euiComboBoxOption__content').eq(2).click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(1).contains('Field'); + cy.get('[data-test-subj="comboBoxSearchInput"]').eq(1).click(); + cy.get('.euiComboBoxOption__content').eq(1).click(); + cy.get('.euiFieldText[placeholder="Custom label"]').eq(0).type('Average field'); + cy.get('.euiTitle.euiTitle--xxsmall').eq(2).contains('Metrics').should('exist'); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(0).contains('Aggregation'); + cy.get('.euiFormRow__fieldWrapper .euiComboBox').eq(2).click(); + cy.get('.euiComboBoxOption__content').eq(4).click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').eq(4).click(); + cy.get('.euiComboBoxOption__content').eq(0).click(); + cy.get('.euiFieldText[placeholder="Custom label"]').eq(1).type('Min field'); + cy.get('.euiButton__text').contains('Right').click(); + cy.get('[data-test-subj="visualizeEditorRenderButton"]').contains('Update chart').click(); + cy.get('.js-plotly-plot').should('exist'); +}; + +export const renderLineChart = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[5].query, TEST_QUERIES[5].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Line') + .type('{enter}'); +}; + +export const renderAddParent = () => { + cy.get(' [data-test-subj="addParentButton"] .euiButton__text').contains('+ Add Parent').click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').contains('Parent 1').should('exist'); + cy.get('p.euiComboBoxPlaceholder').eq(0).click({ force: true }); + cy.get('.euiComboBoxOption__content').eq(0).click(); + cy.get(' [data-test-subj="addParentButton"] .euiButton__text').contains('+ Add Parent').click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').contains('Parent 2').should('exist'); + cy.get('p.euiComboBoxPlaceholder').click({ force: true }); + cy.get('.euiComboBoxOption__content').eq(1).click(); + cy.get(' [data-test-subj="addParentButton"] .euiButton__text').contains('+ Add Parent').click(); + cy.get('.first-division .euiFormLabel.euiFormRow__label').contains('Parent 3').should('exist'); + cy.get('p.euiComboBoxPlaceholder').click({ force: true }); + cy.get('.euiComboBoxOption__content').eq(2).click(); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Line') + .type('{enter}'); +}; + +export const renderGaugeChart = () => { + landOnEventVisualizations(); + querySearch(TEST_QUERIES[1].query, TEST_QUERIES[1].dateRangeDOM); + cy.get('[data-test-subj="configPane__vizTypeSelector"] [data-test-subj="comboBoxInput"]') + .type('Gauge') + .type('{enter}'); +}; diff --git a/.github/workflows/dashboards-observability-test-and-build-workflow.yml b/.github/workflows/dashboards-observability-test-and-build-workflow.yml index 4d25d77fe..057c6b399 100644 --- a/.github/workflows/dashboards-observability-test-and-build-workflow.yml +++ b/.github/workflows/dashboards-observability-test-and-build-workflow.yml @@ -11,8 +11,10 @@ env: jobs: build: - - runs-on: ubuntu-latest + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} steps: - name: Checkout Plugin @@ -57,13 +59,13 @@ jobs: yarn test --coverage - name: Upload coverage + if: ${{ matrix.os == 'ubuntu-latest' }} uses: codecov/codecov-action@v1 with: flags: dashboards-observability directory: ./OpenSearch-Dashboards/plugins/dashboards-observability token: ${{ secrets.CODECOV_TOKEN }} - # TODO remove hard coded version when observability is ready - name: Build Artifact run: | cd OpenSearch-Dashboards/plugins/dashboards-observability @@ -73,6 +75,5 @@ jobs: - name: Upload Artifact uses: actions/upload-artifact@v1 with: - name: dashboards-observability - path: ./OpenSearch-Dashboards/plugins/dashboards-observability/build - + name: dashboards-observability-${{ matrix.os }} + path: ./OpenSearch-Dashboards/plugins/dashboards-observability/build \ No newline at end of file diff --git a/.github/workflows/opensearch-observability-test-and-build-workflow.yml b/.github/workflows/opensearch-observability-test-and-build-workflow.yml index 5990f2b8e..9d70144f3 100644 --- a/.github/workflows/opensearch-observability-test-and-build-workflow.yml +++ b/.github/workflows/opensearch-observability-test-and-build-workflow.yml @@ -4,13 +4,18 @@ on: [pull_request, push] jobs: build: + env: + BUILD_ARGS: ${{ matrix.os_build_args }} strategy: matrix: - java: - - 11 - - 17 - - runs-on: ubuntu-latest + java: [11, 17] + os: [ubuntu-latest, windows-latest, macos-latest] + include: + - os: windows-latest + os_build_args: -x integTest -x jacocoTestReport + - os: macos-latest + os_build_args: -x integTest -x jacocoTestReport + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v1 @@ -21,6 +26,8 @@ jobs: java-version: ${{ matrix.java }} - name: Run Backwards Compatibility Tests + # Temporarily only do this for linux + if: ${{ matrix.os == 'ubuntu-latest' }} run: | cd opensearch-observability echo "Running backwards compatibility tests ..." @@ -29,9 +36,10 @@ jobs: - name: Build with Gradle run: | cd opensearch-observability - ./gradlew build + ./gradlew build ${{ env.BUILD_ARGS }} - name: Upload coverage + if: ${{ matrix.os == 'ubuntu-latest' }} uses: codecov/codecov-action@v1 with: flags: opensearch-observability @@ -46,5 +54,5 @@ jobs: - name: Upload Artifacts uses: actions/upload-artifact@v1 with: - name: opensearch-observability - path: opensearch-observability-builds + name: opensearch-observability-${{ matrix.os }} + path: opensearch-observability-builds \ No newline at end of file diff --git a/.gitignore b/.gitignore index c0fc79791..bf5586d64 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ build/ coverage/ .cypress/screenshots .cypress/videos +common/query_manager/antlr/output diff --git a/common/constants/colors.ts b/common/constants/colors.ts new file mode 100644 index 000000000..3845e41a0 --- /dev/null +++ b/common/constants/colors.ts @@ -0,0 +1,188 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { colorPalette } from '@elastic/eui'; + +export const BLUES_PALETTE = { + name: 'Blues', + label: 'Blues', + colors: [ + 'rgb(5,10,172)', + 'rgb(40,60,190)', + 'rgb(70,100,245)', + 'rgb(90,120,245)', + 'rgb(106,137,247)', + 'rgb(220,220,220)', + ], +}; + +export const REDS_PALETTE = { + name: 'Reds', + label: 'Reds', + colors: ['rgb(220,220,220)', 'rgb(245,195,157)', 'rgb(245,160,105)', 'rgb(178,10,28)'], +}; + +export const GREENS_PALETTE = { + name: 'Greens', + label: 'Greens', + colors: [ + 'rgb(0,68,27)', + 'rgb(0,109,44)', + 'rgb(35,139,69)', + 'rgb(65,171,93)', + 'rgb(116,196,118)', + 'rgb(161,217,155)', + 'rgb(199,233,192)', + 'rgb(229,245,224)', + 'rgb(247,252,245)', + ], +}; + +export const GREYS_PALETTE = { + name: 'Greys', + label: 'Greys', + colors: ['rgb(0,0,0)', 'rgb(255,255,255)'], +}; + +export const BLUE_RED_PALETTE = { + name: 'Bluered', + label: 'Blue-Red', + colors: ['rgb(0,0,255)', 'rgb(255,0,0)'], +}; + +export const RED_BLUE_PALETTE = { + name: 'RdBu', + label: 'Red-Blue', + colors: [ + 'rgb(5,10,172)', + 'rgb(106,137,247)', + 'rgb(190,190,190)', + 'rgb(220,170,132)', + 'rgb(230,145,90)', + 'rgb(178,10,28)', + ], +}; + +export const YELLOW_ORANGE_RED_PALETTE = { + name: 'YlOrRd', + label: 'Yellow-Orange-Red', + colors: [ + 'rgb(128,0,38)', + 'rgb(189,0,38)', + 'rgb(227,26,28)', + 'rgb(252,78,42)', + 'rgb(253,141,60)', + 'rgb(254,178,76)', + 'rgb(254,217,118)', + 'rgb(255,237,160)', + 'rgb(255,255,204)', + ], +}; + +export const YELLOW_GREEN_BLUE_PALETTE = { + name: 'YlGnBu', + label: 'Yellow-Green-Blue', + colors: [ + 'rgb(8,29,88)', + 'rgb(37,52,148)', + 'rgb(34,94,168)', + 'rgb(29,145,192)', + 'rgb(65,182,196)', + 'rgb(127,205,187)', + 'rgb(199,233,180)', + 'rgb(237,248,217)', + 'rgb(255,255,217)', + ], +}; + +export const DEFAULT_PALETTE = 'default'; +export const SINGLE_COLOR_PALETTE = 'singleColor'; +export const MULTI_COLOR_PALETTE = 'multicolor'; + +export const COLOR_PALETTES = [ + { + value: DEFAULT_PALETTE, + title: 'Default', + type: 'text', + }, + { + value: SINGLE_COLOR_PALETTE, + title: 'Single color', + type: 'text', + }, + { + value: MULTI_COLOR_PALETTE, + title: 'Multicolored', + type: 'text', + }, + { + value: BLUES_PALETTE.name, + title: BLUES_PALETTE.label, + palette: colorPalette(BLUES_PALETTE.colors, 20), + type: 'gradient', + }, + { + value: REDS_PALETTE.name, + title: REDS_PALETTE.label, + palette: colorPalette(REDS_PALETTE.colors, 20), + type: 'gradient', + }, + { + value: GREENS_PALETTE.name, + title: GREENS_PALETTE.label, + palette: colorPalette(GREENS_PALETTE.colors, 20), + type: 'gradient', + }, + { + value: GREYS_PALETTE.name, + title: GREYS_PALETTE.label, + palette: colorPalette(GREYS_PALETTE.colors, 20), + type: 'gradient', + }, + { + value: BLUE_RED_PALETTE.name, + title: BLUE_RED_PALETTE.label, + palette: colorPalette(BLUE_RED_PALETTE.colors, 20), + type: 'gradient', + }, + { + value: RED_BLUE_PALETTE.name, + title: RED_BLUE_PALETTE.label, + palette: colorPalette(RED_BLUE_PALETTE.colors, 20, true), + type: 'gradient', + }, + { + value: YELLOW_ORANGE_RED_PALETTE.name, + title: YELLOW_ORANGE_RED_PALETTE.label, + palette: colorPalette(YELLOW_ORANGE_RED_PALETTE.colors, 20), + type: 'gradient', + }, + { + value: YELLOW_GREEN_BLUE_PALETTE.name, + title: YELLOW_GREEN_BLUE_PALETTE.label, + palette: colorPalette(YELLOW_GREEN_BLUE_PALETTE.colors, 20), + type: 'gradient', + }, +]; +export const HEX_CONTRAST_COLOR = 0xffffff; +export const PIE_PALETTES = [ + { + value: DEFAULT_PALETTE, + title: 'Default', + type: 'text', + }, + { + value: SINGLE_COLOR_PALETTE, + title: 'Single Color', + type: 'text', + }, +]; + +export const HEATMAP_PALETTE_COLOR = { name: REDS_PALETTE.label, color: REDS_PALETTE.label }; +export const HEATMAP_SINGLE_COLOR = { name: 'singleColor', color: '#000000' }; +export const OPACITY = 'opacity'; +export const SPECTRUM = 'spectrum'; +export const COLOR_BLACK = 'rgb(0,0,0)'; +export const COLOR_WHITE = 'rgb(255,255,255)'; diff --git a/common/constants/data_table.ts b/common/constants/data_table.ts new file mode 100644 index 000000000..bbca653a7 --- /dev/null +++ b/common/constants/data_table.ts @@ -0,0 +1,16 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export const GRID_HEADER_COLUMN_MAX_WIDTH = '150px'; +export const GRID_PAGE_RANGE_DISPLAY = 5; +export const COLUMN_DEFAULT_MIN_WIDTH = 100; +export const GRID_PAGE_SIZES = [10, 50, 100]; +export const ROW_DENSITIES = [ + { icon: 'tableDensityExpanded', height: 55, selected: false }, + { icon: 'tableDensityNormal', height: 45, selected: false }, + { icon: 'tableDensityCompact', height: 35, selected: true }, +]; + +export const HEADER_HEIGHT = 35; diff --git a/common/constants/explorer.ts b/common/constants/explorer.ts index aa769d3bb..553347ea2 100644 --- a/common/constants/explorer.ts +++ b/common/constants/explorer.ts @@ -3,6 +3,10 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { htmlIdGenerator } from '@elastic/eui'; +import { VIS_CHART_TYPES } from './shared'; +import { ThresholdUnitType } from '../../public/components/event_analytics/explorer/visualizations/config_panel/config_panes/config_controls/config_thresholds'; + export const EVENT_ANALYTICS_DOCUMENTATION_URL = 'https://opensearch.org/docs/latest/observability-plugin/event-analytics/'; export const OPEN_TELEMETRY_LOG_CORRELATION_LINK = @@ -76,7 +80,206 @@ export const REDUX_EXPL_SLICE_QUERY_TABS = 'queryTabs'; export const REDUX_EXPL_SLICE_VISUALIZATION = 'explorerVisualization'; export const REDUX_EXPL_SLICE_COUNT_DISTRIBUTION = 'countDistributionVisualization'; export const REDUX_EXPL_SLICE_PATTERNS = 'patterns'; -export const PLOTLY_GAUGE_COLUMN_NUMBER = 5; +export const PLOTLY_GAUGE_COLUMN_NUMBER = 4; export const APP_ANALYTICS_TAB_ID_REGEX = /application-analytics-tab.+/; export const DEFAULT_AVAILABILITY_QUERY = 'stats count() by span( timestamp, 1h )'; export const PPL_PATTERNS_REGEX = /\|\s*patterns\s+\S+\s*\|\s*where\s+patterns_field\s*\=\s*'[^a-zA-Z0-9]+'/; +export const ADD_BUTTON_TEXT = '+ Add color theme'; +export const NUMBER_INPUT_MIN_LIMIT = 1; + +export const VIZ_CONTAIN_XY_AXIS = [ + VIS_CHART_TYPES.Bar, + VIS_CHART_TYPES.Histogram, + VIS_CHART_TYPES.Line, + VIS_CHART_TYPES.Pie, + VIS_CHART_TYPES.Scatter, + VIS_CHART_TYPES.HorizontalBar, +]; + +// default ppl aggregation method options +export const AGGREGATION_OPTIONS = [ + { + label: 'count', + }, + { + label: 'sum', + }, + { + label: 'avg', + }, + { + label: 'max', + }, + { + label: 'min', + }, + { + label: 'var_samp', + }, + { + label: 'var_pop', + }, + { + label: 'stddev_samp', + }, + { + label: 'stddev_pop', + }, +]; + +// numeric fields type for metrics +export const NUMERICAL_TYPES = [ + 'float', + 'double', + 'bigint', + 'long', + 'octet', + 'short', + 'byte', + 'integer', +]; +// Data table constants +export const GRID_HEADER_COLUMN_MAX_WIDTH = '150px'; +export const GRID_PAGE_RANGE_DISPLAY = 5; +export const COLUMN_DEFAULT_MIN_WIDTH = 100; +export const GRID_PAGE_SIZES = [10, 50, 100]; +export const ROW_DENSITIES = [ + { icon: 'tableDensityExpanded', height: 55, selected: false }, + { icon: 'tableDensityNormal', height: 45, selected: false }, + { icon: 'tableDensityCompact', height: 35, selected: true }, +]; + +export const HEADER_HEIGHT = 35; + +// gauge chart default parameters +export interface DefaultGaugeChartParametersProps { + GaugeTitleSize: number; + DisplayDefaultGauges: number; + OrientationDefault: string; + TickLength: number; + LegendPlacement: string; + ThresholdsMaxLimit: number; +} + +export const DEFAULT_GAUGE_CHART_PARAMETERS: DefaultGaugeChartParametersProps = { + GaugeTitleSize: 14, + DisplayDefaultGauges: 1, + OrientationDefault: 'h', + TickLength: 5, + LegendPlacement: 'center', + ThresholdsMaxLimit: 1, +}; + +// pie chart default parameters +export const PLOTLY_PIE_COLUMN_NUMBER = 2; +export const PIE_XAXIS_GAP = 0.2; +export const PIE_YAXIS_GAP = 0.1; +export interface DefaultPieChartParameterProps { + DefaultMode: string; +} + +export const DEFAULT_PIE_CHART_PARAMETERS: DefaultPieChartParameterProps = { + DefaultMode: 'pie', +}; +export const GROUPBY = 'dimensions'; +export const AGGREGATIONS = 'series'; +export const PARENTFIELDS = 'parentFields'; +export const VALUEFIELD = 'valueField'; +export const CHILDFIELD = 'childField'; +export const TIMESTAMP = 'timestamp'; + +// metrics constants +export const METRICS_GRID_SPACE_BETWEEN_X_AXIS = 0.01; +export const METRICS_GRID_SPACE_BETWEEN_Y_AXIS = 100; +export const METRICS_REDUCE_VALUE_SIZE_PERCENTAGE = 0.08; +export const METRICS_REDUCE_TITLE_SIZE_PERCENTAGE = 0.05; +export const METRICS_REDUCE_SERIES_UNIT_SIZE_PERCENTAGE = 0.2; +export const METRICS_SERIES_UNIT_SUBSTRING_LENGTH = 3; +export const METRICS_AXIS_MARGIN = { + l: 0, + r: 0, + b: 0, + t: 80, +}; + +export const METRICS_ANNOTATION = { + xref: 'paper', + yref: 'paper', + showarrow: false, +}; + +export interface DefaultMetricsChartParametersProps { + DefaultTextMode: string; + DefaultOrientation: string; + DefaultTitleSize: number; + DefaultChartType: string; + TextAlignment: string; + DefaultPrecision: number; + DefaultValueSize: number; + BaseThreshold: ThresholdUnitType; + DefaultTextColor: string; +} + +export const DEFAULT_METRICS_CHART_PARAMETERS: DefaultMetricsChartParametersProps = { + DefaultTextMode: 'auto', + DefaultOrientation: 'auto', + DefaultTitleSize: 30, + DefaultValueSize: 80, + DefaultChartType: 'auto', + TextAlignment: 'auto', + DefaultPrecision: 1, + BaseThreshold: { + thid: htmlIdGenerator('thr')(), + name: 'Base', + color: '#3CA1C7', + value: 0, + isReadOnly: true, + }, + DefaultTextColor: '#FFFFFF', +}; +export interface DefaultBarChartStylesProps { + BarMode: string; + GroupWidth: number; + BarWidth: number; + LabelSize: number; +} + +export const DEFAULT_BAR_CHART_STYLES: DefaultBarChartStylesProps = { + BarMode: 'group', + GroupWidth: 0.7, + BarWidth: 0.97, + LabelSize: 12, +}; + +export const SIMILAR_VIZ_TYPES = [ + VIS_CHART_TYPES.Line, + VIS_CHART_TYPES.Scatter, + VIS_CHART_TYPES.HorizontalBar, + VIS_CHART_TYPES.Bar, +]; + +export enum ConfigChartOptionsEnum { + palettePicker = 'palettePicker', + singleColorPicker = 'singleColorPicker', + colorpicker = 'colorpicker', + treemapColorPicker = 'treemapColorPicker', + input = 'input', + textInput = 'textInput', + slider = 'slider', + switchButton = 'switchButton', + buttons = 'buttons', +} + +export const CUSTOM_LABEL = 'customLabel'; +export const BREAKDOWNS = 'breakdowns'; +export const SPAN = 'span'; +export const TIME_FIELD = 'time_field'; +export const DISABLED_COLOUR = '#fafbfd'; +export const DATA_CONFIG_HINTS_INFO = { + [AGGREGATIONS]: + 'Series is an aggregation function (mandatory). The argument of an aggregation must be a field.', + [GROUPBY]: + "Dimensions are 'by' clauses. They are fields or expressions like scalar and aggregation functions. Besides, the span clause for a dimension can be used to split a specific field into buckets in the same interval, the stats then does the aggregation by these span buckets.", + [BREAKDOWNS]: + "Defines how each series is broken down. Breakdowns are 'by' clauses that subdivide the existing series.", +}; diff --git a/common/constants/shared.ts b/common/constants/shared.ts index 19c0fb8fd..0d56653ff 100644 --- a/common/constants/shared.ts +++ b/common/constants/shared.ts @@ -2,8 +2,8 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ - import CSS from 'csstype'; +import { IField } from '../../common/types/explorer'; // Client route export const PPL_BASE = '/api/ppl'; @@ -71,57 +71,121 @@ export const pageStyles: CSS.Properties = { maxWidth: '1130px', }; +export enum VIS_CHART_TYPES { + Bar = 'bar', + HorizontalBar = 'horizontal_bar', + Line = 'line', + Pie = 'pie', + HeatMap = 'heatmap', + Text = 'text', +} + export const NUMERICAL_FIELDS = ['short', 'integer', 'long', 'float', 'double']; -export const ENABLED_VIS_TYPES = ['bar', 'horizontal_bar', 'line', 'pie', 'heatmap', 'text']; +export const ENABLED_VIS_TYPES = [ + VIS_CHART_TYPES.Bar, + VIS_CHART_TYPES.HorizontalBar, + VIS_CHART_TYPES.Line, + VIS_CHART_TYPES.Pie, + VIS_CHART_TYPES.HeatMap, + VIS_CHART_TYPES.Text, +]; -//Live tail constants +// Live tail constants export const LIVE_OPTIONS = [ { - label:'5s', + label: '5s', startTime: 'now-5s', delayTime: 5000, }, { - label:'10s', + label: '10s', startTime: 'now-10s', delayTime: 10000, }, { - label:'30s', + label: '30s', startTime: 'now-30s', delayTime: 30000, }, { - label:'1m', + label: '1m', startTime: 'now-1m', delayTime: 60000, }, { - label:'5m', + label: '5m', startTime: 'now-5m', delayTime: 60000 * 5, }, { - label:'15m', + label: '15m', startTime: 'now-15m', delayTime: 60000 * 15, }, { - label:'30m', + label: '30m', startTime: 'now-30m', delayTime: 60000 * 30, }, { - label:'1h', + label: '1h', startTime: 'now-1h', delayTime: 60000 * 60, }, { - label:'2h', + label: '2h', startTime: 'now-2h', delayTime: 60000 * 120, }, ]; -export const LIVE_END_TIME ='now'; \ No newline at end of file +export const LIVE_END_TIME = 'now'; +export interface DefaultChartStylesProps { + DefaultModeLine: string; + Interpolation: string; + LineWidth: number; + FillOpacity: number; + MarkerSize: number; + ShowLegend: string; + LegendPosition: string; + LabelAngle: number; + DefaultSortSectors: string; + DefaultModeScatter: string; +} + +export const DEFAULT_CHART_STYLES: DefaultChartStylesProps = { + DefaultModeLine: 'lines', + Interpolation: 'spline', + LineWidth: 2, + FillOpacity: 70, + MarkerSize: 5, + ShowLegend: 'show', + LegendPosition: 'v', + LabelAngle: 0, + DefaultSortSectors: 'largest_to_smallest', + DefaultModeScatter: 'markers', +}; + +export const FILLOPACITY_DIV_FACTOR = 200; +export const SLIDER_MIN_VALUE = 0; +export const SLIDER_MAX_VALUE = 100; +export const SLIDER_STEP = 1; +export const THRESHOLD_LINE_WIDTH = 3; +export const THRESHOLD_LINE_OPACITY = 0.7; +export const MAX_BUCKET_LENGTH = 16; + +export enum BarOrientation { + horizontal = 'h', + vertical = 'v', +} + +export const PLOT_MARGIN = { + l: 30, + r: 5, + b: 30, + t: 50, + pad: 4, +}; + +export const WAITING_TIME_ON_USER_ACTIONS = 300; diff --git a/common/query_manager/antlr/adaptors/case_insensitive_char_stream.ts b/common/query_manager/antlr/adaptors/case_insensitive_char_stream.ts new file mode 100644 index 000000000..6c3414f35 --- /dev/null +++ b/common/query_manager/antlr/adaptors/case_insensitive_char_stream.ts @@ -0,0 +1,60 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CharStream } from 'antlr4ts'; + +export class CaseInsensitiveCharStream { + private stream: CharStream; + + get index(): number { + return this.stream.index; + } + + get size(): number { + return this.stream.size; + } + + get sourceName(): string { + return 'pplquery'; + } + + constructor(stream: CharStream) { + this.stream = stream; + } + + LA(offset: number): number { + const c: number = this.stream.LA(offset); + if (c <= 0) { + return c; + } + + // case insensitivity support for PPL + return String.fromCodePoint(c).toUpperCase().codePointAt(0)!; + } + + consume(): void { + this.stream.consume(); + } + + mark(): number { + return this.stream.mark(); + } + + release(marker: number): void { + this.stream.release(marker); + } + + seek(index: number): void { + this.stream.seek(index); + } + + getText(interval: any): string { + return this.stream.getText(interval); + } + + toString(): string { + return this.stream.toString(); + } +} diff --git a/common/query_manager/antlr/grammar/OpenSearchPPLLexer.g4 b/common/query_manager/antlr/grammar/OpenSearchPPLLexer.g4 new file mode 100644 index 000000000..c30967a77 --- /dev/null +++ b/common/query_manager/antlr/grammar/OpenSearchPPLLexer.g4 @@ -0,0 +1,333 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + + +lexer grammar OpenSearchPPLLexer; + +channels { WHITESPACE, ERRORCHANNEL } + + +// COMMAND KEYWORDS +SEARCH: 'SEARCH'; +FROM: 'FROM'; +WHERE: 'WHERE'; +FIELDS: 'FIELDS'; +RENAME: 'RENAME'; +STATS: 'STATS'; +DEDUP: 'DEDUP'; +SORT: 'SORT'; +EVAL: 'EVAL'; +HEAD: 'HEAD'; +TOP: 'TOP'; +RARE: 'RARE'; +PARSE: 'PARSE'; +KMEANS: 'KMEANS'; +AD: 'AD'; + +// COMMAND ASSIST KEYWORDS +AS: 'AS'; +BY: 'BY'; +SOURCE: 'SOURCE'; +INDEX: 'INDEX'; +D: 'D'; +DESC: 'DESC'; + +// CLAUSE KEYWORDS +SORTBY: 'SORTBY'; + +// FIELD KEYWORDS +AUTO: 'AUTO'; +STR: 'STR'; +IP: 'IP'; +NUM: 'NUM'; + +// ARGUMENT KEYWORDS +KEEPEMPTY: 'KEEPEMPTY'; +CONSECUTIVE: 'CONSECUTIVE'; +DEDUP_SPLITVALUES: 'DEDUP_SPLITVALUES'; +PARTITIONS: 'PARTITIONS'; +ALLNUM: 'ALLNUM'; +DELIM: 'DELIM'; +CENTROIDS: 'CENTROIDS'; +ITERATIONS: 'ITERATIONS'; +DISTANCE_TYPE: 'DISTANCE_TYPE'; +NUMBER_OF_TREES: 'NUMBER_OF_TREES'; +SHINGLE_SIZE: 'SHINGLE_SIZE'; +SAMPLE_SIZE: 'SAMPLE_SIZE'; +OUTPUT_AFTER: 'OUTPUT_AFTER'; +TIME_DECAY: 'TIME_DECAY'; +ANOMALY_RATE: 'ANOMALY_RATE'; +TIME_FIELD: 'TIME_FIELD'; +TIME_ZONE: 'TIME_ZONE'; +TRAINING_DATA_SIZE: 'TRAINING_DATA_SIZE'; +ANOMALY_SCORE_THRESHOLD: 'ANOMALY_SCORE_THRESHOLD'; + +// COMPARISON FUNCTION KEYWORDS +CASE: 'CASE'; +IN: 'IN'; + +// LOGICAL KEYWORDS +NOT: 'NOT'; +OR: 'OR'; +AND: 'AND'; +XOR: 'XOR'; +TRUE: 'TRUE'; +FALSE: 'FALSE'; +REGEXP: 'REGEXP'; + +// DATETIME, INTERVAL AND UNIT KEYWORDS +DATETIME: 'DATETIME'; +INTERVAL: 'INTERVAL'; +MICROSECOND: 'MICROSECOND'; +MILLISECOND: 'MILLISECOND'; +SECOND: 'SECOND'; +MINUTE: 'MINUTE'; +HOUR: 'HOUR'; +DAY: 'DAY'; +WEEK: 'WEEK'; +MONTH: 'MONTH'; +QUARTER: 'QUARTER'; +YEAR: 'YEAR'; +SECOND_MICROSECOND: 'SECOND_MICROSECOND'; +MINUTE_MICROSECOND: 'MINUTE_MICROSECOND'; +MINUTE_SECOND: 'MINUTE_SECOND'; +HOUR_MICROSECOND: 'HOUR_MICROSECOND'; +HOUR_SECOND: 'HOUR_SECOND'; +HOUR_MINUTE: 'HOUR_MINUTE'; +DAY_MICROSECOND: 'DAY_MICROSECOND'; +DAY_SECOND: 'DAY_SECOND'; +DAY_MINUTE: 'DAY_MINUTE'; +DAY_HOUR: 'DAY_HOUR'; +YEAR_MONTH: 'YEAR_MONTH'; + +// DATASET TYPES +DATAMODEL: 'DATAMODEL'; +LOOKUP: 'LOOKUP'; +SAVEDSEARCH: 'SAVEDSEARCH'; + +// CONVERTED DATA TYPES +INT: 'INT'; +INTEGER: 'INTEGER'; +DOUBLE: 'DOUBLE'; +LONG: 'LONG'; +FLOAT: 'FLOAT'; +STRING: 'STRING'; +BOOLEAN: 'BOOLEAN'; + +// SPECIAL CHARACTERS AND OPERATORS +PIPE: '|'; +COMMA: ','; +DOT: '.'; +EQUAL: '='; +GREATER: '>'; +LESS: '<'; +NOT_GREATER: '<' '='; +NOT_LESS: '>' '='; +NOT_EQUAL: '!' '='; +PLUS: '+'; +MINUS: '-'; +STAR: '*'; +DIVIDE: '/'; +MODULE: '%'; +EXCLAMATION_SYMBOL: '!'; +COLON: ':'; +LT_PRTHS: '('; +RT_PRTHS: ')'; +LT_SQR_PRTHS: '['; +RT_SQR_PRTHS: ']'; +SINGLE_QUOTE: '\''; +DOUBLE_QUOTE: '"'; +BACKTICK: '`'; + +// Operators. Bit + +BIT_NOT_OP: '~'; +BIT_AND_OP: '&'; +BIT_XOR_OP: '^'; + +// AGGREGATIONS +AVG: 'AVG'; +COUNT: 'COUNT'; +DISTINCT_COUNT: 'DISTINCT_COUNT'; +ESTDC: 'ESTDC'; +ESTDC_ERROR: 'ESTDC_ERROR'; +MAX: 'MAX'; +MEAN: 'MEAN'; +MEDIAN: 'MEDIAN'; +MIN: 'MIN'; +MODE: 'MODE'; +RANGE: 'RANGE'; +STDEV: 'STDEV'; +STDEVP: 'STDEVP'; +SUM: 'SUM'; +SUMSQ: 'SUMSQ'; +VAR_SAMP: 'VAR_SAMP'; +VAR_POP: 'VAR_POP'; +STDDEV_SAMP: 'STDDEV_SAMP'; +STDDEV_POP: 'STDDEV_POP'; +PERCENTILE: 'PERCENTILE'; +FIRST: 'FIRST'; +LAST: 'LAST'; +LIST: 'LIST'; +VALUES: 'VALUES'; +EARLIEST: 'EARLIEST'; +EARLIEST_TIME: 'EARLIEST_TIME'; +LATEST: 'LATEST'; +LATEST_TIME: 'LATEST_TIME'; +PER_DAY: 'PER_DAY'; +PER_HOUR: 'PER_HOUR'; +PER_MINUTE: 'PER_MINUTE'; +PER_SECOND: 'PER_SECOND'; +RATE: 'RATE'; +SPARKLINE: 'SPARKLINE'; +C: 'C'; +DC: 'DC'; + +// BASIC FUNCTIONS +ABS: 'ABS'; +CEIL: 'CEIL'; +CEILING: 'CEILING'; +CONV: 'CONV'; +CRC32: 'CRC32'; +E: 'E'; +EXP: 'EXP'; +FLOOR: 'FLOOR'; +LN: 'LN'; +LOG: 'LOG'; +LOG10: 'LOG10'; +LOG2: 'LOG2'; +MOD: 'MOD'; +PI: 'PI'; +POW: 'POW'; +POWER: 'POWER'; +RAND: 'RAND'; +ROUND: 'ROUND'; +SIGN: 'SIGN'; +SQRT: 'SQRT'; +TRUNCATE: 'TRUNCATE'; + +// TRIGONOMETRIC FUNCTIONS +ACOS: 'ACOS'; +ASIN: 'ASIN'; +ATAN: 'ATAN'; +ATAN2: 'ATAN2'; +COS: 'COS'; +COT: 'COT'; +DEGREES: 'DEGREES'; +RADIANS: 'RADIANS'; +SIN: 'SIN'; +TAN: 'TAN'; + +// DATE AND TIME FUNCTIONS +ADDDATE: 'ADDDATE'; +DATE: 'DATE'; +DATE_ADD: 'DATE_ADD'; +DATE_SUB: 'DATE_SUB'; +DAYOFMONTH: 'DAYOFMONTH'; +DAYOFWEEK: 'DAYOFWEEK'; +DAYOFYEAR: 'DAYOFYEAR'; +DAYNAME: 'DAYNAME'; +FROM_DAYS: 'FROM_DAYS'; +MONTHNAME: 'MONTHNAME'; +SUBDATE: 'SUBDATE'; +TIME: 'TIME'; +TIME_TO_SEC: 'TIME_TO_SEC'; +TIMESTAMP: 'TIMESTAMP'; +DATE_FORMAT: 'DATE_FORMAT'; +TO_DAYS: 'TO_DAYS'; + +// TEXT FUNCTIONS +SUBSTR: 'SUBSTR'; +SUBSTRING: 'SUBSTRING'; +LTRIM: 'LTRIM'; +RTRIM: 'RTRIM'; +TRIM: 'TRIM'; +TO: 'TO'; +LOWER: 'LOWER'; +UPPER: 'UPPER'; +CONCAT: 'CONCAT'; +CONCAT_WS: 'CONCAT_WS'; +LENGTH: 'LENGTH'; +STRCMP: 'STRCMP'; +RIGHT: 'RIGHT'; +LEFT: 'LEFT'; +ASCII: 'ASCII'; +LOCATE: 'LOCATE'; +REPLACE: 'REPLACE'; +CAST: 'CAST'; + +// BOOL FUNCTIONS +LIKE: 'LIKE'; +ISNULL: 'ISNULL'; +ISNOTNULL: 'ISNOTNULL'; + +// FLOWCONTROL FUNCTIONS +IFNULL: 'IFNULL'; +NULLIF: 'NULLIF'; +IF: 'IF'; + +// RELEVANCE FUNCTIONS AND PARAMETERS +MATCH: 'MATCH'; +MATCH_PHRASE: 'MATCH_PHRASE'; +SIMPLE_QUERY_STRING: 'SIMPLE_QUERY_STRING'; + +ALLOW_LEADING_WILDCARD: 'ALLOW_LEADING_WILDCARD'; +ANALYZE_WILDCARD: 'ANALYZE_WILDCARD'; +ANALYZER: 'ANALYZER'; +AUTO_GENERATE_SYNONYMS_PHRASE_QUERY:'AUTO_GENERATE_SYNONYMS_PHRASE_QUERY'; +BOOST: 'BOOST'; +CUTOFF_FREQUENCY: 'CUTOFF_FREQUENCY'; +DEFAULT_FIELD: 'DEFAULT_FIELD'; +DEFAULT_OPERATOR: 'DEFAULT_OPERATOR'; +ENABLE_POSITION_INCREMENTS: 'ENABLE_POSITION_INCREMENTS'; +FLAGS: 'FLAGS'; +FUZZY_MAX_EXPANSIONS: 'FUZZY_MAX_EXPANSIONS'; +FUZZY_PREFIX_LENGTH: 'FUZZY_PREFIX_LENGTH'; +FUZZY_TRANSPOSITIONS: 'FUZZY_TRANSPOSITIONS'; +FUZZY_REWRITE: 'FUZZY_REWRITE'; +FUZZINESS: 'FUZZINESS'; +LENIENT: 'LENIENT'; +LOW_FREQ_OPERATOR: 'LOW_FREQ_OPERATOR'; +MAX_DETERMINIZED_STATES: 'MAX_DETERMINIZED_STATES'; +MAX_EXPANSIONS: 'MAX_EXPANSIONS'; +MINIMUM_SHOULD_MATCH: 'MINIMUM_SHOULD_MATCH'; +OPERATOR: 'OPERATOR'; +PHRASE_SLOP: 'PHRASE_SLOP'; +PREFIX_LENGTH: 'PREFIX_LENGTH'; +QUOTE_ANALYZER: 'QUOTE_ANALYZER'; +QUOTE_FIELD_SUFFIX: 'QUOTE_FIELD_SUFFIX'; +REWRITE: 'REWRITE'; +SLOP: 'SLOP'; +TIE_BREAKER: 'TIE_BREAKER'; +TYPE: 'TYPE'; +ZERO_TERMS_QUERY: 'ZERO_TERMS_QUERY'; + +// SPAN KEYWORDS +SPAN: 'SPAN'; +MS: 'MS'; +S: 'S'; +M: 'M'; +H: 'H'; +W: 'W'; +Q: 'Q'; +Y: 'Y'; + + +// LITERALS AND VALUES +//STRING_LITERAL: DQUOTA_STRING | SQUOTA_STRING | BQUOTA_STRING; +ID: ID_LITERAL; +INTEGER_LITERAL: DEC_DIGIT+; +DECIMAL_LITERAL: (DEC_DIGIT+)? '.' DEC_DIGIT+; + +fragment DATE_SUFFIX: ([\-.][*0-9]+)*; +fragment ID_LITERAL: [@*A-Z]+?[*A-Z_\-0-9]*; +ID_DATE_SUFFIX: ID_LITERAL DATE_SUFFIX; +DQUOTA_STRING: '"' ( '\\'. | '""' | ~('"'| '\\') )* '"'; +SQUOTA_STRING: '\'' ('\\'. | '\'\'' | ~('\'' | '\\'))* '\''; +BQUOTA_STRING: '`' ( '\\'. | '``' | ~('`'|'\\'))* '`'; +fragment DEC_DIGIT: [0-9]; + + +ERROR_RECOGNITION: . -> channel(ERRORCHANNEL); \ No newline at end of file diff --git a/common/query_manager/antlr/grammar/OpenSearchPPLParser.g4 b/common/query_manager/antlr/grammar/OpenSearchPPLParser.g4 new file mode 100644 index 000000000..de96295f9 --- /dev/null +++ b/common/query_manager/antlr/grammar/OpenSearchPPLParser.g4 @@ -0,0 +1,475 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + + + +parser grammar OpenSearchPPLParser; +options { tokenVocab=OpenSearchPPLLexer; } + +root + : pplStatement? EOF + ; + +/** statement */ +pplStatement + : searchCommand (PIPE commands)* + ; + +/** commands */ +commands + : whereCommand | fieldsCommand | renameCommand | statsCommand | dedupCommand | sortCommand | evalCommand | headCommand + | topCommand | rareCommand | parseCommand | kmeansCommand | adCommand; + +searchCommand + : (SEARCH)? fromClause #searchFrom + | (SEARCH)? fromClause logicalExpression #searchFromFilter + | (SEARCH)? logicalExpression fromClause #searchFilterFrom + ; + +whereCommand + : WHERE logicalExpression + ; + +fieldsCommand + : FIELDS (PLUS | MINUS)? fieldList + ; + +renameCommand + : RENAME renameClasue (COMMA renameClasue)* + ; + +statsCommand + : STATS + (PARTITIONS EQUAL partitions=integerLiteral)? + (ALLNUM EQUAL allnum=booleanLiteral)? + (DELIM EQUAL delim=stringLiteral)? + statsAggTerm (COMMA statsAggTerm)* + (statsByClause)? + (DEDUP_SPLITVALUES EQUAL dedupsplit=booleanLiteral)? + ; + +dedupCommand + : DEDUP + (number=integerLiteral)? + fieldList + (KEEPEMPTY EQUAL keepempty=booleanLiteral)? + (CONSECUTIVE EQUAL consecutive=booleanLiteral)? + ; + +sortCommand + : SORT sortbyClause + ; + +evalCommand + : EVAL evalClause (COMMA evalClause)* + ; + +headCommand + : HEAD + (number=integerLiteral)? + (FROM from=integerLiteral)? + ; + +topCommand + : TOP + (number=integerLiteral)? + fieldList + (byClause)? + ; + +rareCommand + : RARE + fieldList + (byClause)? + ; + +parseCommand + : PARSE expression pattern + ; + +kmeansCommand + : KMEANS (kmeansParameter)* + ; + +kmeansParameter + : (CENTROIDS EQUAL centroids=integerLiteral) + | (ITERATIONS EQUAL iterations=integerLiteral) + | (DISTANCE_TYPE EQUAL distance_type=stringLiteral) + ; + +adCommand + : AD (adParameter)* + ; + +adParameter + : (NUMBER_OF_TREES EQUAL number_of_trees=integerLiteral) + | (SHINGLE_SIZE EQUAL shingle_size=integerLiteral) + | (SAMPLE_SIZE EQUAL sample_size=integerLiteral) + | (OUTPUT_AFTER EQUAL output_after=integerLiteral) + | (TIME_DECAY EQUAL time_decay=decimalLiteral) + | (ANOMALY_RATE EQUAL anomaly_rate=decimalLiteral) + | (TIME_FIELD EQUAL time_field=stringLiteral) + | (DATE_FORMAT EQUAL date_format=stringLiteral) + | (TIME_ZONE EQUAL time_zone=stringLiteral) + | (TRAINING_DATA_SIZE EQUAL training_data_size=integerLiteral) + | (ANOMALY_SCORE_THRESHOLD EQUAL anomaly_score_threshold=decimalLiteral) + ; + +/** clauses */ +fromClause + : SOURCE EQUAL tableSource (COMMA tableSource)* + | INDEX EQUAL tableSource (COMMA tableSource)* + ; + +renameClasue + : orignalField=wcFieldExpression AS renamedField=wcFieldExpression + ; + +byClause + : BY fieldList + ; + +statsByClause + : BY fieldList + | BY bySpanClause + | BY bySpanClause COMMA fieldList + ; + +bySpanClause + : spanClause (AS alias=qualifiedName)? + ; + +spanClause + : SPAN LT_PRTHS fieldExpression COMMA value=literalValue (unit=timespanUnit)? RT_PRTHS + ; + +sortbyClause + : sortField (COMMA sortField)* + ; + +evalClause + : fieldExpression EQUAL expression + ; + +/** aggregation terms */ +statsAggTerm + : statsFunction (AS alias=wcFieldExpression)? + ; + +/** aggregation functions */ +statsFunction + : statsFunctionName LT_PRTHS valueExpression RT_PRTHS #statsFunctionCall + | COUNT LT_PRTHS RT_PRTHS #countAllFunctionCall + | (DISTINCT_COUNT | DC) LT_PRTHS valueExpression RT_PRTHS #distinctCountFunctionCall + | percentileAggFunction #percentileAggFunctionCall + ; + +statsFunctionName + : AVG | COUNT | SUM | MIN | MAX | VAR_SAMP | VAR_POP | STDDEV_SAMP | STDDEV_POP + ; + +percentileAggFunction + : PERCENTILE LESS value=integerLiteral GREATER LT_PRTHS aggField=fieldExpression RT_PRTHS + ; + +/** expressions */ +expression + : logicalExpression + | comparisonExpression + | valueExpression + ; + +logicalExpression + : comparisonExpression #comparsion + | NOT logicalExpression #logicalNot + | left=logicalExpression OR right=logicalExpression #logicalOr + | left=logicalExpression (AND)? right=logicalExpression #logicalAnd + | left=logicalExpression XOR right=logicalExpression #logicalXor + | booleanExpression #booleanExpr + | relevanceExpression #relevanceExpr + ; + +comparisonExpression + : left=valueExpression comparisonOperator right=valueExpression #compareExpr + | valueExpression IN valueList #inExpr + ; + +valueExpression + : left=valueExpression binaryOperator right=valueExpression #binaryArithmetic + | LT_PRTHS left=valueExpression binaryOperator + right=valueExpression RT_PRTHS #parentheticBinaryArithmetic + | primaryExpression #valueExpressionDefault + ; + +primaryExpression + : evalFunctionCall + | dataTypeFunctionCall + | fieldExpression + | literalValue + ; + +booleanExpression + : booleanFunctionCall + ; + +relevanceExpression + : singleFieldRelevanceFunction | multiFieldRelevanceFunction + ; + +// Field is a single column +singleFieldRelevanceFunction + : singleFieldRelevanceFunctionName LT_PRTHS + field=relevanceField COMMA query=relevanceQuery + (COMMA relevanceArg)* RT_PRTHS + ; + +// Field is a list of columns +multiFieldRelevanceFunction + : multiFieldRelevanceFunctionName LT_PRTHS + LT_SQR_PRTHS field=relevanceFieldAndWeight (COMMA field=relevanceFieldAndWeight)* RT_SQR_PRTHS + COMMA query=relevanceQuery (COMMA relevanceArg)* RT_PRTHS + ; + +/** tables */ +tableSource + : qualifiedName + | ID_DATE_SUFFIX + ; + +/** fields */ +fieldList + : fieldExpression (COMMA fieldExpression)* + ; + +wcFieldList + : wcFieldExpression (COMMA wcFieldExpression)* + ; + +sortField + : (PLUS | MINUS)? sortFieldExpression + ; + +sortFieldExpression + : fieldExpression + | AUTO LT_PRTHS fieldExpression RT_PRTHS + | STR LT_PRTHS fieldExpression RT_PRTHS + | IP LT_PRTHS fieldExpression RT_PRTHS + | NUM LT_PRTHS fieldExpression RT_PRTHS + ; + +fieldExpression + : qualifiedName + ; + +wcFieldExpression + : wcQualifiedName + ; + +/** functions */ +evalFunctionCall + : evalFunctionName LT_PRTHS functionArgs RT_PRTHS + ; + +/** cast function */ +dataTypeFunctionCall + : CAST LT_PRTHS expression AS convertedDataType RT_PRTHS + ; + +/** boolean functions */ +booleanFunctionCall + : conditionFunctionBase LT_PRTHS functionArgs RT_PRTHS + ; + +convertedDataType + : typeName=DATE + | typeName=TIME + | typeName=TIMESTAMP + | typeName=INT + | typeName=INTEGER + | typeName=DOUBLE + | typeName=LONG + | typeName=FLOAT + | typeName=STRING + | typeName=BOOLEAN + ; + +evalFunctionName + : mathematicalFunctionBase + | dateAndTimeFunctionBase + | textFunctionBase + | conditionFunctionBase + ; + +functionArgs + : (functionArg (COMMA functionArg)*)? + ; + +functionArg + : valueExpression + ; + +relevanceArg + : relevanceArgName EQUAL relevanceArgValue + ; + +relevanceArgName + : ALLOW_LEADING_WILDCARD | ANALYZER | ANALYZE_WILDCARD | AUTO_GENERATE_SYNONYMS_PHRASE_QUERY + | BOOST | CUTOFF_FREQUENCY | DEFAULT_FIELD | DEFAULT_OPERATOR | ENABLE_POSITION_INCREMENTS + | FIELDS | FLAGS | FUZZINESS | FUZZY_MAX_EXPANSIONS | FUZZY_PREFIX_LENGTH | FUZZY_REWRITE + | FUZZY_TRANSPOSITIONS | LENIENT | LOW_FREQ_OPERATOR | MAX_DETERMINIZED_STATES + | MAX_EXPANSIONS | MINIMUM_SHOULD_MATCH | OPERATOR | PHRASE_SLOP | PREFIX_LENGTH + | QUOTE_ANALYZER | QUOTE_FIELD_SUFFIX | REWRITE | SLOP | TIE_BREAKER | TIME_ZONE | TYPE + | ZERO_TERMS_QUERY + ; + +relevanceFieldAndWeight + : field=relevanceField + | field=relevanceField weight=relevanceFieldWeight + | field=relevanceField BIT_XOR_OP weight=relevanceFieldWeight + ; + +relevanceFieldWeight + : integerLiteral + | decimalLiteral + ; + +relevanceField + : qualifiedName + | stringLiteral + ; + +relevanceQuery + : relevanceArgValue + ; + +relevanceArgValue + : qualifiedName + | literalValue + ; + +mathematicalFunctionBase + : ABS | CEIL | CEILING | CONV | CRC32 | E | EXP | FLOOR | LN | LOG | LOG10 | LOG2 | MOD | PI |POW | POWER + | RAND | ROUND | SIGN | SQRT | TRUNCATE + | trigonometricFunctionName + ; + +trigonometricFunctionName + : ACOS | ASIN | ATAN | ATAN2 | COS | COT | DEGREES | RADIANS | SIN | TAN + ; + +dateAndTimeFunctionBase + : ADDDATE | DATE | DATE_ADD | DATE_SUB | DAY | DAYNAME | DAYOFMONTH | DAYOFWEEK | DAYOFYEAR | FROM_DAYS + | HOUR | MICROSECOND | MINUTE | MONTH | MONTHNAME | QUARTER | SECOND | SUBDATE | TIME | TIME_TO_SEC + | TIMESTAMP | TO_DAYS | YEAR | WEEK | DATE_FORMAT + ; + +/** condition function return boolean value */ +conditionFunctionBase + : LIKE + | IF | ISNULL | ISNOTNULL | IFNULL | NULLIF + ; + +textFunctionBase + : SUBSTR | SUBSTRING | TRIM | LTRIM | RTRIM | LOWER | UPPER | CONCAT | CONCAT_WS | LENGTH | STRCMP + | RIGHT | LEFT | ASCII | LOCATE | REPLACE + ; + +/** operators */ +comparisonOperator + : EQUAL | NOT_EQUAL | LESS | NOT_LESS | GREATER | NOT_GREATER | REGEXP + ; + +binaryOperator + : PLUS | MINUS | STAR | DIVIDE | MODULE + ; + + +singleFieldRelevanceFunctionName + : MATCH + | MATCH_PHRASE + ; + +multiFieldRelevanceFunctionName + : SIMPLE_QUERY_STRING + ; + +/** literals and values*/ +literalValue + : intervalLiteral + | stringLiteral + | integerLiteral + | decimalLiteral + | booleanLiteral + ; + +intervalLiteral + : INTERVAL valueExpression intervalUnit + ; + +stringLiteral + : DQUOTA_STRING | SQUOTA_STRING + ; + +integerLiteral + : (PLUS | MINUS)? INTEGER_LITERAL + ; + +decimalLiteral + : (PLUS | MINUS)? DECIMAL_LITERAL + ; + +booleanLiteral + : TRUE | FALSE + ; + +pattern + : stringLiteral + ; + +intervalUnit + : MICROSECOND | SECOND | MINUTE | HOUR | DAY | WEEK | MONTH | QUARTER | YEAR | SECOND_MICROSECOND + | MINUTE_MICROSECOND | MINUTE_SECOND | HOUR_MICROSECOND | HOUR_SECOND | HOUR_MINUTE | DAY_MICROSECOND + | DAY_SECOND | DAY_MINUTE | DAY_HOUR | YEAR_MONTH + ; + +timespanUnit + : MS | S | M | H | D | W | Q | Y + | MILLISECOND | SECOND | MINUTE | HOUR | DAY | WEEK | MONTH | QUARTER | YEAR + ; + + +valueList + : LT_PRTHS literalValue (COMMA literalValue)* RT_PRTHS + ; + +qualifiedName + : ident (DOT ident)* #identsAsQualifiedName + ; + +wcQualifiedName + : wildcard (DOT wildcard)* #identsAsWildcardQualifiedName + ; + +ident + : (DOT)? ID + | BACKTICK ident BACKTICK + | BQUOTA_STRING + | keywordsCanBeId + ; + +wildcard + : ident (MODULE ident)* (MODULE)? + | SINGLE_QUOTE wildcard SINGLE_QUOTE + | DOUBLE_QUOTE wildcard DOUBLE_QUOTE + | BACKTICK wildcard BACKTICK + ; + +keywordsCanBeId + : D // OD SQL and ODBC special + | statsFunctionName + | TIMESTAMP | DATE | TIME + | FIRST | LAST + | timespanUnit | SPAN + ; \ No newline at end of file diff --git a/common/query_manager/antlr/ppl_syntax_parser.ts b/common/query_manager/antlr/ppl_syntax_parser.ts new file mode 100644 index 000000000..6f62bce1c --- /dev/null +++ b/common/query_manager/antlr/ppl_syntax_parser.ts @@ -0,0 +1,30 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CharStreams, CommonTokenStream } from 'antlr4ts'; +import { CaseInsensitiveCharStream } from './adaptors/case_insensitive_char_stream'; +import { OpenSearchPPLLexer } from './output/OpenSearchPPLLexer'; +import { OpenSearchPPLParser } from './output/OpenSearchPPLParser'; + +/** + * PPL Syntax Parser. + */ +export class PPLSyntaxParser { + /** + * Analyze the query syntax. + */ + + parse(query: string) { + return this.createParser(this.createLexer(query)); + } + + createLexer(query: string) { + return new OpenSearchPPLLexer(new CaseInsensitiveCharStream(CharStreams.fromString(query))); + } + + createParser(lexer: OpenSearchPPLLexer) { + return new OpenSearchPPLParser(new CommonTokenStream(lexer)); + } +} diff --git a/common/query_manager/ast/builder/query_builder.ts b/common/query_manager/ast/builder/query_builder.ts new file mode 100644 index 000000000..a6b6494c0 --- /dev/null +++ b/common/query_manager/ast/builder/query_builder.ts @@ -0,0 +1,8 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export interface QueryBuilder { + build: () => T; +} diff --git a/common/query_manager/ast/builder/stats_ast_builder.ts b/common/query_manager/ast/builder/stats_ast_builder.ts new file mode 100644 index 000000000..6a396654a --- /dev/null +++ b/common/query_manager/ast/builder/stats_ast_builder.ts @@ -0,0 +1,232 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { isFunction, isEqual } from 'lodash'; +import { AbstractParseTreeVisitor } from 'antlr4ts/tree/AbstractParseTreeVisitor'; +import { + RootContext, + PplStatementContext, + CommandsContext, + StatsCommandContext, + BooleanLiteralContext, + BySpanClauseContext, + FieldExpressionContext, + FieldListContext, + IntegerLiteralContext, + LiteralValueContext, + QualifiedNameContext, + SpanClauseContext, + StatsAggTermContext, + StatsByClauseContext, + StatsFunctionContext, + StatsFunctionNameContext, + StringLiteralContext, + TimespanUnitContext, + ValueExpressionContext, + WcFieldExpressionContext, +} from '../../antlr/output/OpenSearchPPLParser'; +import { OpenSearchPPLParserVisitor } from '../../antlr/output/OpenSearchPPLParserVisitor'; +import { PPLNode } from '../node'; +import { Aggregations } from '../tree/aggragations'; +import { + AggregateFunction, + AggregateTerm, + GroupBy, + Field, + Span, + SpanExpression, +} from '../expression'; + +type VisitResult = PPLNode | Array | string; + +export class StatsAstBuilder + extends AbstractParseTreeVisitor + implements OpenSearchPPLParserVisitor { + protected defaultResult(): PPLNode { + return new PPLNode('default', [] as Array); + } + + visitRoot(ctx: RootContext) { + if (!ctx.pplStatement()) return this.defaultResult(); + return this.visitPplStatement(ctx.pplStatement()!); + } + + visitPplStatement(ctx: PplStatementContext): PPLNode { + let statsTree: VisitResult = this.defaultResult(); + ctx.commands().map((pplCommandContext) => { + if ( + isFunction(this.visitChildren(pplCommandContext).getName) && + isEqual(this.visitChildren(pplCommandContext).getName(), 'stats_command') + ) + statsTree = this.visitChildren(pplCommandContext); + }); + return statsTree; + } + + visitCommands(ctx: CommandsContext) { + if (ctx.statsCommand()) { + return this.visitStatsCommand(ctx.statsCommand()!); + } + return this.defaultResult(); + } + + /** + * Stats command + */ + visitStatsCommand(ctx: StatsCommandContext): PPLNode { + return new Aggregations( + 'stats_command', + [] as Array, + ctx.PARTITIONS() && ctx.integerLiteral() + ? { + keyword: ctx.PARTITIONS()?.text, + sign: '=', + value: ctx.integerLiteral()?.text, + } + : {}, // visit partitions partial + ctx.ALLNUM() && ctx.booleanLiteral() + ? { + keyword: ctx.ALLNUM()?.text, + sign: '=', + value: this.visitBooleanLiteral(ctx.booleanLiteral()[0]), + } + : {}, // visit allnum partial + ctx.DELIM() && ctx.stringLiteral() + ? { + keyword: ctx.DELIM()?.text, + sign: '=', + value: this.visitStringLiteral(ctx.stringLiteral()!), + } + : '', // visit delim partial + ctx.statsAggTerm().map((aggTermAlternative) => this.visitStatsAggTerm(aggTermAlternative)), // visit statsAggTerm + ctx.statsByClause() ? this.visitStatsByClause(ctx.statsByClause()!) : ({} as GroupBy), // visit group list + ctx.DEDUP_SPLITVALUES() && ctx.booleanLiteral() + ? { + keyword: ctx.DEDUP_SPLITVALUES()?.text, + sign: '=', + value: this.visitBooleanLiteral(ctx.booleanLiteral()[1]), + } + : '', // visit dedup split value + { + start: ctx.start.startIndex, + end: ctx.stop?.stopIndex, + } // stats start/end indices in query for later query concatenation + ); + } + + visitIntegerLiteral(ctx: IntegerLiteralContext): string { + return ctx.text; + } + + visitBooleanLiteral(ctx: BooleanLiteralContext): string { + return ctx.text; + } + + visitStringLiteral(ctx: StringLiteralContext): string { + return ctx.text; + } + + visitStatsAggTerm(ctx: StatsAggTermContext): PPLNode { + return new AggregateTerm( + 'stats_agg_term', + [] as Array, + this.visitStatsFunction(ctx.statsFunction()), + ctx.wcFieldExpression() ? this.visitWcFieldExpression(ctx.wcFieldExpression()!) : '' + ); + } + + visitWcFieldExpression(ctx: WcFieldExpressionContext): string { + // return only text from here to all its chilren for now + return ctx.wcQualifiedName().text; + } + + visitStatsByClause(ctx: StatsByClauseContext): PPLNode { + return new GroupBy( + 'stats_by_clause', + [] as Array, + ctx.fieldList() ? this.visitFieldList(ctx.fieldList()!) : [], + ctx.bySpanClause() ? this.visitBySpanClause(ctx.bySpanClause()!) : this.defaultResult() + ); + } + + visitBySpanClause(ctx: BySpanClauseContext): PPLNode { + return new Span( + 'span_clause', + [] as Array, + this.visitSpanClause(ctx.spanClause()), + ctx.qualifiedName() ? this.visitQualifiedName(ctx.qualifiedName()!) : '' + ); + } + + visitSpanClause(ctx: SpanClauseContext): PPLNode { + return new SpanExpression( + 'span_expression', + [] as Array, + this.visitFieldExpression(ctx.fieldExpression()), + this.visitLiteralValue(ctx.literalValue()), + ctx.timespanUnit() ? this.visitTimespanUnit(ctx.timespanUnit()!) : '' + ); + } + + visitLiteralValue(ctx: LiteralValueContext): string { + return ctx.text; + } + + visitTimespanUnit(ctx: TimespanUnitContext): string { + return ctx.text; + } + + visitStatsFunction(ctx: StatsFunctionContext): PPLNode { + let funcName = ''; + let valueExpr = ''; + + if (typeof ctx.valueExpression === 'function') { + valueExpr = this.visitValueExpression(ctx.valueExpression()); + if (typeof ctx.statsFunctionName === 'function') { + funcName = this.visitStatsFunctionName(ctx.statsFunctionName()); + } else { + funcName = ctx.DISTINCT_COUNT() ? ctx.DISTINCT_COUNT().text : ctx.DC().text; + } + } else if (typeof ctx.percentileAggFunction === 'function') { + // for now just return plain text + } else { + funcName = ctx.COUNT().text; + } + + return new AggregateFunction( + 'stats_function', + [] as Array, + funcName, + valueExpr, + typeof ctx.percentileAggFunction === 'function' ? ctx.text : '' + ); + } + + visitValueExpression(ctx: ValueExpressionContext): string { + return ctx.text; + } + + visitStatsFunctionName(ctx: StatsFunctionNameContext): string { + return ctx.text; + } + + visitFieldList(ctx: FieldListContext): Array { + return ctx.fieldExpression().map((fieldExprAlternative) => { + return new Field( + 'field_expression', + [] as Array, + this.visitFieldExpression(fieldExprAlternative) + ); + }); + } + + visitFieldExpression(ctx: FieldExpressionContext): string { + return this.visitQualifiedName(ctx.qualifiedName()); + } + + visitQualifiedName(ctx: QualifiedNameContext): string { + return ctx.text; + } +} diff --git a/common/query_manager/ast/builder/stats_builder.ts b/common/query_manager/ast/builder/stats_builder.ts new file mode 100644 index 000000000..064ac8c5d --- /dev/null +++ b/common/query_manager/ast/builder/stats_builder.ts @@ -0,0 +1,136 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { isEmpty } from 'lodash'; +import { QueryBuilder } from './query_builder'; +import { Aggregations } from '../tree/aggragations'; +import { PPLNode } from '../node'; +import { + AggregateFunction, + AggregateTerm, + Field, + GroupBy, + Span, + SpanExpression, +} from '../expression'; +import { + ExpressionChunk, + SpanChunk, + StatsAggregationChunk, + StatsAggregationFunctionChunk, + GroupByChunk, + GroupField, + StatsChunk, + SpanExpressionChunk, +} from '../types'; +import { CUSTOM_LABEL } from '../../../../common/constants/explorer'; + +export class StatsBuilder implements QueryBuilder { + constructor(private statsChunk: StatsChunk) {} + + build(): Aggregations { + // return a new stats subtree + return new Aggregations( + 'stats_command', + [] as PPLNode[], + !isEmpty(this.statsChunk.partitions) ? this.buildParttions(this.statsChunk.partitions) : '', + !isEmpty(this.statsChunk.all_num) ? this.buildAllNum(this.statsChunk.all_num) : '', + !isEmpty(this.statsChunk.delim) ? this.buildDelim(this.statsChunk.delim) : '', + !isEmpty(this.statsChunk.aggregations) + ? this.buildAggList(this.statsChunk.aggregations) + : ([] as PPLNode[]), + !isEmpty(this.statsChunk.groupby) + ? this.buildGroupList(this.statsChunk.groupby) + : new GroupBy('stats_by_clause', [] as PPLNode[], [], null), + !isEmpty(this.statsChunk.dedup_split_value) + ? this.buildDedupSplitValue(this.statsChunk.dedup_split_value) + : '' + ); + } + + /** + * Flags + */ + buildParttions(partitions: ExpressionChunk) { + return `${partitions.keyword} ${partitions.sign} ${partitions.value}`; + } + + buildAllNum(allNum: ExpressionChunk) { + return `${allNum.keyword} ${allNum.sign} ${allNum.value}`; + } + + buildDelim(delim: ExpressionChunk) { + return `${delim.keyword} ${delim.sign} ${delim.value}`; + } + + buildDedupSplitValue(dedupSplitvalue: ExpressionChunk) { + return `${dedupSplitvalue.keyword} ${dedupSplitvalue.sign} ${dedupSplitvalue.value}`; + } + + /** + * Aggregations + */ + buildAggList(aggregations: StatsAggregationChunk[]) { + return aggregations.map((aggregation) => { + return this.buildAggTerm(aggregation); + }); + } + + buildAggTerm(aggTerm: StatsAggregationChunk) { + return new AggregateTerm( + 'stats_agg_term', + [] as PPLNode[], + this.buildAggregateFunction(aggTerm.function), + aggTerm.function_alias + ); + } + + buildAggregateFunction(aggFunction: StatsAggregationFunctionChunk) { + return new AggregateFunction( + 'stats_function', + [] as PPLNode[], + aggFunction.name, + aggFunction.value_expression, + aggFunction.percentile_agg_function + ); + } + + /** + * Groups + */ + buildGroupList(groupby: GroupByChunk) { + return new GroupBy( + 'stats_by_clause', + [] as PPLNode[], + this.buildFieldList(groupby.group_fields), + groupby.span ? this.buildSpan(groupby.span) : null + ); + } + + buildFieldList(group_fields: GroupField[]) { + return group_fields.map((gf: GroupField) => { + return new Field('field_expression', [] as PPLNode[], gf.name); + }); + } + + buildSpan(span: SpanChunk) { + return new Span( + 'span_clause', + [] as PPLNode[], + this.buildeSpanExpression(span.span_expression), + span[CUSTOM_LABEL] + ); + } + + buildeSpanExpression(spanExpression: SpanExpressionChunk) { + return new SpanExpression( + 'span_expression', + [] as PPLNode[], + spanExpression.field, + spanExpression.literal_value, + spanExpression.time_unit + ); + } +} diff --git a/common/query_manager/ast/expression/AggregateFunction.ts b/common/query_manager/ast/expression/AggregateFunction.ts new file mode 100644 index 000000000..06c05cec6 --- /dev/null +++ b/common/query_manager/ast/expression/AggregateFunction.ts @@ -0,0 +1,37 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PPLNode } from '../node'; + +export class AggregateFunction extends PPLNode { + constructor( + name: string, + children: Array, + private statsFunctionName: string, + private valueExpression: string, + private percentileAggFunction: string + ) { + super(name, children); + } + + getTokens() { + return { + name: this.statsFunctionName, + value_expression: this.valueExpression, + percentile_agg_function: this.percentileAggFunction, + }; + } + + toString(): string { + if (this.statsFunctionName && this.valueExpression) { + return `${this.statsFunctionName}(${this.valueExpression})`; + } else if (this.statsFunctionName) { + return `${this.statsFunctionName}()`; + } else if (this.percentileAggFunction) { + return `${this.percentileAggFunction}`; + } + return ''; + } +} diff --git a/common/query_manager/ast/expression/AggregateTerm.ts b/common/query_manager/ast/expression/AggregateTerm.ts new file mode 100644 index 000000000..9864bbfcd --- /dev/null +++ b/common/query_manager/ast/expression/AggregateTerm.ts @@ -0,0 +1,32 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ +import { CUSTOM_LABEL } from '../../../../common/constants/explorer'; +import { PPLNode } from '../node'; +export class AggregateTerm extends PPLNode { + constructor( + name: string, + children: PPLNode[], + private statsFunction: PPLNode, + private customLabel: string + ) { + super(name, children); + } + + getTokens() { + return { + function: this.statsFunction.getTokens(), + [CUSTOM_LABEL]: this[CUSTOM_LABEL], + }; + } + + toString(): string { + if (this[CUSTOM_LABEL]) { + return `${this.statsFunction.toString()}${ + this[CUSTOM_LABEL] ? ` as ${this[CUSTOM_LABEL]}` : '' + }`; + } + return `${this.statsFunction.toString()}`; + } +} diff --git a/common/query_manager/ast/expression/field.ts b/common/query_manager/ast/expression/field.ts new file mode 100644 index 000000000..3bc35ce70 --- /dev/null +++ b/common/query_manager/ast/expression/field.ts @@ -0,0 +1,20 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PPLNode } from '../node'; + +export class Field extends PPLNode { + constructor(name: string, children: Array, private fieldExpression: string) { + super(name, children); + } + + getTokens() { + return { name: this.fieldExpression ?? '' }; + } + + toString(): string { + return this.fieldExpression ?? ''; + } +} diff --git a/common/query_manager/ast/expression/group_by.ts b/common/query_manager/ast/expression/group_by.ts new file mode 100644 index 000000000..ce6d8ffef --- /dev/null +++ b/common/query_manager/ast/expression/group_by.ts @@ -0,0 +1,31 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PPLNode } from '../node'; +import { Field } from './field'; + +export class GroupBy extends PPLNode { + constructor( + name: string, + children: Array, + private fields: Array, + private span: PPLNode | null + ) { + super(name, children); + } + + getTokens() { + return { + group_fields: this.fields.map((field) => field.getTokens()), + span: this.span?.getTokens(), + }; + } + + toString(): string { + return `by ${this.span ? `${this.span.toString()}${this.fields.length > 0 ? ', ' : ''}` : ''}${this.fields + .map((field) => field.toString()) + .join(', ')}`; + } +} diff --git a/common/query_manager/ast/expression/index.ts b/common/query_manager/ast/expression/index.ts new file mode 100644 index 000000000..222ca12ca --- /dev/null +++ b/common/query_manager/ast/expression/index.ts @@ -0,0 +1,6 @@ +export { AggregateFunction } from './AggregateFunction'; +export { AggregateTerm } from './AggregateTerm'; +export { GroupBy } from './group_by'; +export { Span } from './span'; +export { SpanExpression } from './spanExpression'; +export { Field } from './field'; diff --git a/common/query_manager/ast/expression/span.ts b/common/query_manager/ast/expression/span.ts new file mode 100644 index 000000000..278ca18eb --- /dev/null +++ b/common/query_manager/ast/expression/span.ts @@ -0,0 +1,28 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PPLNode } from '../node'; + +export class Span extends PPLNode { + constructor( + name: string, + children: PPLNode[], + private spanExpression: PPLNode, + private customLabel: string + ) { + super(name, children); + } + + getTokens() { + return { + span_expression: this.spanExpression.getTokens(), + customLabel: this.customLabel, + }; + } + + toString(): string { + return `${this.spanExpression.toString()}${this.customLabel ? ` as ${this.customLabel}` : ''}`; + } +} diff --git a/common/query_manager/ast/expression/spanExpression.ts b/common/query_manager/ast/expression/spanExpression.ts new file mode 100644 index 000000000..c3f8d15ed --- /dev/null +++ b/common/query_manager/ast/expression/spanExpression.ts @@ -0,0 +1,30 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PPLNode } from '../node'; + +export class SpanExpression extends PPLNode { + constructor( + name: string, + children: Array, + private fieldExpression: string, + private literalValue: string, + private timeUnit: string + ) { + super(name, children); + } + + getTokens() { + return { + field: this.fieldExpression, + literal_value: this.literalValue, + time_unit: this.timeUnit, + }; + } + + toString(): string { + return `span(${this.fieldExpression}, ${this.literalValue}${this.timeUnit})`; + } +} diff --git a/common/query_manager/ast/index.ts b/common/query_manager/ast/index.ts new file mode 100644 index 000000000..eb0dbf6a7 --- /dev/null +++ b/common/query_manager/ast/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { PPLNode } from './node'; diff --git a/common/query_manager/ast/node.ts b/common/query_manager/ast/node.ts new file mode 100644 index 000000000..c28877369 --- /dev/null +++ b/common/query_manager/ast/node.ts @@ -0,0 +1,31 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +interface PPLNodeProps { + getName: () => string; + getChildren: () => Array; + toString: () => string; + getTokens: () => any; +} + +export class PPLNode implements PPLNodeProps { + constructor(private name: string, private children: Array) {} + + getChildren(): Array { + return this.children; + } + + getName(): string { + return this.name; + } + + toString(): string { + return ''; + } + + getTokens(): any { + return null; + } +} diff --git a/common/query_manager/ast/tree/aggragations.ts b/common/query_manager/ast/tree/aggragations.ts new file mode 100644 index 000000000..39281a0a7 --- /dev/null +++ b/common/query_manager/ast/tree/aggragations.ts @@ -0,0 +1,58 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { isEmpty } from 'lodash'; +import { PPLNode } from '../node'; +import { GroupBy } from '../expression/group_by'; + +export class Aggregations extends PPLNode { + constructor( + name: string, + children: Array, + private partitions: String, + private allNum: string, + private delim: string, + private aggExprList: Array, + private groupExprList: GroupBy, + private dedupSplitValue: string, + private indices?: { start: number; end: number } + ) { + super(name, children); + } + + getStartEndIndicesOfOriginQuery(): { start: number; end: number } { + if (this.indices === undefined) { + return { + start: -1, + end: -1, + }; + } + return { + start: this.indices.start, + end: this.indices.end, + }; + } + + getTokens() { + return { + partitions: this.partitions, + all_num: this.allNum, + delim: this.delim, + aggregations: this.aggExprList.map((aggTerm) => aggTerm.getTokens()), + groupby: !isEmpty(this.groupExprList) ? this.groupExprList.getTokens() : '', + dedup_split_value: this.dedupSplitValue, + }; + } + + toString() { + return `stats ${!isEmpty(this.partitions) ? `${this.partitions} ` : ''}${ + !isEmpty(this.allNum) ? `${this.allNum} ` : '' + }${!isEmpty(this.delim) ? `${this.delim} ` : ''}${this.aggExprList + .map((aggTerm) => aggTerm.toString()) + .join(', ')}${!isEmpty(this.groupExprList) ? ` ${this.groupExprList.toString()}` : ''}${ + !isEmpty(this.dedupSplitValue) ? ` ${this.dedupSplitValue}` : '' + }`; + } +} diff --git a/common/query_manager/ast/types/index.ts b/common/query_manager/ast/types/index.ts new file mode 100644 index 000000000..3256b0992 --- /dev/null +++ b/common/query_manager/ast/types/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { + ExpressionChunk, + SpanChunk, + StatsAggregationChunk, + StatsAggregationFunctionChunk, + GroupByChunk, + GroupField, + StatsChunk, + SpanExpressionChunk, + AggregationConfigurations, + PreviouslyParsedStaleStats +} from './stats'; diff --git a/common/query_manager/ast/types/stats.ts b/common/query_manager/ast/types/stats.ts new file mode 100644 index 000000000..a67565042 --- /dev/null +++ b/common/query_manager/ast/types/stats.ts @@ -0,0 +1,75 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * stats chunck types + */ +export interface StatsAggregationChunk { + function: StatsAggregationFunctionChunk; + function_alias: string; +} + +export interface StatsAggregationFunctionChunk { + name: string; + value_expression: string; + percentile_agg_function: string; +} + +export interface GroupField { + name: string; +} + +export interface SpanChunk { + customLabel: string; + span_expression: SpanExpressionChunk; +} + +export interface SpanExpressionChunk { + type: string; + field: string; + time_unit: string; + literal_value: string; +} + +export interface GroupByChunk { + group_fields: GroupField[]; + span: SpanChunk | null; +} + +export interface statsChunk { + aggregations: StatsAggregationChunk[]; + groupby: GroupByChunk; + partitions: ExpressionChunk; + all_num: ExpressionChunk; + delim: ExpressionChunk; + dedup_split_value: ExpressionChunk; +} + +export interface ExpressionChunk { + keyword: string; + sign: string; + value: string | number; +} + +export interface DataConfigSeries { + customLabel: string; + label: string; + name: string; + aggregation: string; +} + +export interface AggregationConfigurations { + series: DataConfigSeries[]; + dimensions: GroupField[]; + span: SpanChunk; + breakdowns: GroupField[]; +} + +export interface PreviouslyParsedStaleStats { + partitions: ExpressionChunk; + all_num: ExpressionChunk; + delim: ExpressionChunk; + dedup_split_value: ExpressionChunk; +} diff --git a/common/query_manager/index.ts b/common/query_manager/index.ts new file mode 100644 index 000000000..72b5cd2f6 --- /dev/null +++ b/common/query_manager/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { QueryManager } from './ppl_query_manager'; diff --git a/common/query_manager/ppl_query_manager.ts b/common/query_manager/ppl_query_manager.ts new file mode 100644 index 000000000..513485cf5 --- /dev/null +++ b/common/query_manager/ppl_query_manager.ts @@ -0,0 +1,17 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PPLQueryBuilder } from './query_builder/ppl_query_builder'; +import { PPLQueryParser } from './query_parser/ppl_query_parser'; + +export class QueryManager { + queryBuilder(): PPLQueryBuilder { + return new PPLQueryBuilder(); + } + + queryParser(): PPLQueryParser { + return new PPLQueryParser(); + } +} diff --git a/common/query_manager/query_builder/index.ts b/common/query_manager/query_builder/index.ts new file mode 100644 index 000000000..24e0492f5 --- /dev/null +++ b/common/query_manager/query_builder/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { PPLQueryBuilder } from './ppl_query_builder'; diff --git a/common/query_manager/query_builder/ppl_query_builder.ts b/common/query_manager/query_builder/ppl_query_builder.ts new file mode 100644 index 000000000..50ea50b41 --- /dev/null +++ b/common/query_manager/query_builder/ppl_query_builder.ts @@ -0,0 +1,39 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PPLSyntaxParser } from '../antlr/ppl_syntax_parser'; +import { StatsBuilder } from '../ast/builder/stats_builder'; +import { StatsAstBuilder } from '../ast/builder/stats_ast_builder'; + +export class PPLQueryBuilder { + parser: PPLSyntaxParser | null = null; + + build(query: string, pplTokens: any) { + this.parser = new PPLSyntaxParser().parse(query); + return this.buildStats(query, pplTokens); + } + + buildStats(query: string, statsTokens: any) { + const statsTree = new StatsAstBuilder().visitRoot(this.parser!.root()); + const newStatsAstTree = new StatsBuilder(statsTokens).build(); + if (typeof statsTree.getStartEndIndicesOfOriginQuery !== 'function') { + return query + ' | ' + newStatsAstTree.toString(); + } + + const indices = statsTree.getStartEndIndicesOfOriginQuery(); + + if (indices.start !== -1 && indices.end !== -1) { + return ( + query.substring(0, indices.start) + + newStatsAstTree.toString() + + query.substring(indices.end + 1, query.length) + ); + } else if (indices && newStatsAstTree) { + return query + ' | ' + newStatsAstTree.toString(); + } + + return ''; + } +} diff --git a/common/query_manager/query_parser/index.ts b/common/query_manager/query_parser/index.ts new file mode 100644 index 000000000..c4afe0e5d --- /dev/null +++ b/common/query_manager/query_parser/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export { PPLQueryParser } from './ppl_query_parser'; diff --git a/common/query_manager/query_parser/ppl_query_parser.ts b/common/query_manager/query_parser/ppl_query_parser.ts new file mode 100644 index 000000000..9a39276e0 --- /dev/null +++ b/common/query_manager/query_parser/ppl_query_parser.ts @@ -0,0 +1,25 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { PPLSyntaxParser } from '../antlr/ppl_syntax_parser'; +import { OpenSearchPPLParser } from '../antlr/output/OpenSearchPPLParser'; +import { StatsAstBuilder } from '../ast/builder/stats_ast_builder'; + +export class PPLQueryParser { + parser: OpenSearchPPLParser | null = null; + visitor: any = null; + rawQuery: string = ''; + + parse(pplQuery: string) { + this.rawQuery = pplQuery; + this.parser = new PPLSyntaxParser().parse(this.rawQuery); + return this; + } + + getStats() { + this.visitor = new StatsAstBuilder(); + return this.visitor.visitRoot(this.parser!.root()).getTokens(); + } +} diff --git a/common/query_manager/utils/index.ts b/common/query_manager/utils/index.ts new file mode 100644 index 000000000..b8644f050 --- /dev/null +++ b/common/query_manager/utils/index.ts @@ -0,0 +1,47 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { CUSTOM_LABEL } from '../../../common/constants/explorer'; +import { AggregationConfigurations, PreviouslyParsedStaleStats } from '../ast/types'; + +export const composeAggregations = ( + aggConfig: AggregationConfigurations, + staleStats: PreviouslyParsedStaleStats +) => { + return { + aggregations: aggConfig.series.map((metric) => ({ + function_alias: metric[CUSTOM_LABEL], + function: { + name: metric.aggregation, + value_expression: metric.name, + percentile_agg_function: '', + }, + })), + groupby: { + group_fields: [ + ...(aggConfig.dimensions || []), + ...(aggConfig.breakdowns || []), + ].map((dimension) => ({ name: dimension.name })), + ...(aggConfig.span && + JSON.stringify(aggConfig?.span) !== '{}' && { span: composeSpan(aggConfig.span) }), + }, + partitions: staleStats?.partitions ?? {}, + all_num: staleStats?.all_num ?? {}, + delim: staleStats?.delim ?? {}, + dedup_split_value: staleStats?.dedup_split_value ?? {}, + }; +}; + +const composeSpan = (spanConfig) => { + return { + [CUSTOM_LABEL]: spanConfig[CUSTOM_LABEL] ?? '', + span_expression: { + type: spanConfig.time_field[0]?.type ?? 'timestamp', + field: spanConfig.time_field[0]?.name ?? 'timestamp', + time_unit: spanConfig.unit[0]?.value ?? 'd', + literal_value: spanConfig.interval ?? 1, + }, + }; +}; diff --git a/common/types/explorer.ts b/common/types/explorer.ts index 5a735a440..bbf0e270f 100644 --- a/common/types/explorer.ts +++ b/common/types/explorer.ts @@ -5,6 +5,8 @@ import { History } from 'history'; import Plotly from 'plotly.js-dist'; +import { QueryManager } from 'common/query_manager'; +import { VIS_CHART_TYPES } from '../../common/constants/shared'; import { RAW_QUERY, SELECTED_FIELDS, @@ -15,13 +17,23 @@ import { FINAL_QUERY, SELECTED_TIMESTAMP, SELECTED_DATE_RANGE, + GROUPBY, + AGGREGATIONS, + CUSTOM_LABEL, + BREAKDOWNS, } from '../constants/explorer'; -import { CoreStart, HttpStart, NotificationsStart } from '../../../../src/core/public'; +import { + CoreStart, + CoreSetup, + HttpSetup, + HttpStart, + NotificationsStart, +} from '../../../../src/core/public'; import SavedObjects from '../../public/services/saved_objects/event_analytics/saved_objects'; import TimestampUtils from '../../public/services/timestamp/timestamp'; import PPLService from '../../public/services/requests/ppl'; import DSLService from '../../public/services/requests/dsl'; - +import { SavedObjectsStart } from '../../../../src/core/public/saved_objects'; export interface IQueryTab { id: string; name: React.ReactNode | string; @@ -31,6 +43,20 @@ export interface IQueryTab { export interface IField { name: string; type: string; + label?: string; +} + +export interface TimeUnit { + name: string; + label: string; + value: string; +} + +export interface ExplorerFields { + availableFields: IField[]; + queriedFields: IField[]; + selectedFields: IField[]; + unselectedFields: IField[]; } export interface ITabQueryResults { @@ -82,6 +108,7 @@ export interface ILogExplorerProps { ) => void; savedObjectId: string; getExistingEmptyTab: (params: EmptyTabParams) => string; + queryManager: QueryManager; } export interface IExplorerProps { @@ -112,6 +139,7 @@ export interface IExplorerProps { appBaseQuery?: string; callback?: any; callbackInApp?: any; + queryManager: QueryManager; } export interface SavedQuery { @@ -150,6 +178,31 @@ export interface SavedVizRes { tenant: string; } +export interface ExplorerDataType { + jsonData: object[]; + jsonDataAll: object[]; +} + +export interface Query { + index: string; + isLoaded: boolean; + objectType: string; + rawQuery: string; + savedObjectId: string; + selectedDateRange: string[]; + selectedTimestamp: string; + tabCreatedType: string; + finalQuery?: string; +} + +export interface ExplorerData { + explorerData?: ExplorerDataType; + explorerFields?: IExplorerFields; + query?: Query; + http?: HttpSetup; + pplService?: PPLService; +} + export interface IVisualizationContainerPropsData { appData?: { fromApp: boolean }; rawVizData?: any; @@ -160,6 +213,7 @@ export interface IVisualizationContainerPropsData { xaxis: IField[]; yaxis: IField[]; }; + explorer?: ExplorerData; } export interface IVisualizationContainerPropsVis { @@ -186,9 +240,11 @@ export interface IConfigPanelOptions { export interface IConfigPanelOptionSection { name: string; component: null; - mapTo: 'mode'; + mapTo: string; props?: any; isSingleSelection?: boolean; + defaultState?: boolean | string; + eleType?: string; } export interface IVisualizationTypeDefination { @@ -196,13 +252,13 @@ export interface IVisualizationTypeDefination { type: string; id: string; label: string; - fullLabel: string; + fulllabel: string; category: string; icon: React.ReactNode; - editorConfig: { + editorconfig: { panelTabs: IConfigPanelTab; }; - visConfig: { + visconfig: { layout: Partial; config: Partial; }; @@ -232,4 +288,97 @@ export interface PatternTableData { count: number; pattern: string; sampleLog: string; +}; +export interface ConfigListEntry { + label: string; + aggregation: string; + [CUSTOM_LABEL]: string; + name: string; + side: string; + type: string; + alias?: string; +} + +export interface HistogramConfigList { + bucketSize: string; + bucketOffset: string; +} + +export interface DimensionSpan { + time_field: IField[]; + interval: number; + unit: TimeUnit[]; +} + +export interface ConfigList { + [GROUPBY]?: ConfigListEntry[] | HistogramConfigList[]; + [AGGREGATIONS]?: ConfigListEntry[]; + [BREAKDOWNS]?: ConfigListEntry[] | HistogramConfigList[]; + span?: DimensionSpan; +} + +export interface Breadcrumbs { + text: string; + href: string; +} + +export interface EventAnalyticsProps { + chrome: CoreSetup; + parentBreadcrumbs: Breadcrumbs[]; + pplService: any; + dslService: any; + savedObjects: SavedObjectsStart; + timestampUtils: TimestampUtils; + http: HttpStart; + notifications: NotificationsStart; + queryManager: QueryManager; +} + +export interface DataConfigPanelProps { + fieldOptionList: IField[]; + visualizations: IVisualizationContainerProps; + queryManager?: QueryManager; +} +export interface GetTooltipHoverInfoType { + tooltipMode: string; + tooltipText: string; +} + +export interface SelectedConfigItem { + index: number; + name: string; +} + +export interface ParentUnitType { + name: string; + label: string; + type: string; +} + +export interface TreemapParentsProps { + selectedAxis: ParentUnitType[]; + setSelectedParentItem: (item: { isClicked: boolean; index: number }) => void; + handleUpdateParentFields: (arr: ParentUnitType[]) => void; +} + +export interface DataConfigPanelFieldProps { + list: ConfigListEntry[]; + dimensionSpan: DimensionSpan; + sectionName: string; + visType: VIS_CHART_TYPES; + addButtonText: string; + handleServiceAdd: (name: string) => void; + handleServiceRemove: (index: number, name: string) => void; + handleServiceEdit: (arrIndex: number, sectionName: string, isTimeStamp: boolean) => void; +} + +export interface VisMeta { + visId: string; +} + +export interface VisualizationState { + query: Query; + visData: any; + visConfMetadata: ConfigList; + visMeta: VisMeta; } diff --git a/common/utils/index.ts b/common/utils/index.ts index e5f765ccd..6999e7cd3 100644 --- a/common/utils/index.ts +++ b/common/utils/index.ts @@ -3,5 +3,5 @@ * SPDX-License-Identifier: Apache-2.0 */ -export { getIndexPatternFromRawQuery, preprocessQuery, buildQuery } from './query_utils'; +export { getIndexPatternFromRawQuery, preprocessQuery, buildQuery, composeFinalQuery } from './query_utils'; export { uiSettingsService } from './settings_service'; diff --git a/common/utils/query_utils.ts b/common/utils/query_utils.ts index 985964d7b..977014146 100644 --- a/common/utils/query_utils.ts +++ b/common/utils/query_utils.ts @@ -66,3 +66,22 @@ export const buildQuery = (baseQuery: string, currQuery: string) => { } return fullQuery; }; + +export const composeFinalQuery = ( + curQuery: string, + startingTime: string, + endingTime: string, + timeField: string, + isLiveQuery: boolean, + appBaseQuery: string +) => { + const fullQuery = buildQuery(appBaseQuery, curQuery); + if (isEmpty(fullQuery)) return ''; + return preprocessQuery({ + rawQuery: fullQuery, + startTime: startingTime, + endTime: endingTime, + timeField, + isLiveQuery, + }); +}; diff --git a/package.json b/package.json index e47cb401e..85dfb27b2 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "test": "../../node_modules/.bin/jest --config ./test/jest.config.js", "cypress:run": "TZ=America/Los_Angeles cypress run", "cypress:open": "TZ=America/Los_Angeles cypress open", - "plugin_helpers": "node ../../scripts/plugin_helpers" + "plugin_helpers": "node ../../scripts/plugin_helpers", + "postinstall": "antlr4ts -visitor ./common/query_manager/antlr/grammar/OpenSearchPPLLexer.g4 -Xexact-output-dir -o ./common/query_manager/antlr/output && antlr4ts -visitor ./common/query_manager/antlr/grammar/OpenSearchPPLParser.g4 -Xexact-output-dir -o ./common/query_manager/antlr/output" }, "dependencies": { "@algolia/autocomplete-core": "^1.4.1", @@ -17,8 +18,14 @@ "@nteract/outputs": "^3.0.11", "@nteract/presentational-components": "^3.4.3", "@reduxjs/toolkit": "^1.6.1", + "ag-grid-community": "^27.3.0", + "ag-grid-react": "^27.3.0", + "antlr4": "4.8.0", + "antlr4ts": "^0.5.0-alpha.4", "plotly.js-dist": "^2.2.0", + "postinstall": "^0.7.4", "react-graph-vis": "^1.0.5", + "react-paginate": "^8.1.3", "react-plotly.js": "^2.5.1" }, "devDependencies": { @@ -26,6 +33,7 @@ "@types/enzyme-adapter-react-16": "^1.0.6", "@types/react-plotly.js": "^2.5.0", "@types/react-test-renderer": "^16.9.1", + "antlr4ts-cli": "^0.5.0-alpha.4", "cypress": "^5.0.0", "eslint": "^6.8.0", "jest-dom": "^4.0.0", diff --git a/public/.DS_Store b/public/.DS_Store new file mode 100644 index 000000000..dbd07c1a1 Binary files /dev/null and b/public/.DS_Store differ diff --git a/public/components/app.tsx b/public/components/app.tsx index b4d164331..307a0edcf 100644 --- a/public/components/app.tsx +++ b/public/components/app.tsx @@ -7,6 +7,7 @@ import { I18nProvider } from '@osd/i18n/react'; import React from 'react'; import { Provider } from 'react-redux'; import { HashRouter, Route, Switch } from 'react-router-dom'; +import { QueryManager } from 'common/query_manager'; import { CoreStart } from '../../../../src/core/public'; import { observabilityID, observabilityTitle } from '../../common/constants/shared'; import store from '../framework/redux/store'; @@ -24,6 +25,7 @@ interface ObservabilityAppDeps { dslService: any; savedObjects: any; timestampUtils: any; + queryManager: QueryManager; } // for cypress to test redux store @@ -38,6 +40,7 @@ export const App = ({ dslService, savedObjects, timestampUtils, + queryManager, }: ObservabilityAppDeps) => { const { chrome, http, notifications } = CoreStartProp; const parentBreadcrumb = { @@ -130,6 +133,7 @@ export const App = ({ timestampUtils={timestampUtils} http={http} notifications={notifications} + queryManager={queryManager} {...props} /> ); diff --git a/public/components/common/field_button/field_button.scss b/public/components/common/field_button/field_button.scss index 8ae66b433..e9fdd6a30 100644 --- a/public/components/common/field_button/field_button.scss +++ b/public/components/common/field_button/field_button.scss @@ -78,3 +78,45 @@ margin-right: $euiSizeXS; } } + +.shard__fieldSelectorField { + &:hover, + &:focus-within, + &[class*='-isActive'] { + .dscSidebarItem__action { + opacity: 1; + } + } + + & > .osdFieldButton__button { + padding: 0; + } + + & .osdFieldButton__name { + padding: $euiSizeS $euiSizeS $euiSizeS 0; + } + + & > button { + align-items: stretch; + } + + & .osdFieldIcon { + box-shadow: none; + height: 100%; + flex-shrink: 0; + line-height: 0; + } + + & .osdFieldButton__fieldIcon { + box-shadow: none; + margin-right: 8px; + } +} + +#vis__mainContent .explorer__fieldSelectorField { + @include euiBottomShadowSmall; + + background-color: $euiColorEmptyShade; + border: $euiBorderThin; + margin-top: $euiSizeS; +} diff --git a/public/components/common/field_button/field_button.tsx b/public/components/common/field_button/field_button.tsx index 5ceac9875..963cdf84a 100644 --- a/public/components/common/field_button/field_button.tsx +++ b/public/components/common/field_button/field_button.tsx @@ -76,6 +76,7 @@ export function FieldButton({ buttonProps, ...rest }: FieldButtonProps) { + const classes = classNames( 'osdFieldButton', size ? sizeToClassNameMap[size] : null, diff --git a/public/components/common/field_icon/field_icon.tsx b/public/components/common/field_icon/field_icon.tsx index a63dfe5ad..467c2c7c0 100644 --- a/public/components/common/field_icon/field_icon.tsx +++ b/public/components/common/field_icon/field_icon.tsx @@ -26,7 +26,7 @@ export interface FieldIconProps extends Omit { } // defaultIcon => a unknown datatype -const defaultIcon = { iconType: 'questionInCircle', color: 'gray' }; +const defaultIcon = { iconType: 'tokenString' }; export const typeToEuiIconMap: Partial> = { boolean: { iconType: 'tokenBoolean' }, @@ -50,13 +50,12 @@ export const typeToEuiIconMap: Partial> = { export function FieldIcon({ type, label, - size = 's', + size = 'l', scripted, className, ...rest }: FieldIconProps) { const token = typeToEuiIconMap[type] || defaultIcon; - return ( {
-
+ + {fullWord.slice(0, -item.suggestion.length)} + {item.suggestion} + +
+ {/*
{ }
`, }} - /> + /> */}
diff --git a/public/components/common/search/search.tsx b/public/components/common/search/search.tsx index b4e2ebb16..ed9f85381 100644 --- a/public/components/common/search/search.tsx +++ b/public/components/common/search/search.tsx @@ -16,8 +16,8 @@ import { EuiBadge, EuiContextMenuPanel, EuiToolTip, + EuiCallOut } from '@elastic/eui'; -import _ from 'lodash'; import { DatePicker } from './date_picker'; import '@algolia/autocomplete-theme-classic'; import { Autocomplete } from './autocomplete'; @@ -82,10 +82,10 @@ export const Search = (props: any) => { stopLive, setIsLiveTailPopoverOpen, liveTailName, + searchError = null, } = props; - + const appLogEvents = tabId.match(APP_ANALYTICS_TAB_ID_REGEX); - const [isSavePanelOpen, setIsSavePanelOpen] = useState(false); const [isFlyoutVisible, setIsFlyoutVisible] = useState(false); @@ -247,6 +247,17 @@ export const Search = (props: any) => { )} + { searchError && searchError.error && ( + + + +

+ {JSON.parse(searchError.message).error.details} +

+
+
+
) + } {flyout} ); diff --git a/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap b/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap index 5a513f839..4668e70fc 100644 --- a/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap +++ b/public/components/custom_panels/helpers/__tests__/__snapshots__/utils.test.tsx.snap @@ -10,19 +10,83 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "defaultAxes": Object { "xaxis": Array [ Object { + "label": "Carrier", "name": "Carrier", "type": "keyword", }, ], "yaxis": Array [ Object { + "label": "avg(FlightDelayMin)", "name": "avg(FlightDelayMin)", "type": "double", }, ], }, + "explorer": Object { + "explorerData": Object { + "data": Object { + "Carrier": Array [ + "BeatsWest", + "Logstash Airways", + "OpenSearch Dashboards Airlines", + "OpenSearch-Air", + ], + "avg(FlightDelayMin)": Array [ + 53.65384615384615, + 45.36144578313253, + 63.1578947368421, + 46.81318681318681, + ], + }, + "jsonData": Array [ + Object { + "Carrier": "BeatsWest", + "avg(FlightDelayMin)": 53.65384615384615, + }, + Object { + "Carrier": "Logstash Airways", + "avg(FlightDelayMin)": 45.36144578313253, + }, + Object { + "Carrier": "OpenSearch Dashboards Airlines", + "avg(FlightDelayMin)": 63.1578947368421, + }, + Object { + "Carrier": "OpenSearch-Air", + "avg(FlightDelayMin)": 46.81318681318681, + }, + ], + "metadata": Object { + "fields": Array [ + Object { + "name": "avg(FlightDelayMin)", + "type": "double", + }, + Object { + "name": "Carrier", + "type": "keyword", + }, + ], + }, + "size": 4, + "status": 200, + }, + "explorerFields": Array [ + Object { + "name": "avg(FlightDelayMin)", + "type": "double", + }, + Object { + "name": "Carrier", + "type": "keyword", + }, + ], + }, "indexFields": Object {}, - "query": Object {}, + "query": Object { + "rawQuery": "source=opensearch_dashboards_sample_data_flights | fields Carrier,FlightDelayMin | stats sum(FlightDelayMin) as delays by Carrier", + }, "rawVizData": Object { "data": Object { "Carrier": Array [ @@ -74,101 +138,265 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "userConfigs": Object {}, }, "vis": Object { + "barwidth": 0.97, "category": "Visualizations", - "categoryAxis": "xaxis", + "categoryaxis": "xaxis", "component": [Function], - "editorConfig": Object { + "editorconfig": Object { "panelTabs": Array [ Object { "editor": [Function], "id": "data-panel", "mapTo": "dataConfig", - "name": "Data", + "name": "Style", "sections": Array [ Object { "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", + "id": "tooltip_options", + "mapTo": "tooltipOptions", + "name": "Tooltip options", "schemas": Array [ Object { "component": null, - "isSingleSelection": false, - "mapTo": "xaxis", - "name": "X-axis", + "mapTo": "tooltipMode", + "name": "Tooltip mode", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, }, Object { "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", + "mapTo": "tooltipText", + "name": "Tooltip text", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "all", + "name": "All", + }, + ], + "options": Array [ + Object { + "id": "all", + "name": "All", + }, + Object { + "id": "x", + "name": "Dimension", + }, + Object { + "id": "y", + "name": "Series", + }, + ], + }, }, ], }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, - "mapTo": "orientation", - "name": "Orientation", + "mapTo": "showLegend", + "name": "Show legend", "props": Object { "defaultSelections": Array [ Object { - "name": "Vertical", - "orientationId": "v", + "id": "show", + "name": "Show", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "name": "Vertical", - "orientationId": "v", + "id": "show", + "name": "Show", }, Object { - "name": "Horizontal", - "orientationId": "h", + "id": "hidden", + "name": "Hidden", }, ], }, }, Object { "component": null, - "isSingleSelection": true, + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "legendSize", + "name": "Legend size", + "title": "Legend size", + }, + ], + }, + Object { + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", "mapTo": "mode", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, Object { - "modeId": "stack", + "id": "stack", "name": "Stack", }, ], }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label size", + }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateBarLabels", + "name": "Rotate bar labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 0.7, + "eleType": "slider", + "mapTo": "groupWidth", + "name": "Group width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 0.97, + "eleType": "slider", + "mapTo": "barWidth", + "name": "Bar width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 2, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 70, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill opacity", + "props": Object { + "max": 100, + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color theme", + "schemas": Array [], + }, ], }, Object { - "content": Array [], "editor": [Function], - "id": "style-panel", - "mapTo": "layoutConfig", - "name": "Layout", + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", }, Object { "editor": [Function], @@ -178,26 +406,33 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` }, ], }, - "fullLabel": "Bar", + "fillopacity": 70, + "fulllabel": "Vertical bar", + "groupwidth": 0.7, "icon": [Function], - "iconType": "visBarVerticalStacked", + "icontype": "visBarVerticalStacked", "id": "bar", - "label": "Bar", + "label": "Vertical bar", + "labelangle": 0, + "legendposition": "v", + "linewidth": 2, + "mode": "group", "name": "bar", "orientation": "v", "selection": Object { "dataLoss": "nothing", }, - "seriesAxis": "yaxis", + "seriesaxis": "yaxis", + "showlegend": "show", "type": "bar", - "visConfig": Object { + "visconfig": Object { "config": Object { "displaylogo": false, "responsive": true, }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -224,19 +459,83 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "defaultAxes": Object { "xaxis": Array [ Object { + "label": "Carrier", "name": "Carrier", "type": "keyword", }, ], "yaxis": Array [ + Object { + "label": "avg(FlightDelayMin)", + "name": "avg(FlightDelayMin)", + "type": "double", + }, + ], + }, + "explorer": Object { + "explorerData": Object { + "data": Object { + "Carrier": Array [ + "BeatsWest", + "Logstash Airways", + "OpenSearch Dashboards Airlines", + "OpenSearch-Air", + ], + "avg(FlightDelayMin)": Array [ + 53.65384615384615, + 45.36144578313253, + 63.1578947368421, + 46.81318681318681, + ], + }, + "jsonData": Array [ + Object { + "Carrier": "BeatsWest", + "avg(FlightDelayMin)": 53.65384615384615, + }, + Object { + "Carrier": "Logstash Airways", + "avg(FlightDelayMin)": 45.36144578313253, + }, + Object { + "Carrier": "OpenSearch Dashboards Airlines", + "avg(FlightDelayMin)": 63.1578947368421, + }, + Object { + "Carrier": "OpenSearch-Air", + "avg(FlightDelayMin)": 46.81318681318681, + }, + ], + "metadata": Object { + "fields": Array [ + Object { + "name": "avg(FlightDelayMin)", + "type": "double", + }, + Object { + "name": "Carrier", + "type": "keyword", + }, + ], + }, + "size": 4, + "status": 200, + }, + "explorerFields": Array [ Object { "name": "avg(FlightDelayMin)", "type": "double", }, + Object { + "name": "Carrier", + "type": "keyword", + }, ], }, "indexFields": Object {}, - "query": Object {}, + "query": Object { + "rawQuery": "source=opensearch_dashboards_sample_data_flights | fields Carrier,FlightDelayMin | stats sum(FlightDelayMin) as delays by Carrier", + }, "rawVizData": Object { "data": Object { "Carrier": Array [ @@ -288,130 +587,301 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "userConfigs": Object {}, }, "vis": Object { + "barwidth": 0.97, "category": "Visualizations", - "categoryAxis": "xaxis", + "categoryaxis": "xaxis", "component": [Function], - "editorConfig": Object { + "editorconfig": Object { "panelTabs": Array [ Object { "editor": [Function], "id": "data-panel", "mapTo": "dataConfig", - "name": "Data", + "name": "Style", "sections": Array [ Object { "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", + "id": "tooltip_options", + "mapTo": "tooltipOptions", + "name": "Tooltip options", "schemas": Array [ Object { "component": null, - "isSingleSelection": false, - "mapTo": "xaxis", - "name": "X-axis", + "mapTo": "tooltipMode", + "name": "Tooltip mode", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, }, Object { "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", + "mapTo": "tooltipText", + "name": "Tooltip text", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "all", + "name": "All", + }, + ], + "options": Array [ + Object { + "id": "all", + "name": "All", + }, + Object { + "id": "x", + "name": "Dimension", + }, + Object { + "id": "y", + "name": "Series", + }, + ], + }, }, ], }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, - "mapTo": "orientation", - "name": "Orientation", + "mapTo": "showLegend", + "name": "Show legend", "props": Object { "defaultSelections": Array [ Object { - "name": "Vertical", - "orientationId": "v", + "id": "show", + "name": "Show", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "name": "Vertical", - "orientationId": "v", + "id": "show", + "name": "Show", }, Object { - "name": "Horizontal", - "orientationId": "h", + "id": "hidden", + "name": "Hidden", }, ], }, }, Object { "component": null, - "isSingleSelection": true, + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "legendSize", + "name": "Legend size", + "title": "Legend size", + }, + ], + }, + Object { + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", "mapTo": "mode", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, Object { - "modeId": "stack", + "id": "stack", "name": "Stack", }, ], }, }, - ], - }, - ], - }, - Object { - "content": Array [], - "editor": [Function], - "id": "style-panel", - "mapTo": "layoutConfig", - "name": "Layout", - }, - Object { - "editor": [Function], - "id": "availability-panel", - "mapTo": "availabilityConfig", - "name": "Availability", + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label size", + }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateBarLabels", + "name": "Rotate bar labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 0.7, + "eleType": "slider", + "mapTo": "groupWidth", + "name": "Group width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 0.97, + "eleType": "slider", + "mapTo": "barWidth", + "name": "Bar width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 2, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 70, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill opacity", + "props": Object { + "max": 100, + }, + }, + ], + }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color theme", + "schemas": Array [], + }, + ], + }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", }, ], }, - "fullLabel": "Bar", + "fillopacity": 70, + "fulllabel": "Vertical bar", + "groupwidth": 0.7, "icon": [Function], - "iconType": "visBarVerticalStacked", + "icontype": "visBarVerticalStacked", "id": "bar", - "label": "Bar", + "label": "Vertical bar", + "labelangle": 0, + "legendposition": "v", + "linewidth": 2, + "mode": "group", "name": "bar", "orientation": "v", "selection": Object { "dataLoss": "nothing", }, - "seriesAxis": "yaxis", + "seriesaxis": "yaxis", + "showlegend": "show", "type": "bar", - "visConfig": Object { + "visconfig": Object { "config": Object { "displaylogo": false, "responsive": true, }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -437,23 +907,7 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "responsive": true, } } - layout={ - Object { - "height": 500, - "legend": Object { - "orientation": "v", - "traceorder": "normal", - }, - "margin": Object { - "b": 30, - "l": 60, - "pad": 0, - "r": 30, - "t": 50, - }, - "showlegend": true, - } - } + layout={[Function]} visualizations={ Object { "data": Object { @@ -461,19 +915,83 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "defaultAxes": Object { "xaxis": Array [ Object { + "label": "Carrier", "name": "Carrier", "type": "keyword", }, ], "yaxis": Array [ + Object { + "label": "avg(FlightDelayMin)", + "name": "avg(FlightDelayMin)", + "type": "double", + }, + ], + }, + "explorer": Object { + "explorerData": Object { + "data": Object { + "Carrier": Array [ + "BeatsWest", + "Logstash Airways", + "OpenSearch Dashboards Airlines", + "OpenSearch-Air", + ], + "avg(FlightDelayMin)": Array [ + 53.65384615384615, + 45.36144578313253, + 63.1578947368421, + 46.81318681318681, + ], + }, + "jsonData": Array [ + Object { + "Carrier": "BeatsWest", + "avg(FlightDelayMin)": 53.65384615384615, + }, + Object { + "Carrier": "Logstash Airways", + "avg(FlightDelayMin)": 45.36144578313253, + }, + Object { + "Carrier": "OpenSearch Dashboards Airlines", + "avg(FlightDelayMin)": 63.1578947368421, + }, + Object { + "Carrier": "OpenSearch-Air", + "avg(FlightDelayMin)": 46.81318681318681, + }, + ], + "metadata": Object { + "fields": Array [ + Object { + "name": "avg(FlightDelayMin)", + "type": "double", + }, + Object { + "name": "Carrier", + "type": "keyword", + }, + ], + }, + "size": 4, + "status": 200, + }, + "explorerFields": Array [ Object { "name": "avg(FlightDelayMin)", "type": "double", }, + Object { + "name": "Carrier", + "type": "keyword", + }, ], }, "indexFields": Object {}, - "query": Object {}, + "query": Object { + "rawQuery": "source=opensearch_dashboards_sample_data_flights | fields Carrier,FlightDelayMin | stats sum(FlightDelayMin) as delays by Carrier", + }, "rawVizData": Object { "data": Object { "Carrier": Array [ @@ -525,101 +1043,265 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "userConfigs": Object {}, }, "vis": Object { + "barwidth": 0.97, "category": "Visualizations", - "categoryAxis": "xaxis", + "categoryaxis": "xaxis", "component": [Function], - "editorConfig": Object { + "editorconfig": Object { "panelTabs": Array [ Object { "editor": [Function], "id": "data-panel", "mapTo": "dataConfig", - "name": "Data", + "name": "Style", "sections": Array [ Object { "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", + "id": "tooltip_options", + "mapTo": "tooltipOptions", + "name": "Tooltip options", "schemas": Array [ Object { "component": null, - "isSingleSelection": false, - "mapTo": "xaxis", - "name": "X-axis", + "mapTo": "tooltipMode", + "name": "Tooltip mode", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, }, Object { "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", + "mapTo": "tooltipText", + "name": "Tooltip text", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "all", + "name": "All", + }, + ], + "options": Array [ + Object { + "id": "all", + "name": "All", + }, + Object { + "id": "x", + "name": "Dimension", + }, + Object { + "id": "y", + "name": "Series", + }, + ], + }, }, ], }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, - "mapTo": "orientation", - "name": "Orientation", + "mapTo": "showLegend", + "name": "Show legend", "props": Object { "defaultSelections": Array [ Object { - "name": "Vertical", - "orientationId": "v", + "id": "show", + "name": "Show", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "name": "Vertical", - "orientationId": "v", + "id": "show", + "name": "Show", }, Object { - "name": "Horizontal", - "orientationId": "h", + "id": "hidden", + "name": "Hidden", }, ], }, }, Object { "component": null, - "isSingleSelection": true, + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "legendSize", + "name": "Legend size", + "title": "Legend size", + }, + ], + }, + Object { + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", "mapTo": "mode", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "group", + "id": "group", "name": "Group", }, Object { - "modeId": "stack", + "id": "stack", "name": "Stack", }, ], }, }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label size", + }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateBarLabels", + "name": "Rotate bar labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 0.7, + "eleType": "slider", + "mapTo": "groupWidth", + "name": "Group width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 0.97, + "eleType": "slider", + "mapTo": "barWidth", + "name": "Bar width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 2, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 70, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill opacity", + "props": Object { + "max": 100, + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color theme", + "schemas": Array [], + }, ], }, Object { - "content": Array [], "editor": [Function], - "id": "style-panel", - "mapTo": "layoutConfig", - "name": "Layout", + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", }, Object { "editor": [Function], @@ -629,26 +1311,33 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` }, ], }, - "fullLabel": "Bar", + "fillopacity": 70, + "fulllabel": "Vertical bar", + "groupwidth": 0.7, "icon": [Function], - "iconType": "visBarVerticalStacked", + "icontype": "visBarVerticalStacked", "id": "bar", - "label": "Bar", + "label": "Vertical bar", + "labelangle": 0, + "legendposition": "v", + "linewidth": 2, + "mode": "group", "name": "bar", "orientation": "v", "selection": Object { "dataLoss": "nothing", }, - "seriesAxis": "yaxis", + "seriesaxis": "yaxis", + "showlegend": "show", "type": "bar", - "visConfig": Object { + "visconfig": Object { "config": Object { "displaylogo": false, "responsive": true, }, "isUniColor": false, "layout": Object { - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -674,38 +1363,12 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "responsive": true, } } - data={ - Array [ - Object { - "marker": Object { - "color": Array [ - "#3CA1C7", - "#8C55A3", - "#DB748A", - "#F2BE4B", - ], - }, - "name": "avg(FlightDelayMin)", - "orientation": "v", - "type": "bar", - "x": Array [ - "BeatsWest", - "Logstash Airways", - "OpenSearch Dashboards Airlines", - "OpenSearch-Air", - ], - "y": Array [ - 53.65384615384615, - 45.36144578313253, - 63.1578947368421, - 46.81318681318681, - ], - }, - ] - } + data={Array []} layout={ Object { - "barmode": "", + "bargap": 0.30000000000000004, + "bargroupgap": 0.030000000000000027, + "barmode": "group", "colorway": Array [ "#3CA1C7", "#8C55A3", @@ -718,20 +1381,32 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "#BD6F26", "#4C636F", ], - "height": 500, + "hovermode": "closest", "legend": Object { "orientation": "v", - "traceorder": "normal", }, "margin": Object { "b": 30, - "l": 60, - "pad": 0, - "r": 30, + "l": 30, + "pad": 4, + "r": 5, "t": 50, }, - "showlegend": true, + "showlegend": "show", "title": "", + "xaxis": Object { + "automargin": true, + "tickangle": 0, + "tickfont": Object { + "size": 12, + }, + }, + "yaxis": Object { + "automargin": true, + "tickfont": Object { + "size": 12, + }, + }, } } > @@ -743,41 +1418,15 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "responsive": true, } } - data={ - Array [ - Object { - "marker": Object { - "color": Array [ - "#3CA1C7", - "#8C55A3", - "#DB748A", - "#F2BE4B", - ], - }, - "name": "avg(FlightDelayMin)", - "orientation": "v", - "type": "bar", - "x": Array [ - "BeatsWest", - "Logstash Airways", - "OpenSearch Dashboards Airlines", - "OpenSearch-Air", - ], - "y": Array [ - 53.65384615384615, - 45.36144578313253, - 63.1578947368421, - 46.81318681318681, - ], - }, - ] - } + data={Array []} debug={false} divId="explorerPlotComponent" layout={ Object { "autosize": true, - "barmode": "", + "bargap": 0.30000000000000004, + "bargroupgap": 0.030000000000000027, + "barmode": "group", "colorway": Array [ "#3CA1C7", "#8C55A3", @@ -790,31 +1439,31 @@ exports[`Utils helper functions renders displayVisualization function 1`] = ` "#BD6F26", "#4C636F", ], - "height": 500, "hovermode": "closest", "legend": Object { "orientation": "v", - "traceorder": "normal", }, "margin": Object { "b": 30, - "l": 60, - "pad": 0, - "r": 30, + "l": 30, + "pad": 4, + "r": 5, "t": 50, }, - "showlegend": true, + "showlegend": "show", "title": "", "xaxis": Object { "automargin": true, - "rangemode": "normal", - "showgrid": true, - "zeroline": false, + "tickangle": 0, + "tickfont": Object { + "size": 12, + }, }, "yaxis": Object { - "rangemode": "normal", - "showgrid": true, - "zeroline": false, + "automargin": true, + "tickfont": Object { + "size": 12, + }, }, } } @@ -853,38 +1502,106 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "defaultAxes": Object { "xaxis": Array [ Object { - "name": "Carrier", - "type": "keyword", + "label": "", + "name": "", }, ], "yaxis": Array [ Object { + "label": "avg(FlightDelayMin)", "name": "avg(FlightDelayMin)", "type": "double", }, - ], - }, - "indexFields": Object {}, - "query": Object {}, - "rawVizData": Object { - "data": Object { - "Carrier": Array [ - "BeatsWest", - "Logstash Airways", - "OpenSearch Dashboards Airlines", - "OpenSearch-Air", - ], - "avg(FlightDelayMin)": Array [ - 53.65384615384615, - 45.36144578313253, - 63.1578947368421, - 46.81318681318681, - ], - }, - "jsonData": Array [ Object { - "Carrier": "BeatsWest", - "avg(FlightDelayMin)": 53.65384615384615, + "label": "Carrier", + "name": "Carrier", + "type": "keyword", + }, + ], + }, + "explorer": Object { + "explorerData": Object { + "data": Object { + "Carrier": Array [ + "BeatsWest", + "Logstash Airways", + "OpenSearch Dashboards Airlines", + "OpenSearch-Air", + ], + "avg(FlightDelayMin)": Array [ + 53.65384615384615, + 45.36144578313253, + 63.1578947368421, + 46.81318681318681, + ], + }, + "jsonData": Array [ + Object { + "Carrier": "BeatsWest", + "avg(FlightDelayMin)": 53.65384615384615, + }, + Object { + "Carrier": "Logstash Airways", + "avg(FlightDelayMin)": 45.36144578313253, + }, + Object { + "Carrier": "OpenSearch Dashboards Airlines", + "avg(FlightDelayMin)": 63.1578947368421, + }, + Object { + "Carrier": "OpenSearch-Air", + "avg(FlightDelayMin)": 46.81318681318681, + }, + ], + "metadata": Object { + "fields": Array [ + Object { + "name": "avg(FlightDelayMin)", + "type": "double", + }, + Object { + "name": "Carrier", + "type": "keyword", + }, + ], + }, + "size": 4, + "status": 200, + }, + "explorerFields": Array [ + Object { + "name": "avg(FlightDelayMin)", + "type": "double", + }, + Object { + "name": "Carrier", + "type": "keyword", + }, + ], + }, + "indexFields": Object {}, + "query": Object { + "rawQuery": "source = opensearch_dashboards_sample_data_logs | where match(machine.os,'win') | stats avg(machine.ram) by span(timestamp,1d)", + }, + "rawVizData": Object { + "data": Object { + "Carrier": Array [ + "BeatsWest", + "Logstash Airways", + "OpenSearch Dashboards Airlines", + "OpenSearch-Air", + ], + "avg(FlightDelayMin)": Array [ + 53.65384615384615, + 45.36144578313253, + 63.1578947368421, + 46.81318681318681, + ], + }, + "jsonData": Array [ + Object { + "Carrier": "BeatsWest", + "avg(FlightDelayMin)": 53.65384615384615, }, Object { "Carrier": "Logstash Airways", @@ -918,72 +1635,281 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` }, "vis": Object { "category": "Visualizations", - "categoryAxis": "xaxis", + "categoryaxis": "xaxis", "component": [Function], - "editorConfig": Object { + "editorconfig": Object { "panelTabs": Array [ Object { "editor": [Function], "id": "data-panel", "mapTo": "dataConfig", - "name": "Data", + "name": "Style", "sections": Array [ Object { "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, - "mapTo": "xaxis", - "name": "X-axis", + "mapTo": "showLegend", + "name": "Show legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, }, Object { "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "legendSize", + "name": "Legend size", + "title": "Legend size", }, ], }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "tooltip_options", + "mapTo": "tooltipOptions", + "name": "Tooltip options", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, - "mapTo": "mode", + "mapTo": "tooltipMode", + "name": "Tooltip mode", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, + }, + Object { + "component": null, + "mapTo": "tooltipText", + "name": "Tooltip text", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "all", + "name": "All", + }, + ], + "options": Array [ + Object { + "id": "all", + "name": "All", + }, + Object { + "id": "x", + "name": "Dimension", + }, + Object { + "id": "y", + "name": "Series", + }, + ], + }, + }, + ], + }, + Object { + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", + "mapTo": "style", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "lines", + "id": "lines", "name": "Lines", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "markers", - "name": "Markers", + "id": "lines", + "name": "Lines", }, Object { - "modeId": "lines", - "name": "Lines", + "id": "markers", + "name": "Marker", }, Object { - "modeId": "lines+markers", + "id": "lines+markers", "name": "Lines + Markers", }, ], }, }, + Object { + "component": [Function], + "eleType": "buttons", + "mapTo": "interpolation", + "name": "Interpolation", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "spline", + "name": "Smooth", + }, + ], + "options": Array [ + Object { + "id": "linear", + "name": "Linear", + }, + Object { + "id": "spline", + "name": "Smooth", + }, + Object { + "id": "hv", + "name": "Step before", + }, + Object { + "id": "vh", + "name": "Step after", + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 2, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 70, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill opacity", + "props": Object { + "max": 100, + }, + }, + Object { + "component": [Function], + "defaultState": 5, + "eleType": "slider", + "mapTo": "pointSize", + "name": "Point size", + "props": Object { + "max": 40, + }, + }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label size", + "title": "Label size", + }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateLabels", + "name": "Rotate labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color theme", + "schemas": Array [], + }, Object { "defaultState": Array [], "editor": [Function], @@ -995,11 +1921,10 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` ], }, Object { - "content": Array [], "editor": [Function], - "id": "style-panel", - "mapTo": "layoutConfig", - "name": "Layout", + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", }, Object { "editor": [Function], @@ -1009,18 +1934,18 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` }, ], }, - "fullLabel": "Line", + "fulllabel": "Time series", "icon": [Function], - "iconType": "visLine", + "icontype": "visLine", "id": "line", - "label": "Line", + "label": "Time series", "name": "line", "selection": Object { "dataLoss": "nothing", }, - "seriesAxis": "yaxis", + "seriesaxis": "yaxis", "type": "line", - "visConfig": Object { + "visconfig": Object { "config": Object { "barmode": "line", "displaylogo": false, @@ -1045,7 +1970,7 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "#BD6F26", "#4C636F", ], - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -1084,61 +2009,129 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "defaultAxes": Object { "xaxis": Array [ Object { - "name": "Carrier", - "type": "keyword", + "label": "", + "name": "", }, ], "yaxis": Array [ Object { + "label": "avg(FlightDelayMin)", "name": "avg(FlightDelayMin)", "type": "double", }, - ], - }, - "indexFields": Object {}, - "query": Object {}, - "rawVizData": Object { - "data": Object { - "Carrier": Array [ - "BeatsWest", - "Logstash Airways", - "OpenSearch Dashboards Airlines", - "OpenSearch-Air", - ], - "avg(FlightDelayMin)": Array [ - 53.65384615384615, - 45.36144578313253, - 63.1578947368421, - 46.81318681318681, - ], - }, - "jsonData": Array [ - Object { - "Carrier": "BeatsWest", - "avg(FlightDelayMin)": 53.65384615384615, - }, - Object { - "Carrier": "Logstash Airways", - "avg(FlightDelayMin)": 45.36144578313253, - }, Object { - "Carrier": "OpenSearch Dashboards Airlines", - "avg(FlightDelayMin)": 63.1578947368421, - }, - Object { - "Carrier": "OpenSearch-Air", - "avg(FlightDelayMin)": 46.81318681318681, + "label": "Carrier", + "name": "Carrier", + "type": "keyword", }, ], - "metadata": Object { - "fields": Array [ + }, + "explorer": Object { + "explorerData": Object { + "data": Object { + "Carrier": Array [ + "BeatsWest", + "Logstash Airways", + "OpenSearch Dashboards Airlines", + "OpenSearch-Air", + ], + "avg(FlightDelayMin)": Array [ + 53.65384615384615, + 45.36144578313253, + 63.1578947368421, + 46.81318681318681, + ], + }, + "jsonData": Array [ Object { - "name": "avg(FlightDelayMin)", - "type": "double", + "Carrier": "BeatsWest", + "avg(FlightDelayMin)": 53.65384615384615, }, Object { - "name": "Carrier", - "type": "keyword", + "Carrier": "Logstash Airways", + "avg(FlightDelayMin)": 45.36144578313253, + }, + Object { + "Carrier": "OpenSearch Dashboards Airlines", + "avg(FlightDelayMin)": 63.1578947368421, + }, + Object { + "Carrier": "OpenSearch-Air", + "avg(FlightDelayMin)": 46.81318681318681, + }, + ], + "metadata": Object { + "fields": Array [ + Object { + "name": "avg(FlightDelayMin)", + "type": "double", + }, + Object { + "name": "Carrier", + "type": "keyword", + }, + ], + }, + "size": 4, + "status": 200, + }, + "explorerFields": Array [ + Object { + "name": "avg(FlightDelayMin)", + "type": "double", + }, + Object { + "name": "Carrier", + "type": "keyword", + }, + ], + }, + "indexFields": Object {}, + "query": Object { + "rawQuery": "source = opensearch_dashboards_sample_data_logs | where match(machine.os,'win') | stats avg(machine.ram) by span(timestamp,1d)", + }, + "rawVizData": Object { + "data": Object { + "Carrier": Array [ + "BeatsWest", + "Logstash Airways", + "OpenSearch Dashboards Airlines", + "OpenSearch-Air", + ], + "avg(FlightDelayMin)": Array [ + 53.65384615384615, + 45.36144578313253, + 63.1578947368421, + 46.81318681318681, + ], + }, + "jsonData": Array [ + Object { + "Carrier": "BeatsWest", + "avg(FlightDelayMin)": 53.65384615384615, + }, + Object { + "Carrier": "Logstash Airways", + "avg(FlightDelayMin)": 45.36144578313253, + }, + Object { + "Carrier": "OpenSearch Dashboards Airlines", + "avg(FlightDelayMin)": 63.1578947368421, + }, + Object { + "Carrier": "OpenSearch-Air", + "avg(FlightDelayMin)": 46.81318681318681, + }, + ], + "metadata": Object { + "fields": Array [ + Object { + "name": "avg(FlightDelayMin)", + "type": "double", + }, + Object { + "name": "Carrier", + "type": "keyword", }, ], }, @@ -1149,72 +2142,281 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` }, "vis": Object { "category": "Visualizations", - "categoryAxis": "xaxis", + "categoryaxis": "xaxis", "component": [Function], - "editorConfig": Object { + "editorconfig": Object { "panelTabs": Array [ Object { "editor": [Function], "id": "data-panel", "mapTo": "dataConfig", - "name": "Data", + "name": "Style", "sections": Array [ Object { "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, - "mapTo": "xaxis", - "name": "X-axis", + "mapTo": "showLegend", + "name": "Show legend", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, }, Object { "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "legendSize", + "name": "Legend size", + "title": "Legend size", }, ], }, Object { "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", + "id": "tooltip_options", + "mapTo": "tooltipOptions", + "name": "Tooltip options", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, - "mapTo": "mode", + "mapTo": "tooltipMode", + "name": "Tooltip mode", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, + }, + Object { + "component": null, + "mapTo": "tooltipText", + "name": "Tooltip text", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "all", + "name": "All", + }, + ], + "options": Array [ + Object { + "id": "all", + "name": "All", + }, + Object { + "id": "x", + "name": "Dimension", + }, + Object { + "id": "y", + "name": "Series", + }, + ], + }, + }, + ], + }, + Object { + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", + "mapTo": "style", "name": "Mode", "props": Object { "defaultSelections": Array [ Object { - "modeId": "lines", + "id": "lines", "name": "Lines", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "markers", - "name": "Markers", + "id": "lines", + "name": "Lines", }, Object { - "modeId": "lines", - "name": "Lines", + "id": "markers", + "name": "Marker", }, Object { - "modeId": "lines+markers", + "id": "lines+markers", "name": "Lines + Markers", }, ], }, }, + Object { + "component": [Function], + "eleType": "buttons", + "mapTo": "interpolation", + "name": "Interpolation", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "spline", + "name": "Smooth", + }, + ], + "options": Array [ + Object { + "id": "linear", + "name": "Linear", + }, + Object { + "id": "spline", + "name": "Smooth", + }, + Object { + "id": "hv", + "name": "Step before", + }, + Object { + "id": "vh", + "name": "Step after", + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 2, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 70, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill opacity", + "props": Object { + "max": 100, + }, + }, + Object { + "component": [Function], + "defaultState": 5, + "eleType": "slider", + "mapTo": "pointSize", + "name": "Point size", + "props": Object { + "max": 40, + }, + }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label size", + "title": "Label size", + }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateLabels", + "name": "Rotate labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color theme", + "schemas": Array [], + }, Object { "defaultState": Array [], "editor": [Function], @@ -1226,11 +2428,10 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` ], }, Object { - "content": Array [], "editor": [Function], - "id": "style-panel", - "mapTo": "layoutConfig", - "name": "Layout", + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", }, Object { "editor": [Function], @@ -1240,18 +2441,18 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` }, ], }, - "fullLabel": "Line", + "fulllabel": "Time series", "icon": [Function], - "iconType": "visLine", + "icontype": "visLine", "id": "line", - "label": "Line", + "label": "Time series", "name": "line", "selection": Object { "dataLoss": "nothing", }, - "seriesAxis": "yaxis", + "seriesaxis": "yaxis", "type": "line", - "visConfig": Object { + "visconfig": Object { "config": Object { "barmode": "line", "displaylogo": false, @@ -1276,7 +2477,7 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "#BD6F26", "#4C636F", ], - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -1321,47 +2522,7 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` }, } } - layout={ - Object { - "colorway": Array [ - "#3CA1C7", - "#8C55A3", - "#DB748A", - "#F2BE4B", - "#68CCC2", - "#2A7866", - "#843769", - "#374FB8", - "#BD6F26", - "#4C636F", - ], - "height": 500, - "legend": Object { - "orientation": "v", - "traceorder": "normal", - }, - "margin": Object { - "b": 30, - "l": 60, - "pad": 0, - "r": 30, - "t": 50, - }, - "paper_bgcolor": "rgba(0, 0, 0, 0)", - "plot_bgcolor": "rgba(0, 0, 0, 0)", - "showlegend": true, - "xaxis": Object { - "fixedrange": true, - "showgrid": false, - "visible": true, - }, - "yaxis": Object { - "fixedrange": true, - "showgrid": false, - "visible": true, - }, - } - } + layout={[Function]} visualizations={ Object { "data": Object { @@ -1369,19 +2530,87 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "defaultAxes": Object { "xaxis": Array [ Object { + "label": "", + "name": "", + }, + ], + "yaxis": Array [ + Object { + "label": "avg(FlightDelayMin)", + "name": "avg(FlightDelayMin)", + "type": "double", + }, + Object { + "label": "Carrier", "name": "Carrier", "type": "keyword", }, ], - "yaxis": Array [ + }, + "explorer": Object { + "explorerData": Object { + "data": Object { + "Carrier": Array [ + "BeatsWest", + "Logstash Airways", + "OpenSearch Dashboards Airlines", + "OpenSearch-Air", + ], + "avg(FlightDelayMin)": Array [ + 53.65384615384615, + 45.36144578313253, + 63.1578947368421, + 46.81318681318681, + ], + }, + "jsonData": Array [ + Object { + "Carrier": "BeatsWest", + "avg(FlightDelayMin)": 53.65384615384615, + }, + Object { + "Carrier": "Logstash Airways", + "avg(FlightDelayMin)": 45.36144578313253, + }, + Object { + "Carrier": "OpenSearch Dashboards Airlines", + "avg(FlightDelayMin)": 63.1578947368421, + }, + Object { + "Carrier": "OpenSearch-Air", + "avg(FlightDelayMin)": 46.81318681318681, + }, + ], + "metadata": Object { + "fields": Array [ + Object { + "name": "avg(FlightDelayMin)", + "type": "double", + }, + Object { + "name": "Carrier", + "type": "keyword", + }, + ], + }, + "size": 4, + "status": 200, + }, + "explorerFields": Array [ Object { "name": "avg(FlightDelayMin)", "type": "double", }, + Object { + "name": "Carrier", + "type": "keyword", + }, ], }, "indexFields": Object {}, - "query": Object {}, + "query": Object { + "rawQuery": "source = opensearch_dashboards_sample_data_logs | where match(machine.os,'win') | stats avg(machine.ram) by span(timestamp,1d)", + }, "rawVizData": Object { "data": Object { "Carrier": Array [ @@ -1434,72 +2663,281 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` }, "vis": Object { "category": "Visualizations", - "categoryAxis": "xaxis", + "categoryaxis": "xaxis", "component": [Function], - "editorConfig": Object { + "editorconfig": Object { "panelTabs": Array [ Object { "editor": [Function], "id": "data-panel", "mapTo": "dataConfig", - "name": "Data", + "name": "Style", "sections": Array [ Object { "editor": [Function], - "id": "value_options", - "mapTo": "valueOptions", - "name": "Value options", + "id": "legend", + "mapTo": "legend", + "name": "Legend", "schemas": Array [ Object { "component": null, - "isSingleSelection": true, - "mapTo": "xaxis", - "name": "X-axis", - }, - Object { - "component": null, - "isSingleSelection": false, - "mapTo": "yaxis", - "name": "Y-axis", - }, - ], - }, - Object { - "editor": [Function], - "id": "chart_options", - "mapTo": "chartOptions", - "name": "Chart options", - "schemas": Array [ - Object { - "component": null, - "isSingleSelection": true, - "mapTo": "mode", - "name": "Mode", + "mapTo": "showLegend", + "name": "Show legend", "props": Object { "defaultSelections": Array [ Object { - "modeId": "lines", - "name": "Lines", + "id": "show", + "name": "Show", }, ], - "dropdownList": Array [ + "options": Array [ Object { - "modeId": "markers", - "name": "Markers", + "id": "show", + "name": "Show", }, Object { - "modeId": "lines", - "name": "Lines", + "id": "hidden", + "name": "Hidden", }, - Object { - "modeId": "lines+markers", + ], + }, + }, + Object { + "component": null, + "mapTo": "position", + "name": "Position", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "v", + "name": "Right", + }, + ], + "options": Array [ + Object { + "id": "v", + "name": "Right", + }, + Object { + "id": "h", + "name": "Bottom", + }, + ], + }, + }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "legendSize", + "name": "Legend size", + "title": "Legend size", + }, + ], + }, + Object { + "editor": [Function], + "id": "tooltip_options", + "mapTo": "tooltipOptions", + "name": "Tooltip options", + "schemas": Array [ + Object { + "component": null, + "mapTo": "tooltipMode", + "name": "Tooltip mode", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "show", + "name": "Show", + }, + ], + "options": Array [ + Object { + "id": "show", + "name": "Show", + }, + Object { + "id": "hidden", + "name": "Hidden", + }, + ], + }, + }, + Object { + "component": null, + "mapTo": "tooltipText", + "name": "Tooltip text", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "all", + "name": "All", + }, + ], + "options": Array [ + Object { + "id": "all", + "name": "All", + }, + Object { + "id": "x", + "name": "Dimension", + }, + Object { + "id": "y", + "name": "Series", + }, + ], + }, + }, + ], + }, + Object { + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", + "mapTo": "style", + "name": "Mode", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "lines", + "name": "Lines", + }, + ], + "options": Array [ + Object { + "id": "lines", + "name": "Lines", + }, + Object { + "id": "markers", + "name": "Marker", + }, + Object { + "id": "lines+markers", "name": "Lines + Markers", }, ], }, }, + Object { + "component": [Function], + "eleType": "buttons", + "mapTo": "interpolation", + "name": "Interpolation", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "spline", + "name": "Smooth", + }, + ], + "options": Array [ + Object { + "id": "linear", + "name": "Linear", + }, + Object { + "id": "spline", + "name": "Smooth", + }, + Object { + "id": "hv", + "name": "Step before", + }, + Object { + "id": "vh", + "name": "Step after", + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 2, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 70, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill opacity", + "props": Object { + "max": 100, + }, + }, + Object { + "component": [Function], + "defaultState": 5, + "eleType": "slider", + "mapTo": "pointSize", + "name": "Point size", + "props": Object { + "max": 40, + }, + }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label size", + "title": "Label size", + }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateLabels", + "name": "Rotate labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, ], }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color theme", + "schemas": Array [], + }, Object { "defaultState": Array [], "editor": [Function], @@ -1511,11 +2949,10 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` ], }, Object { - "content": Array [], "editor": [Function], - "id": "style-panel", - "mapTo": "layoutConfig", - "name": "Layout", + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", }, Object { "editor": [Function], @@ -1525,18 +2962,18 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` }, ], }, - "fullLabel": "Line", + "fulllabel": "Time series", "icon": [Function], - "iconType": "visLine", + "icontype": "visLine", "id": "line", - "label": "Line", + "label": "Time series", "name": "line", "selection": Object { "dataLoss": "nothing", }, - "seriesAxis": "yaxis", + "seriesaxis": "yaxis", "type": "line", - "visConfig": Object { + "visconfig": Object { "config": Object { "barmode": "line", "displaylogo": false, @@ -1561,7 +2998,7 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` "#BD6F26", "#4C636F", ], - "height": 500, + "height": 1180, "legend": Object { "orientation": "v", "traceorder": "normal", @@ -1592,215 +3029,176 @@ exports[`Utils helper functions renders displayVisualization function 2`] = ` } } > - + +
+ +
+ +
+ + + + + + +
+ +

+ + + No results found + + +

+
+ +
+ +
+ + + + + +
+`; + +exports[`Utils helper functions renders displayVisualization function 3`] = ` +
+ - -
- - - - - -
-`; - -exports[`Utils helper functions renders displayVisualization function 3`] = ` -
- - - - - -
- - - - - -
-`; - -exports[`Utils helper functions renders displayVisualization function 4`] = ` -
- @@ -3232,26 +4530,15 @@ exports[`Utils helper functions renders displayVisualization function 4`] = ` "responsive": true, } } - data={ - Array [ - Object { - "marker": Object { - "color": Array [], - }, - "name": "count('ip')", - "orientation": "h", - "type": "bar", - "x": Array [], - "y": Array [], - }, - ] - } + data={Array []} debug={false} divId="explorerPlotComponent" layout={ Object { "autosize": true, - "barmode": "", + "bargap": 0.30000000000000004, + "bargroupgap": 0.030000000000000027, + "barmode": "group", "colorway": Array [ "#3CA1C7", "#8C55A3", @@ -3264,31 +4551,31 @@ exports[`Utils helper functions renders displayVisualization function 4`] = ` "#BD6F26", "#4C636F", ], - "height": 500, "hovermode": "closest", "legend": Object { "orientation": "v", - "traceorder": "normal", }, "margin": Object { "b": 30, - "l": 60, - "pad": 0, - "r": 30, + "l": 30, + "pad": 4, + "r": 5, "t": 50, }, - "showlegend": true, + "showlegend": "show", "title": "", "xaxis": Object { "automargin": true, - "rangemode": "normal", - "showgrid": true, - "zeroline": false, + "tickfont": Object { + "size": 12, + }, }, "yaxis": Object { - "rangemode": "normal", - "showgrid": true, - "zeroline": false, + "automargin": true, + "tickangle": 0, + "tickfont": Object { + "size": 12, + }, }, } } @@ -3316,3 +4603,5 @@ exports[`Utils helper functions renders displayVisualization function 4`] = `
`; + +exports[`Utils helper functions renders displayVisualization function 4`] = `
`; diff --git a/public/components/custom_panels/helpers/__tests__/utils.test.tsx b/public/components/custom_panels/helpers/__tests__/utils.test.tsx index e1d1f1dbb..55e6349e0 100644 --- a/public/components/custom_panels/helpers/__tests__/utils.test.tsx +++ b/public/components/custom_panels/helpers/__tests__/utils.test.tsx @@ -24,6 +24,10 @@ import { samplePPLEmptyResponse, samplePPLResponse, sampleSavedVisualization, + sampleSavedVisualizationForHorizontalBar, + sampleSavedVisualizationForLine, + sampleSavedVisualizationForPie, + sampleSavedVisualizationForTreeMap, } from '../../../../../test/panels_constants'; import { PPL_DATE_FORMAT } from '../../../../../common/constants/shared'; import React from 'react'; @@ -104,25 +108,31 @@ describe('Utils helper functions', () => { it('renders displayVisualization function', () => { const wrapper1 = mount( -
{displayVisualization(sampleSavedVisualization, samplePPLResponse, 'bar')}
+
+ {displayVisualization(sampleSavedVisualization.visualization, samplePPLResponse, 'bar')} +
); expect(wrapper1).toMatchSnapshot(); const wrapper2 = mount( -
{displayVisualization(sampleSavedVisualization, samplePPLResponse, 'line')}
+
{displayVisualization(sampleSavedVisualizationForLine, samplePPLResponse, 'line')}
); expect(wrapper2).toMatchSnapshot(); - const wrapper3 = mount( + const wrapper4 = mount(
- {displayVisualization(sampleSavedVisualization, samplePPLResponse, 'horizontal_bar')} + {displayVisualization( + sampleSavedVisualizationForHorizontalBar, + samplePPLResponse, + 'horizontal_bar' + )}
); - expect(wrapper3).toMatchSnapshot(); + expect(wrapper4).toMatchSnapshot(); - const wrapper4 = mount( + const wrapper6 = mount(
{displayVisualization({}, samplePPLEmptyResponse, 'horizontal_bar')}
); - expect(wrapper4).toMatchSnapshot(); + expect(wrapper6).toMatchSnapshot(); }); }); diff --git a/public/components/custom_panels/helpers/utils.tsx b/public/components/custom_panels/helpers/utils.tsx index 3121ddeee..855dd55b8 100644 --- a/public/components/custom_panels/helpers/utils.tsx +++ b/public/components/custom_panels/helpers/utils.tsx @@ -7,7 +7,7 @@ import dateMath from '@elastic/datemath'; import { ShortDate } from '@elastic/eui'; import { DurationRange } from '@elastic/eui/src/components/date_picker/types'; -import _ from 'lodash'; +import _, { isEmpty } from 'lodash'; import { Moment } from 'moment-timezone'; import React from 'react'; import { Layout } from 'react-grid-layout'; @@ -289,7 +289,7 @@ export const isPPLFilterValid = ( // Renders visualization in the vizualization container component export const displayVisualization = (metaData: any, data: any, type: string) => { - if (metaData === undefined || metaData === {}) { + if (metaData === undefined || isEmpty(metaData)) { return <>; } return ( @@ -297,9 +297,10 @@ export const displayVisualization = (metaData: any, data: any, type: string) => visualizations={getVizContainerProps({ vizId: type, rawVizData: data, - query: {}, + query: { rawQuery: metaData.query }, indexFields: {}, userConfigs: metaData.user_configs, + explorer: { explorerData: data, explorerFields: data.metadata.fields }, })} /> ); diff --git a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx index 2c97e7095..7c1f2dc9b 100644 --- a/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx +++ b/public/components/custom_panels/panel_modules/visualization_flyout/visualization_flyout.tsx @@ -239,6 +239,7 @@ export const VisaulizationFlyout = ({ hasNoInitialSelection onChange={(e) => onChangeSelection(e)} options={visualizationOptions} + value={selectValue} /> diff --git a/public/components/event_analytics/explorer/explorer.scss b/public/components/event_analytics/explorer/explorer.scss index 423e226e3..1b9696545 100644 --- a/public/components/event_analytics/explorer/explorer.scss +++ b/public/components/event_analytics/explorer/explorer.scss @@ -3,15 +3,22 @@ * SPDX-License-Identifier: Apache-2.0 */ - .liveStream { - margin : 8px; - height: 40px; - align-items: center; - justify-content: center; - flex-direction: row; - display: flex; - flex-grow: 1; - vertical-align: baseline; - } +#opensearch-dashboards-body { + overflow-y: hidden; +} +.liveStream { + margin : 8px; + height: 40px; + align-items: center; + justify-content: center; + flex-direction: row; + display: flex; + flex-grow: 1; + vertical-align: baseline; +} + +.mainContentTabs .euiResizableContainer { + height: calc(100vh - 298px); +} \ No newline at end of file diff --git a/public/components/event_analytics/explorer/explorer.tsx b/public/components/event_analytics/explorer/explorer.tsx index 9f46d43ee..4e2dfebc0 100644 --- a/public/components/event_analytics/explorer/explorer.tsx +++ b/public/components/event_analytics/explorer/explorer.tsx @@ -2,7 +2,6 @@ * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ -/* eslint-disable react-hooks/exhaustive-deps */ import './explorer.scss'; import React, { useState, useMemo, useEffect, useRef, useCallback, ReactElement } from 'react'; @@ -30,7 +29,13 @@ import { NoResults } from './no_results'; import { HitsCounter } from './hits_counter/hits_counter'; import { TimechartHeader } from './timechart_header'; import { ExplorerVisualizations } from './visualizations'; -import { IField, IQueryTab, IDefaultTimestampState } from '../../../../common/types/explorer'; +import { + IField, + IQueryTab, + IDefaultTimestampState, + ConfigListEntry, + DimensionSpan, +} from '../../../../common/types/explorer'; import { TAB_CHART_TITLE, TAB_EVENT_TITLE, @@ -53,14 +58,19 @@ import { DATE_PICKER_FORMAT, PPL_PATTERNS_REGEX, SELECTED_PATTERN, + GROUPBY, + AGGREGATIONS, + CUSTOM_LABEL, + VIZ_CONTAIN_XY_AXIS, } from '../../../../common/constants/explorer'; import { PPL_STATS_REGEX, PPL_NEWLINE_REGEX, LIVE_OPTIONS, LIVE_END_TIME, + VIS_CHART_TYPES, } from '../../../../common/constants/shared'; -import { getIndexPatternFromRawQuery, preprocessQuery, buildQuery } from '../../../../common/utils'; +import { getIndexPatternFromRawQuery, preprocessQuery, buildQuery, composeFinalQuery } from '../../../../common/utils'; import { useFetchEvents, useFetchVisualizations } from '../hooks'; import { changeQuery, changeDateRange, selectQueries } from '../redux/slices/query_slice'; import { selectQueryResult } from '../redux/slices/query_result_slice'; @@ -68,6 +78,7 @@ import { selectFields, updateFields, sortFields } from '../redux/slices/field_sl import { updateTabName } from '../redux/slices/query_tab_slice'; import { selectCountDistribution } from '../redux/slices/count_distribution_slice'; import { selectExplorerVisualization } from '../redux/slices/visualization_slice'; +import { change as changeVizConfig } from '../redux/slices/viualization_config_slice'; import { selectVisualizationConfig, change as changeVisualizationConfig, @@ -82,6 +93,12 @@ import { sleep } from '../../common/live_tail/live_tail_button'; import { PatternsTable } from './log_patterns/patterns_table'; import { selectPatterns } from '../redux/slices/patterns_slice'; import { useFetchPatterns } from '../hooks/use_fetch_patterns'; +import { + statsChunk, + GroupByChunk, + StatsAggregationChunk, + GroupField, +} from '../../../../common/query_manager/ast/types'; const TYPE_TAB_MAPPING = { [SAVED_QUERY]: TAB_EVENT_ID, @@ -110,6 +127,7 @@ export const Explorer = ({ setEndTime, callback, callbackInApp, + queryManager, }: IExplorerProps) => { const dispatch = useDispatch(); const requestParams = { tabId }; @@ -216,24 +234,6 @@ export const Explorer = ({ }; }; - const composeFinalQuery = ( - curQuery: any, - startingTime: string, - endingTime: string, - timeField: string, - isLiveQuery: boolean - ) => { - const fullQuery = buildQuery(appBasedRef.current, curQuery![RAW_QUERY]); - if (isEmpty(fullQuery)) return ''; - return preprocessQuery({ - rawQuery: fullQuery, - startTime: startingTime, - endTime: endingTime, - timeField, - isLiveQuery, - }); - }; - const getSavedDataById = async (objectId: string) => { // load saved query/visualization if object id exists await savedObjects @@ -326,6 +326,7 @@ export const Explorer = ({ const curQuery = queryRef.current; const rawQueryStr = buildQuery(appBasedRef.current, curQuery![RAW_QUERY]); const curIndex = getIndexPatternFromRawQuery(rawQueryStr); + if (isEmpty(rawQueryStr)) return; if (isEmpty(curIndex)) { @@ -334,7 +335,6 @@ export const Explorer = ({ } let curTimestamp: string = curQuery![SELECTED_TIMESTAMP]; - if (isEmpty(curTimestamp)) { const defaultTimestamp = await getDefaultTimestampByIndexPattern(curIndex); if (isEmpty(defaultTimestamp.default_timestamp)) { @@ -367,11 +367,12 @@ export const Explorer = ({ // compose final query const finalQuery = composeFinalQuery( - curQuery, + curQuery![RAW_QUERY], startingTime!, endingTime!, curTimestamp, - isLiveTailOnRef.current + isLiveTailOnRef.current, + appBasedRef.current ); await dispatch( @@ -386,8 +387,18 @@ export const Explorer = ({ // search if (finalQuery.match(PPL_STATS_REGEX)) { + const cusVisIds = userVizConfigs ? Object.keys(userVizConfigs) : []; getVisualizations(); getAvailableFields(`search source=${curIndex}`); + for (const visId of cusVisIds) { + dispatch( + changeVisualizationConfig({ + tabId, + vizId: visId, + data: { ...userVizConfigs[visId] }, + }) + ); + } } else { findAutoInterval(startTime, endTime); if (isLiveTailOnRef.current) { @@ -618,7 +629,7 @@ export const Explorer = ({ data-test-subj="eventExplorer__sidebar" > {!isSidebarClosed && ( -
+
); }; @@ -907,6 +920,7 @@ export const Explorer = ({ isLiveTailOnRef.current, patternsData, viewLogPatterns, + userVizConfigs ]); const handleContentTabClick = (selectedTab: IQueryTab) => setSelectedContentTab(selectedTab.id); @@ -933,6 +947,123 @@ export const Explorer = ({ ); }; + const getSpanValue = (groupByToken: GroupByChunk) => { + const timeUnitValue = TIME_INTERVAL_OPTIONS.find( + (time_unit) => time_unit.value === groupByToken?.span?.span_expression.time_unit + )?.text; + return groupByToken?.span !== null + ? { + time_field: [ + { + name: groupByToken?.span.span_expression.field, + type: 'timestamp', + label: groupByToken?.span.span_expression.field, + }, + ], + unit: [ + { + text: timeUnitValue, + value: groupByToken?.span.span_expression.time_unit, + label: timeUnitValue, + }, + ], + interval: groupByToken?.span.span_expression.literal_value, + } + : undefined; + }; + + const getUpdatedDataConfig = (statsToken: statsChunk) => { + if (statsToken === null) { + return { + [GROUPBY]: [], + [AGGREGATIONS]: [], + }; + } + + const groupByToken = statsToken.groupby; + const seriesToken = statsToken.aggregations && statsToken.aggregations[0]; + const span = getSpanValue(groupByToken); + switch (curVisId) { + case VIS_CHART_TYPES.TreeMap: + return { + [GROUPBY]: [ + { + childField: { + ...(groupByToken?.group_fields + ? { + label: groupByToken?.group_fields[0].name ?? '', + name: groupByToken?.group_fields[0].name ?? '', + } + : { label: '', name: '' }), + }, + parentFields: [], + }, + ], + [AGGREGATIONS]: [ + { + valueField: { + ...(seriesToken + ? { + label: `${seriesToken.function?.name}(${seriesToken.function?.value_expression})`, + name: `${seriesToken.function?.name}(${seriesToken.function?.value_expression})`, + } + : { label: '', name: '' }), + }, + }, + ], + }; + case VIS_CHART_TYPES.Histogram: + return { + [GROUPBY]: [{ bucketSize: '', bucketOffset: '' }], + [AGGREGATIONS]: [], + }; + case VIS_CHART_TYPES.LogsView: { + const dimensions = statsToken.aggregations + .map((agg) => { + const logViewField = `${agg.function.name}(${agg.function.value_expression})` ?? ''; + return { + label: logViewField, + name: logViewField, + }; + }) + .concat( + groupByToken.group_fields?.map((agg) => ({ + label: agg.name ?? '', + name: agg.name ?? '', + })) + ); + if (span !== undefined) { + const { time_field, interval, unit } = span; + const timespanField = `span(${time_field[0].name},${interval}${unit[0].value})`; + dimensions.push({ + label: timespanField, + name: timespanField, + }); + } + return { + [AGGREGATIONS]: [], + [GROUPBY]: dimensions, + }; + } + + default: + return { + [AGGREGATIONS]: statsToken.aggregations.map((agg) => ({ + label: agg.function?.value_expression, + name: agg.function?.value_expression, + aggregation: agg.function?.name, + [CUSTOM_LABEL]: agg[CUSTOM_LABEL as keyof StatsAggregationChunk], + })), + [GROUPBY]: groupByToken?.group_fields?.map((agg) => ({ + label: agg.name ?? '', + name: agg.name ?? '', + [CUSTOM_LABEL]: agg[CUSTOM_LABEL as keyof GroupField] ?? '', + })), + span, + }; + } + }; + const handleQuerySearch = useCallback( async (availability?: boolean) => { // clear previous selected timestamp when index pattern changes @@ -946,14 +1077,25 @@ export const Explorer = ({ if (availability !== true) { await updateQueryInStore(tempQuery); } - fetchData(); + await fetchData(); + + if (selectedContentTabId === TAB_CHART_ID) { + // parse stats section on every search + const statsTokens = queryManager.queryParser().parse(tempQuery).getStats(); + const updatedDataConfig = getUpdatedDataConfig(statsTokens); + await dispatch( + changeVizConfig({ + tabId, + vizId: curVisId, + data: { dataConfig: { ...updatedDataConfig } }, + }) + ); + } }, - [tempQuery, query[RAW_QUERY]] + [tempQuery, query, selectedContentTabId] ); - const handleQueryChange = async (newQuery: string) => { - setTempQuery(newQuery); - }; + const handleQueryChange = async (newQuery: string) => setTempQuery(newQuery); const handleSavingObject = async () => { const currQuery = queryRef.current; @@ -1259,6 +1401,14 @@ export const Explorer = ({ explorerVisualizations, setToast, pplService, + handleQuerySearch, + handleQueryChange, + setTempQuery, + fetchData, + explorerFields, + explorerData, + http, + query, }} >
@@ -1294,6 +1444,7 @@ export const Explorer = ({ stopLive={stopLive} setIsLiveTailPopoverOpen={setIsLiveTailPopoverOpen} liveTailName={liveTailNameRef.current} + searchError={explorerVisualizations} /> { const dispatch = useDispatch(); const tabIds = useSelector(selectQueryTabs).queryTabIds.filter( @@ -184,6 +185,7 @@ export const LogExplorer = ({ curSelectedTabId={curSelectedTabIdRef} http={http} searchBarConfigs={searchBarConfigs} + queryManager={queryManager} /> ), diff --git a/public/components/event_analytics/explorer/no_results.tsx b/public/components/event_analytics/explorer/no_results.tsx index 802099729..91cc8ab62 100644 --- a/public/components/event_analytics/explorer/no_results.tsx +++ b/public/components/event_analytics/explorer/no_results.tsx @@ -38,8 +38,7 @@ export const NoResults = () => {

diff --git a/public/components/event_analytics/explorer/sidebar/__tests__/__snapshots__/field.test.tsx.snap b/public/components/event_analytics/explorer/sidebar/__tests__/__snapshots__/field.test.tsx.snap index 6bcdbaa2e..35c8c3a91 100644 --- a/public/components/event_analytics/explorer/sidebar/__tests__/__snapshots__/field.test.tsx.snap +++ b/public/components/event_analytics/explorer/sidebar/__tests__/__snapshots__/field.test.tsx.snap @@ -21,7 +21,7 @@ exports[`Field component Renders a sidebar field 1`] = ` anchorPosition="rightUp" button={ @@ -87,7 +87,7 @@ exports[`Field component Renders a sidebar field 1`] = ` } isActive={false} onClick={[Function]} - size="s" + size="m" /> } closePopover={[Function]} @@ -105,7 +105,7 @@ exports[`Field component Renders a sidebar field 1`] = ` className="euiPopover__anchor" > @@ -171,10 +171,10 @@ exports[`Field component Renders a sidebar field 1`] = ` } isActive={false} onClick={[Function]} - size="s" + size="m" >
-
- - - - - - - - - - - - - -
-
-
-
-
- - - -
  • - - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - host - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    + + + + + + Query fields + + + + +
    +
    + +
    - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - host - - } - isActive={false} - onClick={[Function]} - size="s" +
      -
      - -
      - - - - - - - - + + + + + + + + + + + + + + + + double_per_ip_bytes + + + +
      + + + + + + + + + + + + + +
      +
      + +
      +
    + + +
  • +
  • + + + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + host + + } + isActive={false} + onClick={[Function]} + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    - - - - - -
    -
  • - -
    -
    - - - -
  • - - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - + + + + + + + + + + + + + + + + host + + + +
    + + + + + + + + + + + + + +
    +
  • + + + + +
    + +
  • - ip_count - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - - - - - - + + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + ip_count + + } + isActive={false} + onClick={[Function]} + size="m" /> - - - - } - fieldIcon={ - - } - fieldName={ - - ip_count - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - + + + + + + + + + + + + + + + + ip_count + + + +
    + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
  • +
  • + + + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + per_ip_bytes + + } + isActive={false} + onClick={[Function]} + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    - - - - - -
    - - - - -
    -
    -
  • -
  • - - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - + + + + + + + + + + + + + + + + per_ip_bytes + + + +
    + + + + + + + + + + + + + +
    + + + + +
    +
    +
  • +
  • - per_ip_bytes - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - - - - - - + + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + resp_code + + } + isActive={false} + onClick={[Function]} + size="m" /> - - - - } - fieldIcon={ - - } - fieldName={ - - per_ip_bytes - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - + + + + + + + + + + + + + + + + resp_code + + + +
    + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
  • +
  • + + + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + sum_bytes + + } + isActive={false} + onClick={[Function]} + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    - - - - - -
    - - + +
    + + + + + + + + + + + + + +
    + + + + +
    +
    +
  • + - - - -
  • + + + + +
    + + + + Selected Fields + + + } + id="fieldSelector__selectedFields" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + paddingSize="xs" + > +
    - - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - resp_code - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" + -
    - - - - - - - - - - - - - -
    -
    - -
    - - - -
  • -
  • - - - - - - - - - - - - - - - } - fieldIcon={ - + - } - fieldName={ - - sum_bytes - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    + + + -
    - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - sum_bytes - - } - isActive={false} - onClick={[Function]} - size="s" + -
    - -
    - - - - - - - - - - - - - -
    -
    -
    + Selected Fields + + + + +
    +
    + +
    +
    +
    - - -
  • - - -

    - - Selected Fields - -

    -
    + + + +
    -
      -
      - -

      - + Available Fields - -

      -
      + + + } + id="fieldSelector__availableFields" + initialIsOpen={true} + isLoading={false} + isLoadingMessage={false} + paddingSize="xs" + >
      - - -
      -
      -
        -
      • - +
        - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - agent - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" + -
        +
        - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - agent - - } - isActive={false} - onClick={[Function]} - size="s" +
          -
          - -
          - - - + } + isActive={false} onClick={[Function]} - size="s" + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
          +
          - - - - - - - - - - + + + + + + + + + + + + + + + + agent + + + +
          + + + + + + + + + + + + + + + + + +
          +
          + +
          +
          + + + +
        • + + + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + bytes + + } + isActive={false} onClick={[Function]} + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
          +
          - - - - -
          -
          - -
        • -
        - - -
      • -
      • - - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - + + + + + + + + + + + + + + + + bytes + + + +
        + + + + + + + + + + + + + +
        +
    + + + + + + +
  • - bytes - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - - - - - - + + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + clientip + + } + isActive={false} onClick={[Function]} + size="m" /> - - - - } - fieldIcon={ - - } - fieldName={ - - bytes - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - + + + + + + + + + + + + + + + + clientip + + + +
    + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
  • +
  • + + + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + event + + } + isActive={false} onClick={[Function]} + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    +
    - - - - -
    -
    - - - -
    -
    -
  • -
  • - - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - + + + + + + + + + + + + + + + + event + + + +
    + + + + + + + + + + + + + +
    + + + + +
    +
    +
  • +
  • - clientip - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - - - - - - + + + + + Override + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + extension + + } + isActive={false} onClick={[Function]} + size="m" /> - - - - } - fieldIcon={ - - } - fieldName={ - - clientip - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - + + + + + + + + + + + + + + + extension + + + +
    + + + + + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
  • +
  • + + + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + geo + + } + isActive={false} onClick={[Function]} + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    +
    - - - - -
    -
    - - - -
    -
    -
  • -
  • - - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - event - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - event - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
  • -
  • - - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - extension - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - extension - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
  • -
  • - - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - geo - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - geo - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
  • -
  • - - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - host - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - host - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
  • -
  • - - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - index - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - index - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
  • -
  • - - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - ip - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - ip - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
  • -
  • - - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - machine - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - machine - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
  • -
  • - - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - memory - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - memory - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
  • -
  • - - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - message - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - message - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
  • -
  • - - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - phpmemory - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - phpmemory - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
  • -
  • - - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - referer - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - referer - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
  • -
  • - - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - request - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - request - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - - - - - - - - - - - - -
    -
    -
    -
    -
    -
    -
    -
  • -
  • - - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - response - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - response - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    + + + + + + + + + + + + + + + + geo + + + +
    + + + + + + + + + + + + + +
    +
    +
    +
    +
    +
    +
    +
  • +
  • - -
    - - - - - - - - - - - - - - - - - -
    - - - - - - -
  • -
  • - - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - tags - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - - tags - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - + + + + + + + + + + + + + + + + host + + + +
    + + + + + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
  • +
  • + + + + + + Override + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + index + + } + isActive={false} onClick={[Function]} - size="s" + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    +
    - - - - - - - - - - + + + + + + + + + + + + + + + + index + + + +
    + + + + + + + + + + + + + + + + + +
    +
    + +
    + +
    +
    +
  • +
  • + + + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + ip + + } + isActive={false} onClick={[Function]} + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    +
    - - - - -
    -
    - - - -
    -
    -
  • -
  • - - - - - - - - - Default Timestamp - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - + + + + + + + + + + + + + + + + ip + + + +
    + + + + + + + + + + + + + +
    + + + + +
    +
    +
  • +
  • - timestamp - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - - - - Default Timestamp - - - - - - + + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + machine + + } + isActive={false} onClick={[Function]} + size="m" /> - - - - } - fieldIcon={ - - } - fieldName={ - - timestamp - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - + + + + + + + + + + + + + + + + machine + + + +
    + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
  • +
  • + + + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + memory + + } + isActive={false} + onClick={[Function]} + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    - - + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + memory + + } + isActive={false} + onClick={[Function]} + size="m" > - Default Timestamp - - - - - - - + +
    + + + + + + + + + + + + + +
    +
    + + + +
    +
    +
  • +
  • + + + + + + Override + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + message + + } + isActive={false} onClick={[Function]} + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    +
    - - - - -
    -
    - - - -
    -
    -
  • -
  • - - - - - - Override - - - - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - + + + + + + + + + + + + + + + + message + + + +
    + + + + + + + + + + + + + + + + + +
    + + + + +
    +
    +
  • +
  • - url - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - + + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + phpmemory + + } + isActive={false} onClick={[Function]} - size="s" + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    +
    - Override - - - - - - - - - + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + phpmemory + + } + isActive={false} + onClick={[Function]} + size="m" + > +
    + +
    + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
  • +
  • + + + + + + Override + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + referer + + } + isActive={false} onClick={[Function]} + size="m" /> - - - - } - fieldIcon={ - - } - fieldName={ - - url - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - + + + + + + + + + + + + + + + + referer + + + +
    + + + + + + + + + + + + + + + + + +
    +
    + +
    + +
    +
    +
  • +
  • + + + + + + Override + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + request + + } + isActive={false} onClick={[Function]} - size="s" + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    +
    - - - - - - - - - - + + + + + + + + + + + + + + + + request + + + +
    + + + + + + + + + + + + + + + + + +
    +
    + +
    + +
    +
    +
  • +
  • + + + + + + Override + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + response + + } + isActive={false} onClick={[Function]} + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    +
    - - - - -
    -
    - - - -
    -
    -
  • -
  • - - - - - - - - - Override - - - - - - - - - - } - fieldIcon={ - - } - fieldName={ - + + + + + + + + + + + + + + + + response + + + +
    + + + + + + + + + + + + + + + + + +
    + + + + +
    +
    +
  • +
  • - utc_time - - } - isActive={false} - onClick={[Function]} - size="s" - /> - } - closePopover={[Function]} - display="block" - hasArrow={true} - isOpen={false} - ownFocus={true} - panelClassName="dscSidebarItem__fieldPopoverPanel" - panelPaddingSize="m" - > -
    -
    - - - - - - - + + + + + Override + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + tags + + } + isActive={false} onClick={[Function]} - size="s" + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    +
    - Override - - - - - - + + + + Override + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + tags + + } + isActive={false} + onClick={[Function]} + size="m" + > +
    + +
    + + + + + + + + + + + + + + + + + +
    +
    + +
    +
    + + +
  • +
  • + + + + + + + + + Default Timestamp + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + timestamp + + } + isActive={false} onClick={[Function]} + size="m" /> - - - - } - fieldIcon={ - - } - fieldName={ - - utc_time - - } - isActive={false} - onClick={[Function]} - size="s" - > -
    - -
    - - - - - - + + + + + + + + + + + + + + + + timestamp + + + +
    + + + + + + + + Default Timestamp + + + + + + + + + + + +
    +
    + +
    + +
    +
    +
  • +
  • + + + + + + Override + + + + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + url + + } + isActive={false} onClick={[Function]} - size="s" + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    +
    - - - - - - - + + + + + + + + + + + + + + + + url + + + +
    + + + + + + + + + + + + + + + + + +
    +
    + +
    + +
    +
    +
  • +
  • + + + + + + + + + Override + + + + + + + + + + } + fieldIcon={ + + } + fieldName={ + + utc_time + + } + isActive={false} onClick={[Function]} + size="m" + /> + } + closePopover={[Function]} + display="block" + hasArrow={true} + isOpen={false} + ownFocus={true} + panelClassName="dscSidebarItem__fieldPopoverPanel" + panelPaddingSize="m" + > +
    +
    - - - - -
    -
    - + +
    + + + + + + + + + + + + + + + + + +
    + + + + +
    +
    +
  • + - - - - + + + + diff --git a/public/components/event_analytics/explorer/sidebar/field.tsx b/public/components/event_analytics/explorer/sidebar/field.tsx index de8604b4d..2b18be672 100644 --- a/public/components/event_analytics/explorer/sidebar/field.tsx +++ b/public/components/event_analytics/explorer/sidebar/field.tsx @@ -10,11 +10,8 @@ import { EuiPopover, EuiButtonIcon, EuiToolTip, - EuiButton, EuiMark, EuiLoadingSpinner, - EuiPopoverTitle, - EuiPanel, EuiFlexGroup, EuiFlexItem, EuiTitle, @@ -182,8 +179,8 @@ export const Field = (props: IFieldProps) => { panelClassName="dscSidebarItem__fieldPopoverPanel" button={ } diff --git a/public/components/event_analytics/explorer/sidebar/field_insights.tsx b/public/components/event_analytics/explorer/sidebar/field_insights.tsx index 6a682cc5c..b9414ba70 100644 --- a/public/components/event_analytics/explorer/sidebar/field_insights.tsx +++ b/public/components/event_analytics/explorer/sidebar/field_insights.tsx @@ -48,8 +48,8 @@ export const FieldInsights = ({ field, query }: any) => { format: 'viz', }, ]; - const numericalTypes = ['short', 'integer', 'long', 'float', 'double']; - const isNumericalField = indexOf(numericalTypes, field.type) > 0; + const NUMERICAL_TYPES = ['short', 'integer', 'long', 'float', 'double']; + const isNumericalField = indexOf(NUMERICAL_TYPES, field.type) > 0; const [curReport, setCurReport] = useState({ ...generalReports[0] }); const [reportContent, setReportContent] = useState({}); @@ -137,7 +137,7 @@ export const FieldInsights = ({ field, query }: any) => { ); })} - {indexOf(numericalTypes, field.type) > 0 && + {indexOf(NUMERICAL_TYPES, field.type) > 0 && numericalOnlyReports.map((report) => { return ( diff --git a/public/components/event_analytics/explorer/sidebar/sidebar.scss b/public/components/event_analytics/explorer/sidebar/sidebar.scss index 8ef4cc875..a5ab8d061 100644 --- a/public/components/event_analytics/explorer/sidebar/sidebar.scss +++ b/public/components/event_analytics/explorer/sidebar/sidebar.scss @@ -37,25 +37,11 @@ background-color: lightOrDarkTheme(tint($euiColorPrimary, 90%), $euiColorLightShade); } -.dscFieldChooser { - padding-left: $euiSize; -} - .dscFieldChooser__toggle { color: $euiColorMediumShade; margin-left: $euiSizeS !important; } -.dscSidebarItem { - &:hover, - &:focus-within, - &[class*='-isActive'] { - .dscSidebarItem__action { - opacity: 1; - } - } -} - /** * 1. Only visually hide the action, so that it's still accessible to screen readers. * 2. When tabbed to, this element needs to be visible for keyboard accessibility. @@ -110,3 +96,47 @@ .override_timestamp_loading { vertical-align: middle; } + +.explorerFieldSelector { + padding: $euiSizeS; +} + +#vis__mainContent { + .explorer__insights { + min-height: 0; + display: grid; + grid-template-columns: 50% 50%; + height: 100%; + .explorerFieldSelector, .explorer__vizDataConfig { + padding: $euiSizeS; + overflow: auto; + + &__fieldGroups { + @include euiYScrollWithShadows; + + overflow-y: auto; + margin-right: -$euiSizeS; + padding-right: $euiSizeS; + margin-top: $euiSizeS; + } + + &__fieldGroup { + margin-top: $euiSizeS; + + &:first-child { + margin-top: 0; + } + } + } + .explorerFieldSelector { + @include euiYScrollWithShadows; + .sidebar-list { + height: 100%; + } + } + } +} + +.ws__configPanel--right{ + overflow-y: unset; +} diff --git a/public/components/event_analytics/explorer/sidebar/sidebar.tsx b/public/components/event_analytics/explorer/sidebar/sidebar.tsx index c54b89baa..794ce5328 100644 --- a/public/components/event_analytics/explorer/sidebar/sidebar.tsx +++ b/public/components/event_analytics/explorer/sidebar/sidebar.tsx @@ -7,10 +7,8 @@ import './sidebar.scss'; import React, { useState } from 'react'; import { isEmpty } from 'lodash'; -import { EuiTitle, EuiSpacer, EuiButtonIcon, EuiFieldSearch } from '@elastic/eui'; -import { i18n } from '@osd/i18n'; -import { FormattedMessage, I18nProvider } from '@osd/i18n/react'; -import { cssNumber } from 'jquery'; +import { EuiTitle, EuiSpacer, EuiFieldSearch, EuiAccordion } from '@elastic/eui'; +import { I18nProvider } from '@osd/i18n/react'; import { Field } from './field'; import { IExplorerFields, IField } from '../../../../../common/types/explorer'; @@ -51,7 +49,6 @@ export const Sidebar = (props: ISidebarProps) => { return (
    -
    { !isEmpty(explorerFields.availableFields)) && ( <> {explorerFields?.queriedFields && explorerFields.queriedFields?.length > 0 && ( - <> - -

    - -

    -
    - + + Query fields + + } + paddingSize="xs" + >
      {explorerFields.queriedFields && @@ -111,93 +108,28 @@ export const Sidebar = (props: ISidebarProps) => { ); })}
    - +
    )} - -

    - -

    -
    - -
      - {explorerData && - !isEmpty(explorerData.jsonData) && - explorerFields.selectedFields && - explorerFields.selectedFields.map((field) => { - return ( -
    • - -
    • - ); - })} -
    -
    - -

    - -

    -
    -
    - setShowFields(!showFields)} - aria-label={ - showFields - ? i18n.translate( - 'discover.fieldChooser.filter.indexAndFieldsSectionHideAriaLabel', - { - defaultMessage: 'Hide fields', - } - ) - : i18n.translate( - 'discover.fieldChooser.filter.indexAndFieldsSectionShowAriaLabel', - { - defaultMessage: 'Show fields', - } - ) - } - /> -
    -
    -
      + + Selected Fields + + } + paddingSize="xs" > - {explorerFields.availableFields && - explorerFields.availableFields - .filter((field) => searchTerm === '' || field.name.indexOf(searchTerm) !== -1) - .map((field) => { +
        + {explorerData && + !isEmpty(explorerData.jsonData) && + explorerFields.selectedFields && + explorerFields.selectedFields.map((field) => { return (
      • { selectedTimestamp={selectedTimestamp} isOverridingTimestamp={isOverridingTimestamp} handleOverrideTimestamp={handleOverrideTimestamp} - onToggleField={handleAddField} - selected={false} + selected={true} isFieldToggleButtonDisabled={isFieldToggleButtonDisabled} showTimestampOverrideButton={true} + onToggleField={handleRemoveField} />
      • ); })} -
      +
    + + + + Available Fields + + } + paddingSize="xs" + > +
      + {explorerFields.availableFields && + explorerFields.availableFields + .filter((field) => searchTerm === '' || field.name.indexOf(searchTerm) !== -1) + .map((field) => { + return ( +
    • + +
    • + ); + })} +
    +
    )}
    diff --git a/public/components/event_analytics/explorer/visualizations/app.scss b/public/components/event_analytics/explorer/visualizations/app.scss index 2f01e1b8e..8f4e129e9 100644 --- a/public/components/event_analytics/explorer/visualizations/app.scss +++ b/public/components/event_analytics/explorer/visualizations/app.scss @@ -3,45 +3,15 @@ * SPDX-License-Identifier: Apache-2.0 */ -.lnsAppWrapper { - display: flex; - flex-direction: column; - flex-grow: 1; +.dataConfigContainer { + height: 1242px; + overflow: auto; } -.lnsApp { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; - display: flex; - flex-direction: column; +.explorerViz__commonPanel { + display: grid; + grid-template-rows: auto 1fr; + grid-area: workspace; + background-color: #FFF; height: 100%; - overflow: hidden; -} - -.lnsApp__header { - border-bottom: $euiBorderThin; -} - -.lnsApp__frame { - position: relative; - display: flex; - flex-direction: column; - flex-grow: 1; -} - -.lensChartIcon__subdued { - fill: $euiTextSubduedColor; - - // Not great, but the easiest way to fix the gray fill when stuck in a button with a fill - // Like when selected in a button group - .euiButton--fill & { - fill: currentColor; - } -} - -.lensChartIcon__accent { - fill: $euiColorVis0; } diff --git a/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap b/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap index 065ec0e9c..8f981e609 100644 --- a/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap +++ b/public/components/event_analytics/explorer/visualizations/config_panel/__tests__/__snapshots__/config_panel.test.tsx.snap @@ -2,6 +2,7 @@ exports[`Config panel component Renders config panel with visualization data 1`] = ` -
    - -
    - -
    - - -
    - - -
    -
    -
    - - - - +
    + + +
    +
    +
    + + + + + + + Vertical bar + + + + + + + + + + + + +
    + +
    +
    + +
    + +
    + + + +
    +
    +
    +
    + + +
    + +
    +
    + , + "id": "data-panel", + "name": "Style", + } + } + tabs={ + Array [ + Object { + "content": , + "id": "data-panel", + "name": "Style", + }, + Object { + "content": , + "id": "availability-panel", + "name": "Availability", + }, + Object { + "content": - - - Bar - - - - - - - - - - - - -
    - -
    -
    - -
    - -
    - - - - - - - - -
    -
    -
    -
    - - -
    - - -
    - -
    - - + "editor": [Function], + "id": "chart_styles", + "mapTo": "chartStyles", + "name": "Chart styles", + "schemas": Array [ + Object { + "component": [Function], + "eleType": "buttons", + "mapTo": "mode", + "name": "Mode", + "props": Object { + "defaultSelections": Array [ + Object { + "id": "group", + "name": "Group", + }, + ], + "options": Array [ + Object { + "id": "group", + "name": "Group", + }, + Object { + "id": "stack", + "name": "Stack", + }, + ], + }, + }, + Object { + "component": [Function], + "eleType": "input", + "mapTo": "labelSize", + "name": "Label size", + }, + Object { + "component": [Function], + "defaultState": 0, + "eleType": "slider", + "mapTo": "rotateBarLabels", + "name": "Rotate bar labels", + "props": Object { + "max": 90, + "min": -90, + "showTicks": true, + "ticks": Array [ + Object { + "label": "-90°", + "value": -90, + }, + Object { + "label": "-45°", + "value": -45, + }, + Object { + "label": "0°", + "value": 0, + }, + Object { + "label": "45°", + "value": 45, + }, + Object { + "label": "90°", + "value": 90, + }, + ], + }, + }, + Object { + "component": [Function], + "defaultState": 0.7, + "eleType": "slider", + "mapTo": "groupWidth", + "name": "Group width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 0.97, + "eleType": "slider", + "mapTo": "barWidth", + "name": "Bar width", + "props": Object { + "max": 1, + "step": 0.01, + }, + }, + Object { + "component": [Function], + "defaultState": 2, + "eleType": "slider", + "mapTo": "lineWidth", + "name": "Line width", + "props": Object { + "max": 10, + }, + }, + Object { + "component": [Function], + "defaultState": 70, + "eleType": "slider", + "mapTo": "fillOpacity", + "name": "Fill opacity", + "props": Object { + "max": 100, + }, + }, + ], + }, + Object { + "editor": [Function], + "id": "color-theme", + "mapTo": "colorTheme", + "name": "Color theme", + "schemas": Array [], + }, + ], + }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, + Object { + "editor": [Function], + "id": "availability-panel", + "mapTo": "availabilityConfig", + "name": "Availability", + }, + ], + }, + "fillopacity": 70, + "fulllabel": "Vertical bar", + "groupwidth": 0.7, + "icon": [Function], + "icontype": "visBarVerticalStacked", + "id": "bar", + "label": "Vertical bar", + "labelangle": 0, + "legendposition": "v", + "linewidth": 2, + "mode": "group", + "name": "bar", + "orientation": "v", + "selection": Object { + "dataLoss": "nothing", + }, + "seriesaxis": "yaxis", + "showlegend": "show", + "type": "bar", + "visconfig": Object { + "config": Object { + "displaylogo": false, + "responsive": true, + }, + "isUniColor": false, + "layout": Object { + "height": 1180, + "legend": Object { + "orientation": "v", + "traceorder": "normal", + }, + "margin": Object { + "b": 30, + "l": 60, + "pad": 0, + "r": 30, + "t": 50, + }, + "showlegend": true, + }, + }, + }, + } + } + />, + "id": "availability-panel", + "name": "Availability", + }, + ] + } + >
    -
    - + + + + + + + + +
    + +
    + +
    + +
    + +
    +
    + + +
    +
    + +
    +
    + +
    +
    + +
    + +
    +
    + + + +
    +
    + + +
    +
    + + + + +
    +
    +
    +
    + +
    + Name your visualization. +
    +
    +
    +
    +
    + +
    +
    + + + +
    +
    + + +