From 91c47c8fa6d52f854aca05fbf35882a229a19f00 Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Tue, 27 Aug 2024 23:41:33 +0000 Subject: [PATCH 1/4] fix query editor extensions enhance key Signed-off-by: Joshua Li --- docs/_sidebar.md | 1 + src/plugins/query_enhancements/public/plugin.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 792c35e3949b..b1b70d6abb01 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -147,6 +147,7 @@ - [Opensearch dashboards.release notes 1.3.14](../release-notes/opensearch-dashboards.release-notes-1.3.14.md) - [Opensearch dashboards.release notes 1.3.15](../release-notes/opensearch-dashboards.release-notes-1.3.15.md) - [Opensearch dashboards.release notes 1.3.17](../release-notes/opensearch-dashboards.release-notes-1.3.17.md) + - [Opensearch dashboards.release notes 1.3.19](../release-notes/opensearch-dashboards.release-notes-1.3.19.md) - [Opensearch dashboards.release notes 1.3.2](../release-notes/opensearch-dashboards.release-notes-1.3.2.md) - [Opensearch dashboards.release notes 1.3.3](../release-notes/opensearch-dashboards.release-notes-1.3.3.md) - [Opensearch dashboards.release notes 1.3.5](../release-notes/opensearch-dashboards.release-notes-1.3.5.md) diff --git a/src/plugins/query_enhancements/public/plugin.tsx b/src/plugins/query_enhancements/public/plugin.tsx index 62f5270fa1b1..ffbd7af326f7 100644 --- a/src/plugins/query_enhancements/public/plugin.tsx +++ b/src/plugins/query_enhancements/public/plugin.tsx @@ -95,7 +95,7 @@ export class QueryEnhancementsPlugin queryString.getLanguageService().registerLanguage(sqlLanguageConfig); data.__enhance({ - ui: { + editor: { queryEditorExtension: createQueryAssistExtension(core.http, data, this.config.queryAssist), }, }); From 03dcec741189bd525e2cd7cd504d7c0235d0d962 Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Tue, 27 Aug 2024 23:42:23 +0000 Subject: [PATCH 2/4] add query assist icons to clusters if available Signed-off-by: Joshua Li --- .../dataset_service/lib/index_pattern_type.ts | 5 +- .../dataset_service/lib/index_type.ts | 6 +- .../query_string/dataset_service/lib/utils.ts | 45 +++++++++++ .../query_editor_extension.tsx | 7 ++ .../query_assist/utils/create_extension.tsx | 79 +++++++++---------- 5 files changed, 99 insertions(+), 43 deletions(-) create mode 100644 src/plugins/data/public/query/query_string/dataset_service/lib/utils.ts diff --git a/src/plugins/data/public/query/query_string/dataset_service/lib/index_pattern_type.ts b/src/plugins/data/public/query/query_string/dataset_service/lib/index_pattern_type.ts index 71710adc7b50..f346d1cb5ff7 100644 --- a/src/plugins/data/public/query/query_string/dataset_service/lib/index_pattern_type.ts +++ b/src/plugins/data/public/query/query_string/dataset_service/lib/index_pattern_type.ts @@ -15,6 +15,7 @@ import { } from '../../../../../common'; import { DatasetTypeConfig } from '../types'; import { getIndexPatterns } from '../../../../services'; +import { injectMetaToDataStructures } from './utils'; export const indexPatternTypeConfig: DatasetTypeConfig = { id: DEFAULT_DATA.SET_TYPES.INDEX_PATTERN, @@ -97,7 +98,7 @@ const fetchIndexPatterns = async (client: SavedObjectsClientContract): Promise { const dataSourceId = savedObject.references.find((ref) => ref.type === 'data-source')?.id; const dataSource = dataSourceId ? dataSourceMap[dataSourceId] : undefined; @@ -122,4 +123,6 @@ const fetchIndexPatterns = async (client: SavedObjectsClientContract): Promise { type: 'data-source', perPage: 10000, }); - const dataSources: DataStructure[] = [INDEX_INFO.LOCAL_DATASOURCE]; - return dataSources.concat( + const dataSources: DataStructure[] = [INDEX_INFO.LOCAL_DATASOURCE].concat( resp.savedObjects.map((savedObject) => ({ id: savedObject.id, title: savedObject.attributes.title, type: 'DATA_SOURCE', })) ); + + return injectMetaToDataStructures(dataSources); }; const fetchIndices = async (dataStructure: DataStructure): Promise => { diff --git a/src/plugins/data/public/query/query_string/dataset_service/lib/utils.ts b/src/plugins/data/public/query/query_string/dataset_service/lib/utils.ts new file mode 100644 index 000000000000..867b00befaec --- /dev/null +++ b/src/plugins/data/public/query/query_string/dataset_service/lib/utils.ts @@ -0,0 +1,45 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { DataStructure, DataStructureMeta } from '../../../../../common'; +import { getQueryService } from '../../../../services'; + +/** + * Inject {@link DataStructureMeta} to DataStructures based on + * {@link QueryEditorExtensions}. + * + * This function combines the meta fields from QueryEditorExtensions and in + * provided data structures. Lower extension order is higher priority, and + * existing meta fields have highest priority. + * + * @param dataStructures - {@link DataStructure} + * @returns data structures with meta + */ +export const injectMetaToDataStructures = async (dataStructures: DataStructure[]) => { + const queryEditorExtensions = Object.values( + getQueryService().queryString.getLanguageService().getQueryEditorExtensionMap() + ); + queryEditorExtensions.sort((a, b) => b.order - a.order); + + return Promise.all( + dataStructures.map(async (dataStructure) => { + const metaArray = await Promise.allSettled( + queryEditorExtensions.map((curr) => curr.getDataStructureMeta?.(dataStructure.id)) + ).then((settledResults) => + settledResults + .filter( + (result: PromiseSettledResult): result is PromiseFulfilledResult => + result.status === 'fulfilled' + ) + .map((result) => result.value) + ); + const meta = metaArray.reduce( + (acc, curr) => (acc || curr ? ({ ...acc, ...curr } as DataStructureMeta) : undefined), + undefined + ); + return { meta, ...dataStructure }; + }) + ); +}; diff --git a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extension.tsx b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extension.tsx index 86d904d2b2b8..be74558fae70 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extension.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_extensions/query_editor_extension.tsx @@ -7,6 +7,7 @@ import { EuiErrorBoundary } from '@elastic/eui'; import React, { useEffect, useMemo, useRef, useState } from 'react'; import ReactDOM from 'react-dom'; import { Observable } from 'rxjs'; +import { DataStructureMeta } from '../../../../common'; interface QueryEditorExtensionProps { config: QueryEditorExtensionConfig; @@ -48,6 +49,12 @@ export interface QueryEditorExtensionConfig { * @returns whether the extension is enabled. */ isEnabled$: (dependencies: QueryEditorExtensionDependencies) => Observable; + /** + * @returns DataStructureMeta for a given data source id. + */ + getDataStructureMeta?: ( + dataSourceId: string | undefined + ) => Promise; /** * A function that returns the query editor extension component. The component * will be displayed on top of the query editor in the search bar. diff --git a/src/plugins/query_enhancements/public/query_assist/utils/create_extension.tsx b/src/plugins/query_enhancements/public/query_assist/utils/create_extension.tsx index f7614547c127..ec8a219655c2 100644 --- a/src/plugins/query_enhancements/public/query_assist/utils/create_extension.tsx +++ b/src/plugins/query_enhancements/public/query_assist/utils/create_extension.tsx @@ -6,7 +6,7 @@ import { HttpSetup } from 'opensearch-dashboards/public'; import React, { useEffect, useState } from 'react'; import { distinctUntilChanged, map, startWith, switchMap } from 'rxjs/operators'; -import { DEFAULT_DATA } from '../../../../data/common'; +import { DATA_STRUCTURE_META_TYPES, DEFAULT_DATA } from '../../../../data/common'; import { DataPublicPluginSetup, QueryEditorExtensionConfig, @@ -15,16 +15,32 @@ import { import { API } from '../../../common'; import { ConfigSchema } from '../../../common/config'; import { QueryAssistBanner, QueryAssistBar } from '../components'; +import assistantMark from '../../assets/query_assist_mark.svg'; + +/** + * @returns list of query assist supported languages for the given data source. + */ +const getAvailableLanguagesForDataSource = (() => { + const availableLanguagesByDataSource: Map = new Map(); + return async (http: HttpSetup, dataSourceId: string | undefined) => { + const cached = availableLanguagesByDataSource.get(dataSourceId); + if (cached !== undefined) return cached; + const languages = await http + .get<{ configuredLanguages: string[] }>(API.QUERY_ASSIST.LANGUAGES, { + query: { dataSourceId }, + }) + .then((response) => response.configuredLanguages) + .catch(() => []); + availableLanguagesByDataSource.set(dataSourceId, languages); + return languages; + }; +})(); /** * @returns observable list of query assist agent configured languages in the * selected data source. */ -const getAvailableLanguages$ = ( - availableLanguagesByDataSource: Map, - http: HttpSetup, - data: DataPublicPluginSetup -) => +const getAvailableLanguages$ = (http: HttpSetup, data: DataPublicPluginSetup) => data.query.queryString.getUpdates$().pipe( startWith(data.query.queryString.getQuery()), distinctUntilChanged(), @@ -34,16 +50,7 @@ const getAvailableLanguages$ = ( if (query.dataset?.dataSource?.type !== DEFAULT_DATA.SOURCE_TYPES.OPENSEARCH) return []; const dataSourceId = query.dataset?.dataSource?.id; - const cached = availableLanguagesByDataSource.get(dataSourceId); - if (cached !== undefined) return cached; - const languages = await http - .get<{ configuredLanguages: string[] }>(API.QUERY_ASSIST.LANGUAGES, { - query: { dataSourceId }, - }) - .then((response) => response.configuredLanguages) - .catch(() => []); - availableLanguagesByDataSource.set(dataSourceId, languages); - return languages; + return getAvailableLanguagesForDataSource(http, dataSourceId); }) ); @@ -52,24 +59,27 @@ export const createQueryAssistExtension = ( data: DataPublicPluginSetup, config: ConfigSchema['queryAssist'] ): QueryEditorExtensionConfig => { - const availableLanguagesByDataSource: Map = new Map(); - return { id: 'query-assist', order: 1000, + getDataStructureMeta: async (dataSourceId) => { + const isEnabled = await getAvailableLanguagesForDataSource(http, dataSourceId).then( + (languages) => languages.length > 0 + ); + if (isEnabled) { + return { + type: DATA_STRUCTURE_META_TYPES.TYPE, + icon: { type: assistantMark }, + tooltip: 'Query assist is available', + }; + } + }, isEnabled$: () => - getAvailableLanguages$(availableLanguagesByDataSource, http, data).pipe( - map((languages) => languages.length > 0) - ), + getAvailableLanguages$(http, data).pipe(map((languages) => languages.length > 0)), getComponent: (dependencies) => { // only show the component if user is on a supported language. return ( - + ); @@ -77,13 +87,7 @@ export const createQueryAssistExtension = ( getBanner: (dependencies) => { // advertise query assist if user is not on a supported language. return ( - + conf.language)} @@ -95,7 +99,6 @@ export const createQueryAssistExtension = ( }; interface QueryAssistWrapperProps { - availableLanguagesByDataSource: Map; dependencies: QueryEditorExtensionDependencies; http: HttpSetup; data: DataPublicPluginSetup; @@ -108,11 +111,7 @@ const QueryAssistWrapper: React.FC = (props) => { useEffect(() => { let mounted = true; - const subscription = getAvailableLanguages$( - props.availableLanguagesByDataSource, - props.http, - props.data - ).subscribe((languages) => { + const subscription = getAvailableLanguages$(props.http, props.data).subscribe((languages) => { const available = languages.includes(props.dependencies.language); if (mounted) setVisible(props.invert ? !available : available); }); From 555c2992301d2271e14cb07dda8fe9b9daa89cd2 Mon Sep 17 00:00:00 2001 From: Joshua Li Date: Wed, 28 Aug 2024 01:05:40 +0000 Subject: [PATCH 3/4] allow custom icon for `DATA_STRUCTURE_META_TYPES.FEATURE` Signed-off-by: Joshua Li --- src/plugins/data/common/datasets/types.ts | 4 +++- .../dataset_service/lib/index_pattern_type.ts | 2 +- .../query_string/dataset_service/lib/utils.ts | 19 ++++++++++++++---- .../ui/dataset_selector/advanced_selector.tsx | 8 ++++---- .../ui/dataset_selector/dataset_explorer.tsx | 20 +++++++++---------- .../query_assist/utils/create_extension.tsx | 2 +- 6 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/plugins/data/common/datasets/types.ts b/src/plugins/data/common/datasets/types.ts index 69549a631ab9..c98d277b7c77 100644 --- a/src/plugins/data/common/datasets/types.ts +++ b/src/plugins/data/common/datasets/types.ts @@ -139,7 +139,7 @@ export enum DATA_STRUCTURE_META_TYPES { */ export interface DataStructureFeatureMeta { type: DATA_STRUCTURE_META_TYPES.FEATURE; - icon?: string; + icon?: EuiIconProps; tooltip?: string; } @@ -157,6 +157,8 @@ export interface DataStructureDataTypeMeta { */ export interface DataStructureCustomMeta { type: DATA_STRUCTURE_META_TYPES.CUSTOM; + icon?: EuiIconProps; + tooltip?: string; [key: string]: any; } diff --git a/src/plugins/data/public/query/query_string/dataset_service/lib/index_pattern_type.ts b/src/plugins/data/public/query/query_string/dataset_service/lib/index_pattern_type.ts index f346d1cb5ff7..e549a5abdf3d 100644 --- a/src/plugins/data/public/query/query_string/dataset_service/lib/index_pattern_type.ts +++ b/src/plugins/data/public/query/query_string/dataset_service/lib/index_pattern_type.ts @@ -124,5 +124,5 @@ const fetchIndexPatterns = async (client: SavedObjectsClientContract): Promise dataStructure.parent?.id); }; diff --git a/src/plugins/data/public/query/query_string/dataset_service/lib/utils.ts b/src/plugins/data/public/query/query_string/dataset_service/lib/utils.ts index 867b00befaec..aada2c2421cf 100644 --- a/src/plugins/data/public/query/query_string/dataset_service/lib/utils.ts +++ b/src/plugins/data/public/query/query_string/dataset_service/lib/utils.ts @@ -10,14 +10,20 @@ import { getQueryService } from '../../../../services'; * Inject {@link DataStructureMeta} to DataStructures based on * {@link QueryEditorExtensions}. * - * This function combines the meta fields from QueryEditorExtensions and in + * This function combines the meta fields from QueryEditorExtensions and * provided data structures. Lower extension order is higher priority, and * existing meta fields have highest priority. * * @param dataStructures - {@link DataStructure} + * @param selectDataSourceId - function to get data source id given a data structure * @returns data structures with meta */ -export const injectMetaToDataStructures = async (dataStructures: DataStructure[]) => { +export const injectMetaToDataStructures = async ( + dataStructures: DataStructure[], + selectDataSourceId: (dataStructure: DataStructure) => string | undefined = ( + dataStructure: DataStructure + ) => dataStructure.id +) => { const queryEditorExtensions = Object.values( getQueryService().queryString.getLanguageService().getQueryEditorExtensionMap() ); @@ -26,7 +32,9 @@ export const injectMetaToDataStructures = async (dataStructures: DataStructure[] return Promise.all( dataStructures.map(async (dataStructure) => { const metaArray = await Promise.allSettled( - queryEditorExtensions.map((curr) => curr.getDataStructureMeta?.(dataStructure.id)) + queryEditorExtensions.map((curr) => + curr.getDataStructureMeta?.(selectDataSourceId(dataStructure)) + ) ).then((settledResults) => settledResults .filter( @@ -39,7 +47,10 @@ export const injectMetaToDataStructures = async (dataStructures: DataStructure[] (acc, curr) => (acc || curr ? ({ ...acc, ...curr } as DataStructureMeta) : undefined), undefined ); - return { meta, ...dataStructure }; + if (meta || dataStructure.meta) { + dataStructure.meta = { ...meta, ...dataStructure.meta } as DataStructureMeta; + } + return dataStructure; }) ); }; diff --git a/src/plugins/data/public/ui/dataset_selector/advanced_selector.tsx b/src/plugins/data/public/ui/dataset_selector/advanced_selector.tsx index a319c9a376bd..8afaedbb492e 100644 --- a/src/plugins/data/public/ui/dataset_selector/advanced_selector.tsx +++ b/src/plugins/data/public/ui/dataset_selector/advanced_selector.tsx @@ -37,11 +37,11 @@ export const AdvancedSelector = ({ .getTypes() .map((type) => { return { - id: type!.id, - title: type!.title, - type: type!.id, + id: type.id, + title: type.title, + type: type.id, meta: { - ...type!.meta, + ...type.meta, type: DATA_STRUCTURE_META_TYPES.TYPE, }, } as DataStructure; diff --git a/src/plugins/data/public/ui/dataset_selector/dataset_explorer.tsx b/src/plugins/data/public/ui/dataset_selector/dataset_explorer.tsx index dd35ce8065b2..48a0dcac84b2 100644 --- a/src/plugins/data/public/ui/dataset_selector/dataset_explorer.tsx +++ b/src/plugins/data/public/ui/dataset_selector/dataset_explorer.tsx @@ -190,26 +190,24 @@ const LoadingEmptyColumn = ({ isLoading }: { isLoading: boolean }) => ); const appendIcon = (item: DataStructure) => { - if (item.meta?.type === DATA_STRUCTURE_META_TYPES.FEATURE) { + if (item.meta?.type === DATA_STRUCTURE_META_TYPES.TYPE) { + return ( + + + + ); + } else { if (item.meta?.icon && item.meta?.tooltip) { return ( - + ); } else if (item.meta?.icon) { - return ; + return ; } } - if (item.meta?.type === DATA_STRUCTURE_META_TYPES.TYPE) { - return ( - - - - ); - } - return null; }; diff --git a/src/plugins/query_enhancements/public/query_assist/utils/create_extension.tsx b/src/plugins/query_enhancements/public/query_assist/utils/create_extension.tsx index ec8a219655c2..bebe250a91ce 100644 --- a/src/plugins/query_enhancements/public/query_assist/utils/create_extension.tsx +++ b/src/plugins/query_enhancements/public/query_assist/utils/create_extension.tsx @@ -68,7 +68,7 @@ export const createQueryAssistExtension = ( ); if (isEnabled) { return { - type: DATA_STRUCTURE_META_TYPES.TYPE, + type: DATA_STRUCTURE_META_TYPES.FEATURE, icon: { type: assistantMark }, tooltip: 'Query assist is available', }; From bd43fd01d6c093984d4b2c965250933366ab0b37 Mon Sep 17 00:00:00 2001 From: "opensearch-changeset-bot[bot]" <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com> Date: Wed, 28 Aug 2024 01:16:26 +0000 Subject: [PATCH 4/4] Changeset file for PR #7871 created/updated --- changelogs/fragments/7871.yml | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 changelogs/fragments/7871.yml diff --git a/changelogs/fragments/7871.yml b/changelogs/fragments/7871.yml new file mode 100644 index 000000000000..f25eb51445d1 --- /dev/null +++ b/changelogs/fragments/7871.yml @@ -0,0 +1,2 @@ +feat: +- Support injecting `DataStructureMeta` from `QueryEditorExtensions` for Query Assist ([#7871](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7871)) \ No newline at end of file