+
{queryBar}
- {filterBar}
+ {queryEditor}
+ {!isEnhancementsEnabledOverride && filterBar}
{this.state.showSaveQueryModal ? (
(false);
+ private enhancedAppNames: string[] = [];
+
constructor(
+ private readonly config: ConfigSchema['enhancements'],
private readonly storage: IStorageWrapper,
private readonly queryEnhancements: Map
- ) {}
+ ) {
+ this.enabledQueryEnhancementsUpdated$.next(this.config.enabled);
+ this.enhancedAppNames = this.config.enabled ? this.config.supportedAppNames : [];
+ }
+
+ supportsEnhancementsEnabled(appName: string) {
+ return this.enhancedAppNames.includes(appName);
+ }
+
+ getEnabledQueryEnhancementsUpdated$ = () => {
+ return this.enabledQueryEnhancementsUpdated$.asObservable();
+ };
+
+ getUserQueryEnhancementsEnabled() {
+ return (
+ this.storage.get('opensearchDashboards.userQueryEnhancementsEnabled') || this.config.enabled
+ );
+ }
+
+ setUserQueryEnhancementsEnabled(enabled: boolean) {
+ if (!this.config.enabled) return;
+ this.storage.set('opensearchDashboards.userQueryEnhancementsEnabled', enabled);
+ if (!enabled) {
+ this.storage.remove('opensearchDashboards.userQueryLanguage');
+ this.storage.remove('opensearchDashboards.userQueryString');
+ }
+ this.enabledQueryEnhancementsUpdated$.next(enabled);
+ return true;
+ }
+
+ getAllQueryEnhancements() {
+ return this.queryEnhancements;
+ }
+
+ getQueryEnhancements(language: string) {
+ return this.queryEnhancements.get(language);
+ }
+
+ getUserQueryLanguageBlocklist() {
+ return this.storage.get('opensearchDashboards.userQueryLanguageBlocklist') || [];
+ }
+
+ setUserQueryLanguageBlocklist(languages: string[]) {
+ if (!this.config.enabled) return;
+ this.storage.set(
+ 'opensearchDashboards.userQueryLanguageBlocklist',
+ languages.map((language) => language.toLowerCase())
+ );
+ return true;
+ }
getUserQueryLanguage() {
return this.storage.get('opensearchDashboards.userQueryLanguage') || 'kuery';
@@ -84,10 +139,11 @@ export class Settings {
}
interface Deps {
+ config: ConfigSchema['enhancements'];
storage: IStorageWrapper;
queryEnhancements: Map;
}
-export function createSettings({ storage, queryEnhancements }: Deps) {
- return new Settings(storage, queryEnhancements);
+export function createSettings({ config, storage, queryEnhancements }: Deps) {
+ return new Settings(config, storage, queryEnhancements);
}
diff --git a/src/plugins/data/public/ui/types.ts b/src/plugins/data/public/ui/types.ts
index 464e6a97afde..d570a31f2ceb 100644
--- a/src/plugins/data/public/ui/types.ts
+++ b/src/plugins/data/public/ui/types.ts
@@ -3,6 +3,7 @@
* SPDX-License-Identifier: Apache-2.0
*/
+import { BehaviorSubject } from 'rxjs';
import { SearchInterceptor } from '../search';
import { IndexPatternSelectProps } from './index_pattern_select';
import { StatefulSearchBarProps } from './search_bar';
@@ -57,9 +58,9 @@ export interface IUiSetup {
* Data plugin prewired UI components
*/
export interface IUiStart {
- isEnhancementsEnabled: boolean;
- queryEnhancements: Map;
IndexPatternSelect: React.ComponentType;
SearchBar: React.ComponentType;
Settings: Settings;
+ containerRef: HTMLDivElement | null;
+ container$: BehaviorSubject;
}
diff --git a/src/plugins/data/public/ui/ui_service.ts b/src/plugins/data/public/ui/ui_service.ts
index 1ef834b54564..f0d398400e19 100644
--- a/src/plugins/data/public/ui/ui_service.ts
+++ b/src/plugins/data/public/ui/ui_service.ts
@@ -4,6 +4,7 @@
*/
import { Plugin, CoreSetup, CoreStart, PluginInitializerContext } from 'src/core/public';
+import { BehaviorSubject } from 'rxjs';
import { IUiStart, IUiSetup, QueryEnhancement, UiEnhancements } from './types';
import { ConfigSchema } from '../../config';
@@ -26,6 +27,8 @@ export interface UiServiceStartDependencies {
export class UiService implements Plugin {
enhancementsConfig: ConfigSchema['enhancements'];
private queryEnhancements: Map = new Map();
+ private containerRef: HTMLDivElement | null = null;
+ private container$ = new BehaviorSubject(null);
constructor(initializerContext: PluginInitializerContext) {
const { enhancements } = initializerContext.config.get();
@@ -46,23 +49,31 @@ export class UiService implements Plugin {
}
public start(core: CoreStart, { dataServices, storage }: UiServiceStartDependencies): IUiStart {
- const Settings = createSettings({ storage, queryEnhancements: this.queryEnhancements });
+ const Settings = createSettings({
+ config: this.enhancementsConfig,
+ storage,
+ queryEnhancements: this.queryEnhancements,
+ });
+
+ const setContainerRef = (ref: HTMLDivElement | null) => {
+ this.containerRef = ref;
+ this.container$.next(ref);
+ };
const SearchBar = createSearchBar({
core,
data: dataServices,
storage,
- isEnhancementsEnabled: this.enhancementsConfig?.enabled,
- queryEnhancements: this.queryEnhancements,
settings: Settings,
+ setContainerRef,
});
return {
- isEnhancementsEnabled: this.enhancementsConfig?.enabled,
- queryEnhancements: this.queryEnhancements,
IndexPatternSelect: createIndexPatternSelect(core.savedObjects.client),
SearchBar,
Settings,
+ containerRef: this.containerRef,
+ container$: this.container$,
};
}
diff --git a/src/plugins/data/server/ui_settings.ts b/src/plugins/data/server/ui_settings.ts
index e12113b87b29..ca24151e681d 100644
--- a/src/plugins/data/server/ui_settings.ts
+++ b/src/plugins/data/server/ui_settings.ts
@@ -705,8 +705,22 @@ export function getUiSettings(): Record> {
}),
schema: schema.boolean(),
},
- [UI_SETTINGS.DATAFRAME_HYDRATION_STRATEGY]: {
- name: i18n.translate('data.advancedSettings.dataFrameHydrationStrategyTitle', {
+ [UI_SETTINGS.QUERY_ENHANCEMENTS_ENABLED]: {
+ name: i18n.translate('data.advancedSettings.query.enhancements.enableTitle', {
+ defaultMessage: 'Enable query enhancements',
+ }),
+ value: false,
+ description: i18n.translate('data.advancedSettings.query.enhancements.enableText', {
+ defaultMessage: `
+ Experimental:
+ Allows users to query data using enhancements where available. If disabled,
+ only querying and querying languages that are considered production-ready are available to the user.`,
+ }),
+ category: ['search'],
+ schema: schema.boolean(),
+ },
+ [UI_SETTINGS.QUERY_DATAFRAME_HYDRATION_STRATEGY]: {
+ name: i18n.translate('data.advancedSettings.query.dataFrameHydrationStrategyTitle', {
defaultMessage: 'Data frame hydration strategy',
}),
value: 'perSource',
@@ -724,7 +738,8 @@ export function getUiSettings(): Record> {
Not Implemented.
{advanced}: hydrates the schema in intervals. If the schema hasn't changed the interval increases.
If the schema has changed the interval resets. Not Implemented.
- `,
+
+ Experimental: Requires query enhancements enabled.`,
values: {
perSource: dataFrameHydrationStrategyOptionLabels.perSource,
perQuery: dataFrameHydrationStrategyOptionLabels.perQuery,
@@ -736,16 +751,28 @@ export function getUiSettings(): Record> {
schema: schema.string(),
},
[UI_SETTINGS.QUERY_DATA_SOURCE_READONLY]: {
- name: i18n.translate('data.advancedSettings.query.dataSourceReadOnlyTitle', {
- defaultMessage: 'Read-only data source in query bar',
+ name: i18n.translate('data.advancedSettings.query.dataSource.readOnlyTitle', {
+ defaultMessage: 'Read-only data source in query editor',
}),
value: true,
- description: i18n.translate('data.advancedSettings.query.dataSourceReadOnlyText', {
+ description: i18n.translate('data.advancedSettings.query.dataSource.readOnlyText', {
defaultMessage:
- 'When enabled, the global search bar prevents modifying the data source in the query input. ' +
- '
Experimental: Setting to false enables modifying the data source.',
+ 'When enabled, the search bar prevents modifying the data source in the query input. ' +
+ 'Experimental: Requires query enhancements enabled.',
}),
+ category: ['search'],
schema: schema.boolean(),
},
+ [UI_SETTINGS.SEARCH_QUERY_LANGUAGE_BLOCKLIST]: {
+ name: i18n.translate('data.advancedSettings.searchQueryLanguageBlocklistTitle', {
+ defaultMessage: 'Additional query languages blocklist',
+ }),
+ value: ['none'],
+ description: i18n.translate('data.advancedSettings.searchQueryLanguageBlocklistText', {
+ defaultMessage: `Additional languages that are blocked from being used in the query editor.
+ Note: DQL and Lucene will not be blocked even if set.`,
+ }),
+ schema: schema.arrayOf(schema.string()),
+ },
};
}
diff --git a/src/plugins/data_explorer/public/components/sidebar/index.test.tsx b/src/plugins/data_explorer/public/components/sidebar/index.test.tsx
index 540321be55bf..6f090b6de188 100644
--- a/src/plugins/data_explorer/public/components/sidebar/index.test.tsx
+++ b/src/plugins/data_explorer/public/components/sidebar/index.test.tsx
@@ -47,6 +47,14 @@ jest.mock('../../../../opensearch_dashboards_react/public', () => {
),
},
},
+ ui: {
+ Settings: {
+ getEnabledQueryEnhancementsUpdated$: jest
+ .fn()
+ .mockImplementation(() => createObservable(false)),
+ },
+ container$: jest.fn().mockImplementation(() => createObservable(null)),
+ },
},
notifications: {
toasts: {
diff --git a/src/plugins/data_explorer/public/components/sidebar/index.tsx b/src/plugins/data_explorer/public/components/sidebar/index.tsx
index b65bd43950cd..0773d9c59c15 100644
--- a/src/plugins/data_explorer/public/components/sidebar/index.tsx
+++ b/src/plugins/data_explorer/public/components/sidebar/index.tsx
@@ -3,8 +3,8 @@
* SPDX-License-Identifier: Apache-2.0
*/
-import React, { FC, useCallback, useEffect, useState } from 'react';
-import { EuiPageSideBar, EuiSplitPanel } from '@elastic/eui';
+import React, { FC, useCallback, useEffect, useRef, useState } from 'react';
+import { EuiPageSideBar, EuiPortal, EuiSplitPanel } from '@elastic/eui';
import { i18n } from '@osd/i18n';
import { DataSource, DataSourceGroup, DataSourceSelectable } from '../../../../data/public';
import { DataSourceOption } from '../../../../data/public/';
@@ -19,15 +19,47 @@ export const Sidebar: FC = ({ children }) => {
const [selectedSources, setSelectedSources] = useState([]);
const [dataSourceOptionList, setDataSourceOptionList] = useState([]);
const [activeDataSources, setActiveDataSources] = useState([]);
+ const [isEnhancementsEnabled, setIsEnhancementsEnabled] = useState(false);
+ const containerRef = useRef(null);
const {
services: {
- data: { indexPatterns, dataSources },
+ data: { indexPatterns, dataSources, ui },
notifications: { toasts },
application,
},
} = useOpenSearchDashboards();
+ useEffect(() => {
+ const subscriptions = ui.Settings.getEnabledQueryEnhancementsUpdated$().subscribe(
+ (enabledQueryEnhancements) => {
+ setIsEnhancementsEnabled(enabledQueryEnhancements);
+ }
+ );
+
+ return () => {
+ subscriptions.unsubscribe();
+ };
+ }, [ui.Settings]);
+
+ const setContainerRef = useCallback((uiContainerRef) => {
+ uiContainerRef.appendChild(containerRef.current);
+ }, []);
+
+ useEffect(() => {
+ if (!isEnhancementsEnabled) return;
+ const subscriptions = ui.container$.subscribe((container) => {
+ if (container === null) return;
+ if (containerRef.current) {
+ setContainerRef(container);
+ }
+ });
+
+ return () => {
+ subscriptions.unsubscribe();
+ };
+ }, [ui.container$, containerRef, setContainerRef, ui.containerRef, isEnhancementsEnabled]);
+
useEffect(() => {
let isMounted = true;
const subscription = dataSources.dataSourceService
@@ -102,6 +134,19 @@ export const Sidebar: FC = ({ children }) => {
dataSources.dataSourceService.reload();
}, [dataSources.dataSourceService]);
+ const dataSourceSelector = (
+
+ );
+
return (
{
borderRadius="none"
color="transparent"
>
-
-
-
+ {isEnhancementsEnabled && (
+ {
+ containerRef.current = node;
+ }}
+ >
+ {dataSourceSelector}
+
+ )}
+ {!isEnhancementsEnabled && (
+
+ {dataSourceSelector}
+
+ )}
{children}
diff --git a/src/plugins/opensearch_dashboards_react/public/code_editor/editor_theme.ts b/src/plugins/opensearch_dashboards_react/public/code_editor/editor_theme.ts
index c5b76c8bf206..56821d45d214 100644
--- a/src/plugins/opensearch_dashboards_react/public/code_editor/editor_theme.ts
+++ b/src/plugins/opensearch_dashboards_react/public/code_editor/editor_theme.ts
@@ -28,6 +28,8 @@
* under the License.
*/
+import Color from 'color';
+
import { monaco } from '@osd/monaco';
import darkTheme from '@elastic/eui/dist/eui_theme_dark.json';
@@ -35,6 +37,7 @@ import lightTheme from '@elastic/eui/dist/eui_theme_light.json';
// NOTE: For talk around where this theme information will ultimately live,
// please see this discuss issue: https://github.com/elastic/kibana/issues/43814
+const standardizeColor = (color: string) => new Color(color).hex().toLowerCase();
export function createTheme(
euiTheme: typeof darkTheme | typeof lightTheme,
@@ -46,78 +49,78 @@ export function createTheme(
rules: [
{
token: '',
- foreground: euiTheme.euiColorDarkestShade,
- background: euiTheme.euiColorEmptyShade,
+ foreground: standardizeColor(euiTheme.euiColorDarkestShade),
+ background: standardizeColor(euiTheme.euiColorEmptyShade),
},
- { token: 'invalid', foreground: euiTheme.euiColorAccent },
+ { token: 'invalid', foreground: standardizeColor(euiTheme.euiColorAccent) },
{ token: 'emphasis', fontStyle: 'italic' },
{ token: 'strong', fontStyle: 'bold' },
- { token: 'variable', foreground: euiTheme.euiColorPrimary },
- { token: 'variable.predefined', foreground: euiTheme.euiColorSecondary },
- { token: 'constant', foreground: euiTheme.euiColorAccent },
- { token: 'comment', foreground: euiTheme.euiColorMediumShade },
- { token: 'number', foreground: euiTheme.euiColorAccent },
- { token: 'number.hex', foreground: euiTheme.euiColorAccent },
- { token: 'regexp', foreground: euiTheme.euiColorDanger },
- { token: 'annotation', foreground: euiTheme.euiColorMediumShade },
- { token: 'type', foreground: euiTheme.euiColorVis0 },
-
- { token: 'delimiter', foreground: euiTheme.euiColorDarkestShade },
- { token: 'delimiter.html', foreground: euiTheme.euiColorDarkShade },
- { token: 'delimiter.xml', foreground: euiTheme.euiColorPrimary },
-
- { token: 'tag', foreground: euiTheme.euiColorDanger },
- { token: 'tag.id.jade', foreground: euiTheme.euiColorPrimary },
- { token: 'tag.class.jade', foreground: euiTheme.euiColorPrimary },
- { token: 'meta.scss', foreground: euiTheme.euiColorAccent },
- { token: 'metatag', foreground: euiTheme.euiColorSecondary },
- { token: 'metatag.content.html', foreground: euiTheme.euiColorDanger },
- { token: 'metatag.html', foreground: euiTheme.euiColorMediumShade },
- { token: 'metatag.xml', foreground: euiTheme.euiColorMediumShade },
+ { token: 'variable', foreground: standardizeColor(euiTheme.euiColorPrimary) },
+ { token: 'variable.predefined', foreground: standardizeColor(euiTheme.euiColorSecondary) },
+ { token: 'constant', foreground: standardizeColor(euiTheme.euiColorAccent) },
+ { token: 'comment', foreground: standardizeColor(euiTheme.euiColorMediumShade) },
+ { token: 'number', foreground: standardizeColor(euiTheme.euiColorAccent) },
+ { token: 'number.hex', foreground: standardizeColor(euiTheme.euiColorAccent) },
+ { token: 'regexp', foreground: standardizeColor(euiTheme.euiColorDanger) },
+ { token: 'annotation', foreground: standardizeColor(euiTheme.euiColorMediumShade) },
+ { token: 'type', foreground: standardizeColor(euiTheme.euiColorVis0) },
+
+ { token: 'delimiter', foreground: standardizeColor(euiTheme.euiColorDarkestShade) },
+ { token: 'delimiter.html', foreground: standardizeColor(euiTheme.euiColorDarkShade) },
+ { token: 'delimiter.xml', foreground: standardizeColor(euiTheme.euiColorPrimary) },
+
+ { token: 'tag', foreground: standardizeColor(euiTheme.euiColorDanger) },
+ { token: 'tag.id.jade', foreground: standardizeColor(euiTheme.euiColorPrimary) },
+ { token: 'tag.class.jade', foreground: standardizeColor(euiTheme.euiColorPrimary) },
+ { token: 'meta.scss', foreground: standardizeColor(euiTheme.euiColorAccent) },
+ { token: 'metatag', foreground: standardizeColor(euiTheme.euiColorSecondary) },
+ { token: 'metatag.content.html', foreground: standardizeColor(euiTheme.euiColorDanger) },
+ { token: 'metatag.html', foreground: standardizeColor(euiTheme.euiColorMediumShade) },
+ { token: 'metatag.xml', foreground: standardizeColor(euiTheme.euiColorMediumShade) },
{ token: 'metatag.php', fontStyle: 'bold' },
- { token: 'key', foreground: euiTheme.euiColorWarning },
- { token: 'string.key.json', foreground: euiTheme.euiColorDanger },
- { token: 'string.value.json', foreground: euiTheme.euiColorPrimary },
-
- { token: 'attribute.name', foreground: euiTheme.euiColorDanger },
- { token: 'attribute.name.css', foreground: euiTheme.euiColorSecondary },
- { token: 'attribute.value', foreground: euiTheme.euiColorPrimary },
- { token: 'attribute.value.number', foreground: euiTheme.euiColorWarning },
- { token: 'attribute.value.unit', foreground: euiTheme.euiColorWarning },
- { token: 'attribute.value.html', foreground: euiTheme.euiColorPrimary },
- { token: 'attribute.value.xml', foreground: euiTheme.euiColorPrimary },
-
- { token: 'string', foreground: euiTheme.euiColorDanger },
- { token: 'string.html', foreground: euiTheme.euiColorPrimary },
- { token: 'string.sql', foreground: euiTheme.euiColorDanger },
- { token: 'string.yaml', foreground: euiTheme.euiColorPrimary },
-
- { token: 'keyword', foreground: euiTheme.euiColorPrimary },
- { token: 'keyword.json', foreground: euiTheme.euiColorPrimary },
- { token: 'keyword.flow', foreground: euiTheme.euiColorWarning },
- { token: 'keyword.flow.scss', foreground: euiTheme.euiColorPrimary },
-
- { token: 'operator.scss', foreground: euiTheme.euiColorDarkShade },
- { token: 'operator.sql', foreground: euiTheme.euiColorMediumShade },
- { token: 'operator.swift', foreground: euiTheme.euiColorMediumShade },
- { token: 'predefined.sql', foreground: euiTheme.euiColorMediumShade },
+ { token: 'key', foreground: standardizeColor(euiTheme.euiColorWarning) },
+ { token: 'string.key.json', foreground: standardizeColor(euiTheme.euiColorDanger) },
+ { token: 'string.value.json', foreground: standardizeColor(euiTheme.euiColorPrimary) },
+
+ { token: 'attribute.name', foreground: standardizeColor(euiTheme.euiColorDanger) },
+ { token: 'attribute.name.css', foreground: standardizeColor(euiTheme.euiColorSecondary) },
+ { token: 'attribute.value', foreground: standardizeColor(euiTheme.euiColorPrimary) },
+ { token: 'attribute.value.number', foreground: standardizeColor(euiTheme.euiColorWarning) },
+ { token: 'attribute.value.unit', foreground: standardizeColor(euiTheme.euiColorWarning) },
+ { token: 'attribute.value.html', foreground: standardizeColor(euiTheme.euiColorPrimary) },
+ { token: 'attribute.value.xml', foreground: standardizeColor(euiTheme.euiColorPrimary) },
+
+ { token: 'string', foreground: standardizeColor(euiTheme.euiColorDanger) },
+ { token: 'string.html', foreground: standardizeColor(euiTheme.euiColorPrimary) },
+ { token: 'string.sql', foreground: standardizeColor(euiTheme.euiColorDanger) },
+ { token: 'string.yaml', foreground: standardizeColor(euiTheme.euiColorPrimary) },
+
+ { token: 'keyword', foreground: standardizeColor(euiTheme.euiColorPrimary) },
+ { token: 'keyword.json', foreground: standardizeColor(euiTheme.euiColorPrimary) },
+ { token: 'keyword.flow', foreground: standardizeColor(euiTheme.euiColorWarning) },
+ { token: 'keyword.flow.scss', foreground: standardizeColor(euiTheme.euiColorPrimary) },
+
+ { token: 'operator.scss', foreground: standardizeColor(euiTheme.euiColorDarkShade) },
+ { token: 'operator.sql', foreground: standardizeColor(euiTheme.euiColorMediumShade) },
+ { token: 'operator.swift', foreground: standardizeColor(euiTheme.euiColorMediumShade) },
+ { token: 'predefined.sql', foreground: standardizeColor(euiTheme.euiColorMediumShade) },
],
colors: {
- 'editor.foreground': euiTheme.euiColorDarkestShade,
- 'editor.background': euiTheme.euiColorEmptyShade,
- 'editorLineNumber.foreground': euiTheme.euiColorDarkShade,
- 'editorLineNumber.activeForeground': euiTheme.euiColorDarkShade,
- 'editorIndentGuide.background': euiTheme.euiColorLightShade,
- 'editor.selectionBackground': selectionBackgroundColor,
- 'editorWidget.border': euiTheme.euiColorLightShade,
- 'editorWidget.background': euiTheme.euiColorLightestShade,
- 'editorCursor.foreground': euiTheme.euiColorDarkestShade,
- 'editorSuggestWidget.selectedBackground': euiTheme.euiColorLightShade,
- 'list.hoverBackground': euiTheme.euiColorLightShade,
- 'list.highlightForeground': euiTheme.euiColorPrimary,
- 'editor.lineHighlightBorder': euiTheme.euiColorLightestShade,
+ 'editor.foreground': standardizeColor(euiTheme.euiColorDarkestShade),
+ 'editor.background': standardizeColor(euiTheme.euiColorEmptyShade),
+ 'editorLineNumber.foreground': standardizeColor(euiTheme.euiColorDarkShade),
+ 'editorLineNumber.activeForeground': standardizeColor(euiTheme.euiColorDarkShade),
+ 'editorIndentGuide.background': standardizeColor(euiTheme.euiColorLightShade),
+ 'editor.selectionBackground': standardizeColor(selectionBackgroundColor),
+ 'editorWidget.border': standardizeColor(euiTheme.euiColorLightShade),
+ 'editorWidget.background': standardizeColor(euiTheme.euiColorLightestShade),
+ 'editorCursor.foreground': standardizeColor(euiTheme.euiColorDarkestShade),
+ 'editorSuggestWidget.selectedBackground': standardizeColor(euiTheme.euiColorLightShade),
+ 'list.hoverBackground': standardizeColor(euiTheme.euiColorLightShade),
+ 'list.highlightForeground': standardizeColor(euiTheme.euiColorPrimary),
+ 'editor.lineHighlightBorder': standardizeColor(euiTheme.euiColorLightestShade),
},
};
}