Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changelogs/fragments/7951.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- Add query result and time to the query editor footer ([#7951](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7951))
2 changes: 2 additions & 0 deletions src/plugins/data/public/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,8 @@ export {
LanguageServiceContract,
RecentQueriesTable,
QueryControls,
QueryResult,
QueryStatus,
SavedQuery,
SavedQueryService,
SavedQueryTimeFilter,
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/data/public/query/query_string/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,6 @@ export {
EditorEnhancements,
RecentQueriesTable,
QueryControls,
QueryResult,
QueryStatus,
} from './language_service';
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@

export * from './types';
export { LanguageServiceContract, LanguageService } from './language_service';
export { RecentQueriesTable, QueryControls } from './lib';
export { RecentQueriesTable, QueryControls, QueryResult, QueryStatus } from './lib';
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from './lucene_language';
export * from './default_language_reference';
export * from './get_query_control_links';
export * from './recent_query';
export * from './query_result';
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { i18n } from '@osd/i18n';

import './_recent_query.scss';
import { EuiButtonEmpty, EuiPopover, EuiText, EuiPopoverTitle } from '@elastic/eui';

import React, { useState } from 'react';

export enum ResultStatus {
UNINITIALIZED = 'uninitialized',
LOADING = 'loading', // initial data load
READY = 'ready', // results came back
NO_RESULTS = 'none', // no results came back
ERROR = 'error', // error occurred
}

export interface QueryStatus {
status: ResultStatus;
body?: {
error?: {
reason?: string;
details: string;
};
statusCode?: number;
};
elapsedMs?: number;
}

export function QueryResult(props: { queryStatus: QueryStatus }) {
const [isPopoverOpen, setPopover] = useState(false);
const onButtonClick = () => {
setPopover(!isPopoverOpen);
};

if (props.queryStatus.status === ResultStatus.READY) {
return (
<EuiButtonEmpty iconSide="left" iconType={'checkInCircleEmpty'} size="xs" onClick={() => {}}>
<EuiText size="xs" color="subdued">
{props.queryStatus.elapsedMs
? `Completed in ${props.queryStatus.elapsedMs} ms`
: 'Completed'}
</EuiText>
</EuiButtonEmpty>
);
}

if (!props.queryStatus.body || !props.queryStatus.body.error) {
return null;
}

return (
<EuiPopover
button={
<EuiButtonEmpty iconSide="left" iconType={'alert'} size="xs" onClick={onButtonClick}>
<EuiText size="xs" color="subdued">
{'Error'}
</EuiText>
</EuiButtonEmpty>
}
isOpen={isPopoverOpen}
closePopover={() => setPopover(false)}
panelPaddingSize="s"
anchorPosition={'downRight'}
>
<EuiPopoverTitle>ERRORS</EuiPopoverTitle>
<div style={{ width: '250px' }}>
<EuiText size="s">
<strong>
{i18n.translate('data.query.languageService.queryResults.reasons', {
defaultMessage: `Reasons:`,
})}
</strong>
{props.queryStatus.body.error.reason}
</EuiText>
<EuiText size="s">
<p>
<strong>
{i18n.translate('data.query.languageService.queryResults.details', {
defaultMessage: `Details:`,
})}
</strong>{' '}
{props.queryStatus.body.error.details}
</p>
</EuiText>
</div>
</EuiPopover>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ export class QueryStringManager {
const language = this.languageService.getLanguage(languageId);
const dataset = curQuery.dataset;
const input = language?.getQueryString(curQuery) || '';
this.languageService.setUserQueryString(input);

return {
query: input,
Expand Down
5 changes: 5 additions & 0 deletions src/plugins/data/public/ui/query_editor/query_editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
EuiText,
PopoverAnchorPosition,
} from '@elastic/eui';
import { BehaviorSubject } from 'rxjs';
import classNames from 'classnames';
import { isEqual } from 'lodash';
import React, { Component, createRef, RefObject } from 'react';
Expand All @@ -27,6 +28,8 @@ import {
TimeRange,
QueryControls,
RecentQueriesTable,
QueryResult,
QueryStatus,
} from '../..';
import { OpenSearchDashboardsReactContextValue } from '../../../../opensearch_dashboards_react/public';
import { fromUser, getQueryLog, PersistedLog, toUser } from '../../query';
Expand Down Expand Up @@ -61,6 +64,7 @@ export interface QueryEditorProps {
filterBar?: any;
prepend?: React.ComponentProps<typeof EuiCompressedFieldText>['prepend'];
savedQueryManagement?: any;
queryStatus?: QueryStatus;
}

interface Props extends QueryEditorProps {
Expand Down Expand Up @@ -366,6 +370,7 @@ export default class QueryEditorUI extends Component<Props, State> {
<EuiText size="xs" color="subdued">
{this.props.query.dataset?.timeFieldName || ''}
</EuiText>,
<QueryResult queryStatus={this.props.queryStatus!} />,
],
end: [
<EuiButtonEmpty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
withOpenSearchDashboards,
} from '../../../../opensearch_dashboards_react/public';
import { UI_SETTINGS } from '../../../common';
import { getQueryLog, PersistedLog } from '../../query';
import { getQueryLog, PersistedLog, QueryStatus } from '../../query';
import { NoDataPopover } from './no_data_popover';
import QueryEditorUI from './query_editor';

Expand Down Expand Up @@ -54,6 +54,7 @@ export interface QueryEditorTopRowProps {
indicateNoData?: boolean;
datePickerRef?: React.RefObject<HTMLDivElement>;
savedQueryManagement?: any;
queryStatus?: QueryStatus;
}

// Needed for React.lazy
Expand Down Expand Up @@ -186,6 +187,7 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) {
dataTestSubj={props.dataTestSubj}
filterBar={props.filterBar}
savedQueryManagement={props.savedQueryManagement}
queryStatus={props.queryStatus}
/>
</EuiFlexItem>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ export function createSearchBar({ core, storage, data }: StatefulSearchBarDeps)
showQueryBar={props.showQueryBar}
showQueryInput={props.showQueryInput}
showSaveQuery={props.showSaveQuery}
queryStatus={props.queryStatus}
screenTitle={props.screenTitle}
indexPatterns={props.indexPatterns}
indicateNoData={props.indicateNoData}
Expand Down
4 changes: 3 additions & 1 deletion src/plugins/data/public/ui/search_bar/search_bar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import {
withOpenSearchDashboards,
} from '../../../../opensearch_dashboards_react/public';
import { Filter, IIndexPattern, Query, TimeRange, UI_SETTINGS } from '../../../common';
import { SavedQuery, SavedQueryAttributes, TimeHistoryContract } from '../../query';
import { SavedQuery, SavedQueryAttributes, TimeHistoryContract, QueryStatus } from '../../query';
import { IDataPluginServices } from '../../types';
import { FilterBar } from '../filter_bar/filter_bar';
import { QueryEditorTopRow } from '../query_editor';
Expand Down Expand Up @@ -92,6 +92,7 @@ export interface SearchBarOwnProps {

onRefresh?: (payload: { dateRange: TimeRange }) => void;
indicateNoData?: boolean;
queryStatus?: QueryStatus;
}

export type SearchBarProps = SearchBarOwnProps & SearchBarInjectedDeps;
Expand Down Expand Up @@ -550,6 +551,7 @@ class SearchBarUI extends Component<SearchBarProps, State> {
indicateNoData={this.props.indicateNoData}
datePickerRef={this.props.datePickerRef}
savedQueryManagement={searchBarMenu(false, true)}
queryStatus={this.props.queryStatus}
/>
);
}
Expand Down
1 change: 1 addition & 0 deletions src/plugins/data/public/ui/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { QueryStatus } from '../query';
import { IndexPatternSelectProps } from './index_pattern_select';
import { StatefulSearchBarProps } from './search_bar';
import { SuggestionsComponentProps } from './typeahead/suggestions_component';
Expand Down
3 changes: 3 additions & 0 deletions src/plugins/data/public/ui/ui_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,16 @@
*/

import { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from 'src/core/public';
import { BehaviorSubject } from 'rxjs';
import { ConfigSchema } from '../../config';
import { DataPublicPluginStart } from '../types';
import { createIndexPatternSelect } from './index_pattern_select';
import { createSearchBar } from './search_bar/create_search_bar';
import { SuggestionsComponent } from './typeahead';
import { IUiSetup, IUiStart } from './types';
import { DataStorage } from '../../common';
import { QueryStatus } from '../query';
import { ResultStatus } from '../query/query_string/language_service/lib';

/** @internal */
// eslint-disable-next-line @typescript-eslint/no-empty-interface
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,9 @@ export default function DiscoverCanvas({ setHeaderActionMenu, history, optionalR
{fetchState.status === ResultStatus.NO_RESULTS && (
<DiscoverNoResults timeFieldName={timeField} queryLanguage={''} />
)}
{fetchState.status === ResultStatus.ERROR && (
<DiscoverNoResults timeFieldName={timeField} queryLanguage={''} />
)}
{fetchState.status === ResultStatus.UNINITIALIZED && (
<DiscoverUninitialized onRefresh={() => refetch$.next()} />
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import { createPortal } from 'react-dom';
import { EuiButtonIcon, EuiFlexGroup, EuiFlexItem, EuiToolTip } from '@elastic/eui';
import { i18n } from '@osd/i18n';
import { AppMountParameters } from '../../../../../../core/public';
import { connectStorageToQueryState, opensearchFilters } from '../../../../../data/public';
import {
connectStorageToQueryState,
opensearchFilters,
QueryStatus,
} from '../../../../../data/public';
import { useOpenSearchDashboards } from '../../../../../opensearch_dashboards_react/public';
import { PLUGIN_ID } from '../../../../common';
import { DiscoverViewServices } from '../../../build_services';
Expand All @@ -21,6 +25,7 @@ import { useDispatch, setSavedQuery, useSelector } from '../../utils/state_manag

import './discover_canvas.scss';
import { TopNavMenuItemRenderType } from '../../../../../navigation/public';
import { ResultStatus } from '../utils';

export interface TopNavProps {
opts: {
Expand All @@ -34,9 +39,10 @@ export interface TopNavProps {

export const TopNav = ({ opts, showSaveQuery, isEnhancementsEnabled }: TopNavProps) => {
const { services } = useOpenSearchDashboards<DiscoverViewServices>();
const { inspectorAdapters, savedSearch, indexPattern } = useDiscoverContext();
const { data$, inspectorAdapters, savedSearch, indexPattern } = useDiscoverContext();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we only require do we think we should maybe just create a new observable that this listens to instead of subscribing to a lot of data in the top nav?

const [indexPatterns, setIndexPatterns] = useState<IndexPattern[] | undefined>(undefined);
const [screenTitle, setScreenTitle] = useState<string>('');
const [queryStatus, setQueryStatus] = useState<QueryStatus>({ status: ResultStatus.READY });
const state = useSelector((s) => s.discover);
const dispatch = useDispatch();

Expand Down Expand Up @@ -69,6 +75,16 @@ export const TopNav = ({ opts, showSaveQuery, isEnhancementsEnabled }: TopNavPro
uiSettings
);

useEffect(() => {
const subscription = data$.subscribe((queryData) => {
const result = {
status: queryData.status,
...queryData.queryStatus,
};
setQueryStatus(result);
});
}, [data$]);

useEffect(() => {
let isMounted = true;
const initializeDataset = async () => {
Expand Down Expand Up @@ -160,6 +176,7 @@ export const TopNav = ({ opts, showSaveQuery, isEnhancementsEnabled }: TopNavPro
datePickerRef={opts?.optionalRef?.datePickerRef}
groupActions={showActionsInGroup}
screenTitle={screenTitle}
queryStatus={queryStatus}
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@

export * from './canvas';
export * from './panel';
export * from './utils';
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

export { SearchData, ResultStatus } from './use_search';
Loading