-
Notifications
You must be signed in to change notification settings - Fork 890
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support injecting DataStructureMeta
from QueryEditorExtensions
for Query Assist
#7871
Changes from 4 commits
91c47c8
03dcec7
555c299
bd43fd0
a249227
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
feat: | ||
- Support injecting `DataStructureMeta` from `QueryEditorExtensions` for Query Assist ([#7871](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/7871)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* | ||
* 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 | ||
* 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 ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @ashwin-pc are we sure we don't want to do convert the dataset config to a class. I was thinking it would be similar to the the interceptor class: This way we don't have to ensure developers import this in their configs. @joshuali925 could implement this in the base class and then it would be available for classes that extend the base class There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agree. will think about it later, right now just trying to add instead of refactor.. |
||
dataStructures: DataStructure[], | ||
selectDataSourceId: (dataStructure: DataStructure) => string | undefined = ( | ||
dataStructure: DataStructure | ||
) => dataStructure.id | ||
) => { | ||
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?.(selectDataSourceId(dataStructure)) | ||
) | ||
).then((settledResults) => | ||
settledResults | ||
.filter( | ||
<T>(result: PromiseSettledResult<T>): result is PromiseFulfilledResult<T> => | ||
result.status === 'fulfilled' | ||
) | ||
.map((result) => result.value) | ||
); | ||
const meta = metaArray.reduce( | ||
(acc, curr) => (acc || curr ? ({ ...acc, ...curr } as DataStructureMeta) : undefined), | ||
undefined | ||
); | ||
if (meta || dataStructure.meta) { | ||
dataStructure.meta = { ...meta, ...dataStructure.meta } as DataStructureMeta; | ||
} | ||
return dataStructure; | ||
}) | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -95,7 +95,7 @@ export class QueryEnhancementsPlugin | |
queryString.getLanguageService().registerLanguage(sqlLanguageConfig); | ||
|
||
data.__enhance({ | ||
ui: { | ||
editor: { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. sorry about that. Thanks for fixing. |
||
queryEditorExtension: createQueryAssistExtension(core.http, data, this.config.queryAssist), | ||
}, | ||
}); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 { 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<string | undefined, string[]> = new Map(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice way to cache the data |
||
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<string | undefined, string[]>, | ||
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 @@ | |
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,38 +59,35 @@ | |
data: DataPublicPluginSetup, | ||
config: ConfigSchema['queryAssist'] | ||
): QueryEditorExtensionConfig => { | ||
const availableLanguagesByDataSource: Map<string | undefined, string[]> = 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.FEATURE, | ||
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 ( | ||
<QueryAssistWrapper | ||
availableLanguagesByDataSource={availableLanguagesByDataSource} | ||
dependencies={dependencies} | ||
http={http} | ||
data={data} | ||
> | ||
<QueryAssistWrapper dependencies={dependencies} http={http} data={data}> | ||
<QueryAssistBar dependencies={dependencies} /> | ||
</QueryAssistWrapper> | ||
); | ||
}, | ||
getBanner: (dependencies) => { | ||
// advertise query assist if user is not on a supported language. | ||
return ( | ||
<QueryAssistWrapper | ||
availableLanguagesByDataSource={availableLanguagesByDataSource} | ||
dependencies={dependencies} | ||
http={http} | ||
data={data} | ||
invert | ||
> | ||
<QueryAssistWrapper dependencies={dependencies} http={http} data={data} invert> | ||
<QueryAssistBanner | ||
dependencies={dependencies} | ||
languages={config.supportedLanguages.map((conf) => conf.language)} | ||
|
@@ -95,7 +99,6 @@ | |
}; | ||
|
||
interface QueryAssistWrapperProps { | ||
availableLanguagesByDataSource: Map<string | undefined, string[]>; | ||
dependencies: QueryEditorExtensionDependencies; | ||
http: HttpSetup; | ||
data: DataPublicPluginSetup; | ||
|
@@ -108,11 +111,7 @@ | |
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); | ||
}); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a blocker but can we add some tests for this function?