/* * Copyright OpenSearch Contributors * SPDX-License-Identifier: Apache-2.0 */ import React from "react"; // @ts-ignore import { SortableProperties, SortableProperty } from "@elastic/eui/lib/services"; // @ts-ignore import { EuiPanel, EuiFlexGroup, EuiFlexItem, EuiTab, EuiTabs, EuiPopover, EuiContextMenuItem, EuiContextMenuPanel, EuiHorizontalRule, EuiSearchBar, Pager, EuiIcon, EuiText, EuiSpacer, EuiTextAlign, EuiButton, EuiButtonIcon, Comparators } from "@elastic/eui"; import { QueryResult, QueryMessage, Tab, ResponseDetail, ItemIdToExpandedRowMap, DataRow } from "../Main/main"; import QueryResultsBody from "./QueryResultsBody"; import { getQueryIndex, needsScrolling, getSelectedResults } from "../../utils/utils"; import { DEFAULT_NUM_RECORDS_PER_PAGE, MESSAGE_TAB_LABEL, TAB_CONTAINER_ID } from "../../utils/constants"; import { PanelWrapper } from '../../utils/PanelWrapper'; import _ from 'lodash'; interface QueryResultsProps { language: string; queries: string[]; queryResults: ResponseDetail[]; queryResultsJSON: string; queryResultsJDBC: string; queryResultsCSV: string; queryResultsTEXT: string; messages: QueryMessage[]; selectedTabName: string; selectedTabId: string; searchQuery: string; tabsOverflow: boolean; onSelectedTabIdChange: (tab: Tab) => void; onQueryChange: (object: any) => void; updateExpandedMap: (map: ItemIdToExpandedRowMap) => void; itemIdToExpandedRowMap: ItemIdToExpandedRowMap; getJson: (queries: string[]) => void; getJdbc: (queries: string[]) => void; getCsv: (queries: string[]) => void; getText: (queries: string[]) => void; isResultFullScreen: boolean; setIsResultFullScreen: (isFullScreen: boolean) => void; } interface QueryResultsState { isPopoverOpen: boolean; tabsOverflow: boolean; itemsPerPage: number; } class QueryResults extends React.Component { public sortableColumns: Array>; public sortableProperties: SortableProperties; public sortedColumn: string; public tabNames: string[]; public pager: Pager; constructor(props: QueryResultsProps) { super(props); this.state = { isPopoverOpen: false, tabsOverflow: this.props.tabsOverflow ? this.props.tabsOverflow : false, itemsPerPage: DEFAULT_NUM_RECORDS_PER_PAGE }; this.sortableColumns = []; this.sortedColumn = ""; this.sortableProperties = new SortableProperties( [ { name: "", getValue: (item: any) => "", isAscending: true } ], "" ); this.tabNames = []; this.pager = new Pager(0, this.state.itemsPerPage); } componentDidUpdate() { const showArrow = needsScrolling("tabsContainer"); if (showArrow !== this.state.tabsOverflow) { this.setState({ tabsOverflow: showArrow }); } } // Actions for Tabs Button showTabsMenu = (): void => { this.setState(prevState => ({ isPopoverOpen: !prevState.isPopoverOpen })); }; slideTabsRight = (): void => { if (document.getElementById(TAB_CONTAINER_ID)) { document.getElementById(TAB_CONTAINER_ID)!.scrollBy(50, 0); } }; slideTabsLeft = (): void => { if (document.getElementById(TAB_CONTAINER_ID)) { document.getElementById(TAB_CONTAINER_ID)!.scrollBy(-50, 0); } }; closePopover = (): void => { this.setState({ isPopoverOpen: false }); }; onChangeItemsPerPage = (itemsPerPage: number) => { this.pager.setItemsPerPage(itemsPerPage); this.setState({ itemsPerPage }); }; onChangePage = (pageIndex: number) => { this.pager.goToPageIndex(pageIndex); this.setState({}); }; updatePagination(totalItemsCount: number): void { this.pager.setTotalItems(totalItemsCount); } // Update SORTABLE COLUMNS - All columns updateSortableColumns = (queryResultsSelected: QueryResult) => { if (this.sortableColumns.length != 0) { this.sortableColumns = []; } queryResultsSelected.fields.map((field: string) => { this.sortableColumns.push({ name: field, getValue: (item: DataRow) => item.data[field], isAscending: true }); }); this.sortedColumn = this.sortableColumns.length > 0 ? this.sortableColumns[0].name : ""; this.sortableProperties = new SortableProperties( this.sortableColumns, this.sortedColumn ); } searchItems(dataRows: DataRow[], searchQuery: string): DataRow[] { let rows: { [key: string]: any }[] = []; for (const row of dataRows) { rows.push(row.data) } const searchResult = EuiSearchBar.Query.execute(searchQuery, rows); let result: DataRow[] = []; for (const row of searchResult) { let dataRow: DataRow = { // rowId does not matter here since the data rows would be sorted later rowId: 0, data: row } result.push(dataRow) } return result; } onSort = (prop: string, items: DataRow[]): DataRow[] => { let sortedRows = this.sortDataRows(items, prop); this.sortableProperties.sortOn(prop) this.sortedColumn = prop; return sortedRows; } sortDataRows(dataRows: DataRow[], field: string): DataRow[] { const property = this.sortableProperties.getSortablePropertyByName(field); const copy = [...dataRows]; let comparator = (a: DataRow, b: DataRow) => { if (typeof property === "undefined") { return 0; } let dataA = a.data; let dataB = b.data; if (dataA[field] && dataB[field]) { if (dataA[field] > dataB[field]) { return 1; } if (dataA[field] < dataB[field]) { return -1; } } return 0; } if (!this.sortableProperties.isAscendingByName(field)) { Comparators.reverse(comparator); } return copy.sort(comparator); } renderTabs(): Tab[] { const tabs = [ { id: MESSAGE_TAB_LABEL, name: _.truncate(MESSAGE_TAB_LABEL, { length: 17 }), disabled: false } ]; this.tabNames = []; if (this.props.queryResults) { for (let i = 0; i < this.props.queryResults.length; i += 1) { const tabName = this.props.language === "SQL" ? getQueryIndex(this.props.queries[i]) : "Events"; this.tabNames.push(tabName); if (this.props.queryResults[i].fulfilled) { tabs.push({ id: i.toString(), name: tabName, disabled: false }); } } } return tabs; } render() { // Update PAGINATION and SORTABLE columns const queryResultSelected = getSelectedResults(this.props.queryResults, this.props.selectedTabId); if (queryResultSelected) { const matchingItems: object[] = this.props.searchQuery ? this.searchItems(queryResultSelected.records, this.props.searchQuery) : queryResultSelected.records; this.updatePagination(matchingItems.length); this.updateSortableColumns(queryResultSelected); } // Action button with list of tabs, TODO: disable tabArrowRight and tabArrowLeft when no more scrolling is possible const tabArrowDown = ( ); const tabs: Tab[] = this.renderTabs(); const tabsItems = tabs.map((tab, index) => ( { this.closePopover(); this.pager.goToPageIndex(0); this.sortableColumns = []; this.props.onSelectedTabIdChange(tab); }} > {tab.name} )); const tabsButtons = tabs.map((tab, index) => ( { this.pager.goToPageIndex(0); this.sortableColumns = []; this.props.onSelectedTabIdChange(tab); }} isSelected={tab.id === this.props.selectedTabId} disabled={tab.disabled} key={index} > {tab.name} )); return (

Results

{this.props.queryResults.length > 0 && ( this.props.isResultFullScreen ? this.props.setIsResultFullScreen(false)} /> : this.props.setIsResultFullScreen(true)} > Full screen view )}
{this.props.queryResults.length === 0 ? ( // show no results message instead of the results table when there are no results <>

No result

Enter a query in the query editor above to see results.

) : ( <> {/*TABS*/} {tabsButtons} {/*ARROW DOWN*/} {this.state.tabsOverflow && (
)}
{/*RESULTS TABLE*/} )}
); } } export default QueryResults;