diff --git a/src/plugins/data/public/query/query_service.ts b/src/plugins/data/public/query/query_service.ts index 1da535f3f9e..7264b2305ca 100644 --- a/src/plugins/data/public/query/query_service.ts +++ b/src/plugins/data/public/query/query_service.ts @@ -88,7 +88,7 @@ export class QueryService { uiSettings, indexPatterns, }: QueryServiceStartDependencies): IQueryStart { - this.queryStringManager.initDataset(); + this.queryStringManager.getDatasetService().init(); return { addToQueryLog: createAddToQueryLog({ storage, diff --git a/src/plugins/data/public/query/query_string/dataset_manager/dataset_manager.mock.ts b/src/plugins/data/public/query/query_string/dataset_manager/dataset_manager.mock.ts deleted file mode 100644 index bb522f4ad8e..00000000000 --- a/src/plugins/data/public/query/query_string/dataset_manager/dataset_manager.mock.ts +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -// import { DatasetContract } from '.'; -// import { Dataset, DataStructure, CachedDataStructure } from '../../../../common'; -// import { DatasetTypeConfig } from './types'; - -// const createSetupContractMock = () => { -// const datasetManagerMock: jest.Mocked = { -// init: jest.fn(), -// getDataset: jest.fn(), -// setDataset: jest.fn(), -// getUpdates$: jest.fn(), -// getDefaultDataset: jest.fn(), -// fetchDefaultDataset: jest.fn(), -// init: jest.fn(), -// registerDatasetHandler: jest.fn(), -// fetchOptions: jest.fn(), -// getCachedDataStructure: jest.fn(), -// clearDataStructureCache: jest.fn(), -// }; -// return datasetManagerMock; -// }; - -// export const datasetManagerMock = { -// createSetupContract: createSetupContractMock, -// createStartContract: createSetupContractMock, -// }; - -// // Additional mock for DatasetHandlerConfig -// export const createDatasetHandlerConfigMock = (): jest.Mocked => ({ -// toDataset: jest.fn(), -// toDataStructure: jest.fn(), -// fetchOptions: jest.fn(), -// isLeaf: jest.fn(), -// }); - -// // Mock for Dataset -// export const createDatasetMock = (): jest.Mocked => ({ -// id: 'mock-dataset-id', -// title: 'Mock Dataset', -// type: 'mock-type', -// dataSource: { -// id: 'mock-datasource-id', -// title: 'Mock DataSource', -// type: 'mock-datasource-type', -// }, -// }); - -// // Mock for DataStructure -// export const createDataStructureMock = (): jest.Mocked => ({ -// id: 'mock-datastructure-id', -// title: 'Mock DataStructure', -// type: 'mock-type', -// parent: undefined, -// children: [], -// }); - -// // Mock for CachedDataStructure -// export const createCachedDataStructureMock = (): jest.Mocked => ({ -// id: 'mock-cached-datastructure-id', -// title: 'Mock Cached DataStructure', -// type: 'mock-type', -// parent: 'mock-parent-id', -// children: ['mock-child-id-1', 'mock-child-id-2'], -// }); diff --git a/src/plugins/data/public/query/query_string/dataset_manager/dataset_manager.test.ts b/src/plugins/data/public/query/query_string/dataset_manager/dataset_manager.test.ts deleted file mode 100644 index 287f733fcc5..00000000000 --- a/src/plugins/data/public/query/query_string/dataset_manager/dataset_manager.test.ts +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -// import { DatasetManager } from './dataset_manager'; -// import { coreMock } from '../../../../../../core/public/mocks'; -// import { Dataset } from '../../../../common'; - -// describe('DatasetManager', () => { -// let service: DatasetManager; - -// beforeEach(() => { -// const uiSettingsMock = coreMock.createSetup().uiSettings; -// uiSettingsMock.get.mockReturnValue(true); -// service = new DatasetManager(uiSettingsMock); -// }); - -// test('getUpdates$ is a cold emits only after dataset changes', () => { -// const obs$ = service.getUpdates$(); -// const emittedValues: Dataset[] = []; -// obs$.subscribe((v) => { -// emittedValues.push(v!); -// }); -// expect(emittedValues).toHaveLength(0); -// expect(emittedValues[0]).toEqual(undefined); - -// const newDataset: Dataset = { -// id: 'test_dataset', -// title: 'Test Dataset', -// type: 'INDEX_PATTERN', -// }; -// service.setDataset(newDataset); -// expect(emittedValues).toHaveLength(1); -// expect(emittedValues[0]).toEqual(newDataset); - -// service.setDataset({ ...newDataset }); -// expect(emittedValues).toHaveLength(2); -// }); -// }); diff --git a/src/plugins/data/public/query/query_string/dataset_manager/dataset_manager.ts b/src/plugins/data/public/query/query_string/dataset_manager/dataset_manager.ts deleted file mode 100644 index e8106a6fb64..00000000000 --- a/src/plugins/data/public/query/query_string/dataset_manager/dataset_manager.ts +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ -import { BehaviorSubject } from 'rxjs'; -import { CoreStart, SavedObjectsClientContract } from 'opensearch-dashboards/public'; -import { skip } from 'rxjs/operators'; -import { - Dataset, - DEFAULT_DATA, - DataStructure, - CachedDataStructure, - DataStructureCache, - createDataStructureCache, - IndexPatternSpec, -} from '../../../../common'; -import { IndexPatternsContract } from '../../../index_patterns'; -import { indexPatternHandlerConfig, indexHandlerConfig } from './lib'; -import { DatasetTypeConfig } from './types'; - -/** - * Manages datasets and their associated handlers. - */ -export class DatasetManager { - private dataset$: BehaviorSubject; - private indexPatterns?: IndexPatternsContract; - private defaultDataset?: Dataset; - private dataStructureCache: DataStructureCache; - private datasetHandlers: Map; - private dataStructuresMap: Map; - - /** - * Creates an instance of DatasetManager. - * @param uiSettings - The CoreStart's uiSettings. - * @param savedObjects - The SavedObjectsClientContract. - */ - constructor(private readonly uiSettings: CoreStart['uiSettings']) { - this.dataset$ = new BehaviorSubject(undefined); - this.dataStructureCache = createDataStructureCache(); - this.datasetHandlers = new Map(); - this.dataStructuresMap = new Map(); - this.registerDefaultHandlers(); - } - - /** - * Registers default handlers for index patterns and indices. - */ - private registerDefaultHandlers() { - this.registerDatasetHandler(indexPatternHandlerConfig); - this.registerDatasetHandler(indexHandlerConfig); - } - - /** - * Registers a dataset handler. - * @param handler - The dataset handler configuration. - */ - public registerDatasetHandler(handler: DatasetTypeConfig) { - this.datasetHandlers.set(handler.id, handler); - } - - /** - * Gets all registered dataset handlers. - * @returns An array of dataset handler configurations. - */ - public getDatasetHandlers(): DatasetTypeConfig[] { - return Array.from(this.datasetHandlers.values()); - } - - /** - * Gets a dataset handler by its ID. - * @param id - The ID of the dataset handler. - * @returns The dataset handler configuration, or undefined if not found. - */ - public getDatasetHandlerById(id: string): DatasetTypeConfig | undefined { - return this.datasetHandlers.get(id); - } - - /** - * Fetches options for a given data structure. - * @param dataStructure - The data structure to fetch options for. - * @returns A promise that resolves to a DatasetHandlerFetchResponse. - */ - public fetchOptions( - savedObjects: SavedObjectsClientContract, - path: DataStructure[] - ): Promise { - const dataStructure = path[path.length - 1]; - const handler = this.getHandlerForDataStructure(dataStructure); - return handler.fetch(savedObjects, path); - } - - /** - * Checks if a given data structure is a leaf node. - * @param dataStructure - The data structure to check. - * @returns True if the data structure is a leaf node, false otherwise. - */ - public isLeafDataStructure(dataStructure: DataStructure): boolean { - const handler = this.getHandlerForDataStructure(dataStructure); - return handler.id === dataStructure.type; - } - - /** - * Converts a data structure to a dataset. - * @param dataStructure - The data structure to convert. - * @returns A promise that resolves to a BaseDataset. - */ - public toDataset(path: DataStructure[]): Dataset { - const dataStructure = path[path.length - 1]; - const handler = this.getHandlerForDataStructure(dataStructure); - return handler.toDataset(path); - } - - /** - * Gets the appropriate handler for a given data structure. - * @param dataStructure - The data structure to get the handler for. - * @returns The dataset handler configuration. - * @throws Error if no handler is found. - */ - private getHandlerForDataStructure(dataStructure: DataStructure): DatasetTypeConfig { - const handler = - this.datasetHandlers.get(dataStructure.type) || - this.datasetHandlers.get(DEFAULT_DATA.SET_TYPES.INDEX_PATTERN); - if (!handler) { - throw new Error(`No handler found for type: ${dataStructure.type}`); - } - return handler; - } - - /** - * Sets the current dataset. - * @param dataset - The dataset to set. - */ - public async setDataset(dataset: Dataset | undefined) { - if (dataset) { - const spec = { - ...dataset, - dataSourceRef: dataset.dataSource - ? { - id: dataset.dataSource.id!, - name: dataset.dataSource.title, - type: dataset.dataSource.type, - } - : undefined, - } as IndexPatternSpec; - const temporaryIndexPattern = await this.indexPatterns!.create(spec, true); - this.indexPatterns?.saveToCache(dataset.id, temporaryIndexPattern); - } - this.dataset$.next(dataset); - } - - /** - * Gets the current dataset. - * @returns The current dataset, or undefined if not set. - */ - public getDataset(): Dataset | undefined { - return this.dataset$.getValue(); - } - - /** - * Gets an observable of dataset updates. - * @returns An observable of dataset updates. - */ - public getUpdates$() { - return this.dataset$.asObservable().pipe(skip(1)); - } - - /** - * Gets a cached data structure by ID. - * @param id - The ID of the data structure to retrieve. - * @returns The cached data structure, if found. - */ - public getCachedDataStructure(id: string): CachedDataStructure | undefined { - return this.dataStructureCache.get(id); - } - - /** - * Clears the data structure cache. - * @param id - The ID of the specific data structure to clear. If not provided, clears all. - */ - public clearDataStructureCache(id?: string) { - if (id) { - this.dataStructureCache.clear(id); - this.dataStructuresMap.delete(id); - } else { - this.dataStructureCache.clearAll(); - this.dataStructuresMap.clear(); - } - } - - /** - * Initializes the DatasetManager with index patterns. - * @param indexPatterns - The index patterns contract. - */ - public init = async (indexPatterns: IndexPatternsContract) => { - this.indexPatterns = indexPatterns; - this.defaultDataset = await this.fetchDefaultDataset(); - }; - - /** - * Gets the default dataset. - * @returns The default dataset, or undefined if not set. - */ - public getDefaultDataset = () => { - return this.defaultDataset; - }; - - /** - * Fetches the default dataset. - * @returns A promise that resolves to the default dataset, or undefined if not found. - */ - public fetchDefaultDataset = async (): Promise => { - const defaultIndexPatternId = this.uiSettings.get('defaultIndex'); - if (!defaultIndexPatternId || !this.indexPatterns) { - return undefined; - } - - const indexPattern = await this.indexPatterns.get(defaultIndexPatternId); - if (!indexPattern || !indexPattern.id) { - return undefined; - } - - const handler = this.datasetHandlers.get(DEFAULT_DATA.SET_TYPES.INDEX_PATTERN); - if (handler) { - const dataset = handler.toDataset([ - { - id: indexPattern.id, - title: indexPattern.title, - type: DEFAULT_DATA.SET_TYPES.INDEX_PATTERN, - }, - ]); - return { ...dataset, timeFieldName: indexPattern.timeFieldName }; - } - - return undefined; - }; -} - -export type DatasetContract = PublicMethodsOf; diff --git a/src/plugins/data/public/query/query_string/dataset_manager/index.ts b/src/plugins/data/public/query/query_string/dataset_manager/index.ts deleted file mode 100644 index c80f62eb39c..00000000000 --- a/src/plugins/data/public/query/query_string/dataset_manager/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -export * from './types'; -export { DatasetContract, DatasetManager } from './dataset_manager'; diff --git a/src/plugins/data/public/query/query_string/dataset_manager/lib/index.ts b/src/plugins/data/public/query/query_string/dataset_manager/lib/index.ts deleted file mode 100644 index 061b7724421..00000000000 --- a/src/plugins/data/public/query/query_string/dataset_manager/lib/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -export * from './index_handler'; -export * from './index_pattern_handler'; diff --git a/src/plugins/data/public/query/query_string/dataset_manager/lib/index_handler.ts b/src/plugins/data/public/query/query_string/dataset_manager/lib/index_handler.ts deleted file mode 100644 index ecf56b957ff..00000000000 --- a/src/plugins/data/public/query/query_string/dataset_manager/lib/index_handler.ts +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { SavedObjectsClientContract } from 'opensearch-dashboards/public'; -import { map } from 'rxjs/operators'; -import { DEFAULT_DATA, DataStructure, DatasetField, Dataset } from '../../../../../common'; -import { DatasetTypeConfig } from '../types'; -import { getSearchService, getIndexPatterns } from '../../../../services'; - -const INDEX_INFO = { - ID: DEFAULT_DATA.SET_TYPES.INDEX, - TITLE: 'Indexes', - ICON: 'logoOpenSearch', - LOCAL_DATASOURCE: { - id: '', - title: 'Local Cluster', - type: 'DATA_SOURCE', - }, -}; - -export const indexHandlerConfig: DatasetTypeConfig = { - id: DEFAULT_DATA.SET_TYPES.INDEX, - title: 'Indexes', - meta: { - icon: { type: 'logoOpenSearch' }, - tooltip: 'OpenSearch Indexes', - }, - - toDataset: (path: DataStructure[]): Dataset => { - const index = path[path.length - 1]; - const dataSource = path.find((ds) => ds.type === 'DATA_SOURCE'); - - return { - id: index.id, - title: index.title, - type: DEFAULT_DATA.SET_TYPES.INDEX, - dataSource: dataSource - ? { - id: dataSource.id, - title: dataSource.title, - type: dataSource.type, - } - : INDEX_INFO.LOCAL_DATASOURCE, - }; - }, - - fetch: async ( - savedObjects: SavedObjectsClientContract, - path: DataStructure[] - ): Promise => { - const dataStructure = path[path.length - 1]; - switch (dataStructure.type) { - case 'DATA_SOURCE': { - const indices = await fetchIndices(dataStructure); - return { - ...dataStructure, - hasNext: true, - columnHeader: 'Indexes', - children: indices.map((indexName) => ({ - id: `${dataStructure.id}::${indexName}`, - title: indexName, - type: 'INDEX', - })), - }; - } - - default: { - const dataSources = await fetchDataSources(savedObjects); - return { - ...dataStructure, - columnHeader: 'Cluster', - hasNext: false, - children: dataSources, - }; - } - } - }, - - fetchFields: async (dataset: Dataset): Promise => { - const fields = await getIndexPatterns().getFieldsForWildcard({ - pattern: dataset.title, - dataSourceId: dataset.dataSource?.id, - }); - return fields.map((field: any) => ({ - name: field.name, - type: field.type, - })); - }, - - supportedLanguages: async (): Promise => { - return ['SQL', 'PPL']; - }, -}; - -const fetchDataSources = async (client: SavedObjectsClientContract) => { - const resp = await client.find({ - type: 'data-source', - perPage: 10000, - }); - const dataSources: DataStructure[] = [INDEX_INFO.LOCAL_DATASOURCE]; - return dataSources.concat( - resp.savedObjects.map((savedObject) => ({ - id: savedObject.id, - title: savedObject.attributes.title, - type: 'DATA_SOURCE', - })) - ); -}; - -const fetchIndices = async (dataStructure: DataStructure): Promise => { - const search = getSearchService(); - const buildSearchRequest = () => ({ - params: { - ignoreUnavailable: true, - expand_wildcards: 'all', - index: '*', - body: { - size: 0, - aggs: { - indices: { - terms: { - field: '_index', - size: 100, - }, - }, - }, - }, - }, - dataSourceId: dataStructure.id, - }); - - const searchResponseToArray = (response: any) => { - const { rawResponse } = response; - return rawResponse.aggregations - ? rawResponse.aggregations.indices.buckets.map((bucket: { key: any }) => bucket.key) - : []; - }; - - return search - .getDefaultSearchInterceptor() - .search(buildSearchRequest()) - .pipe(map(searchResponseToArray)) - .toPromise(); -}; diff --git a/src/plugins/data/public/query/query_string/dataset_manager/lib/index_pattern_handler.ts b/src/plugins/data/public/query/query_string/dataset_manager/lib/index_pattern_handler.ts deleted file mode 100644 index 37a7d738bb0..00000000000 --- a/src/plugins/data/public/query/query_string/dataset_manager/lib/index_pattern_handler.ts +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - -import { SavedObjectsClientContract } from 'opensearch-dashboards/public'; -import { DEFAULT_DATA, DataStructure, DatasetField, Dataset } from '../../../../../common'; -import { DatasetTypeConfig } from '../types'; -import { getIndexPatterns } from '../../../../services'; - -export const indexPatternHandlerConfig: DatasetTypeConfig = { - id: DEFAULT_DATA.SET_TYPES.INDEX_PATTERN, - title: 'Index Patterns', - meta: { - icon: { type: 'indexPatternApp' }, - tooltip: 'OpenSearch Index Patterns', - }, - - toDataset: (path: DataStructure[]): Dataset => { - const pattern = path[path.length - 1]; - return { - id: pattern.id, - title: pattern.title, - type: DEFAULT_DATA.SET_TYPES.INDEX_PATTERN, - dataSource: pattern.parent - ? { - id: pattern.parent.id, - title: pattern.parent.title, - type: pattern.parent.type, - } - : undefined, - }; - }, - - fetch: async ( - savedObjects: SavedObjectsClientContract, - path: DataStructure[] - ): Promise => { - const dataStructure = path[path.length - 1]; - const indexPatterns = await fetchIndexPatterns(savedObjects); - return { - ...dataStructure, - groupName: 'Index patterns', - children: indexPatterns, - hasNext: false, - }; - }, - - fetchFields: async (dataset: Dataset): Promise => { - const indexPattern = await getIndexPatterns().get(dataset.id); - return indexPattern.fields.map((field: any) => ({ - name: field.name, - type: field.type, - })); - }, - - supportedLanguages: async (): Promise => { - return ['SQL', 'PPL', 'KQL', 'Lucene']; - }, -}; - -const fetchIndexPatterns = async (client: SavedObjectsClientContract): Promise => { - const resp = await client.find({ - type: 'index-pattern', - fields: ['title'], - perPage: 10000, - }); - return resp.savedObjects.map((savedObject) => ({ - id: savedObject.id, - title: savedObject.attributes.title, - type: DEFAULT_DATA.SET_TYPES.INDEX_PATTERN, - isLeaf: true, - })); -}; diff --git a/src/plugins/data/public/query/query_string/dataset_manager/types.ts b/src/plugins/data/public/query/query_string/dataset_manager/types.ts deleted file mode 100644 index 0d7bf37703c..00000000000 --- a/src/plugins/data/public/query/query_string/dataset_manager/types.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ -import { SavedObjectsClientContract } from 'opensearch-dashboards/public'; -import { EuiIconProps } from '@elastic/eui'; -import { Dataset, DatasetField, DataStructure } from '../../../../common'; - -/** - * Configuration for handling dataset operations. - */ -export interface DatasetTypeConfig { - /** Unique identifier for the dataset handler */ - id: string; - /** Human-readable title for the dataset type */ - title: string; - /** Metadata for UI representation */ - meta: { - /** Icon to represent the dataset type */ - icon: EuiIconProps; - /** Optional tooltip text */ - tooltip?: string; - }; - /** - * Converts a DataStructure to a Dataset. - * @param {DataStructure} dataStructure - The data structure to convert. - * @returns {Dataset} Dataset. - */ - toDataset: (path: DataStructure[]) => Dataset; - /** - * Fetches child options for a given DataStructure. - * @param {SavedObjectsClientContract} client - The saved objects client. - * @param {DataStructure} dataStructure - The parent DataStructure. - * @returns {Promise} A promise that resolves to a DatasetHandlerFetchResponse. - */ - fetch: (client: SavedObjectsClientContract, path: DataStructure[]) => Promise; - /** - * Fetches fields for the dataset. - * @returns {Promise} A promise that resolves to an array of DatasetFields. - */ - fetchFields: (dataset: Dataset) => Promise; - /** - * Retrieves the supported query languages for this dataset type. - * @returns {Promise} A promise that resolves to an array of supported language names. - */ - supportedLanguages: () => Promise; -} diff --git a/src/plugins/data/public/query/query_string/dataset_service/dataset_service.ts b/src/plugins/data/public/query/query_string/dataset_service/dataset_service.ts index 59e8138726a..0f152784f91 100644 --- a/src/plugins/data/public/query/query_string/dataset_service/dataset_service.ts +++ b/src/plugins/data/public/query/query_string/dataset_service/dataset_service.ts @@ -4,13 +4,7 @@ */ import { CoreStart, SavedObjectsClientContract } from 'opensearch-dashboards/public'; -import { - Dataset, - UI_SETTINGS, - DataStructure, - IndexPatternSpec, - DEFAULT_DATA, -} from '../../../../common'; +import { Dataset, DataStructure, IndexPatternSpec, DEFAULT_DATA } from '../../../../common'; import { getIndexPatterns } from '../../../services'; import { DatasetTypeConfig } from './types'; import { indexPatternTypeConfig, indexTypeConfig } from './lib'; @@ -32,7 +26,6 @@ export class DatasetService { } public async init(): Promise { - if (!this.uiSettings.get(UI_SETTINGS.QUERY_ENHANCEMENTS_ENABLED)) return; this.defaultDataset = await this.fetchDefaultDataset(); } @@ -44,6 +37,10 @@ export class DatasetService { return this.typesRegistry.get(type); } + public getTypes(): DatasetTypeConfig[] { + return Array.from(this.typesRegistry.values()); + } + public getDefault(): Dataset | undefined { return this.defaultDataset; } @@ -79,10 +76,6 @@ export class DatasetService { return type.fetch(savedObjects, path); } - public getTypes(): string[] { - return Array.from(this.typesRegistry.keys()); - } - private async fetchDefaultDataset(): Promise { const defaultIndexPatternId = this.uiSettings.get('defaultIndex'); if (!defaultIndexPatternId) { 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 6e4e6106291..c5f44445695 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 @@ -55,7 +55,7 @@ export const indexPatternTypeConfig: DatasetTypeConfig = { }, supportedLanguages: (): string[] => { - return ['SQL', 'PPL', 'kuery', 'lucene']; + return ['SQL', 'PPL', 'DQL', 'Lucene']; }, }; @@ -69,6 +69,5 @@ const fetchIndexPatterns = async (client: SavedObjectsClientContract): Promise ({ id: `${dataStructure.id}::${indexName}`, @@ -68,7 +68,7 @@ export const indexTypeConfig: DatasetTypeConfig = { return { ...dataStructure, columnHeader: 'Cluster', - hasNext: false, + hasNext: true, children: dataSources, }; } diff --git a/src/plugins/data/public/query/query_string/language_service/language_service.ts b/src/plugins/data/public/query/query_string/language_service/language_service.ts index 9c2e897097c..6e6e60411b6 100644 --- a/src/plugins/data/public/query/query_string/language_service/language_service.ts +++ b/src/plugins/data/public/query/query_string/language_service/language_service.ts @@ -35,8 +35,8 @@ export class LanguageService { return this.languages.get(language); } - public getLanguages(): string[] { - return Array.from(this.languages.keys()); + public getLanguages(): LanguageConfig[] { + return Array.from(this.languages.values()); } public getDefaultLanguage(): LanguageConfig { diff --git a/src/plugins/data/public/query/query_string/query_string_manager.mock.ts b/src/plugins/data/public/query/query_string/query_string_manager.mock.ts index df19bce5898..45e4fbc6913 100644 --- a/src/plugins/data/public/query/query_string/query_string_manager.mock.ts +++ b/src/plugins/data/public/query/query_string/query_string_manager.mock.ts @@ -28,38 +28,7 @@ * under the License. */ -/* - * SPDX-License-Identifier: Apache-2.0 - * - * The OpenSearch Contributors require contributions made to - * this file be licensed under the Apache-2.0 license or a - * compatible open source license. - * - * Any modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - import { QueryStringContract } from '.'; -// import { datasetManagerMock } from './dataset_manager/dataset_manager.mock'; const createSetupContractMock = () => { const queryStringManagerMock: jest.Mocked = { @@ -73,14 +42,9 @@ const createSetupContractMock = () => { getQueryHistory: jest.fn(), clearQueryHistory: jest.fn(), changeQueryHistory: jest.fn(), - getDatasetManager: jest.fn(), - initDataset: jest.fn(), - registerType: jest.fn(), - getDatasetType: jest.fn(), - getDatasetTypes: jest.fn(), - getLanguage: jest.fn(), - getLanguages: jest.fn(), - registerLanguage: jest.fn(), + getInitialQuery: jest.fn(), + getDatasetService: jest.fn(), + getLanguageService: jest.fn(), }; return queryStringManagerMock; }; diff --git a/src/plugins/data/public/query/query_string/query_string_manager.ts b/src/plugins/data/public/query/query_string/query_string_manager.ts index 1fa6641dd0a..7760e621ed3 100644 --- a/src/plugins/data/public/query/query_string/query_string_manager.ts +++ b/src/plugins/data/public/query/query_string/query_string_manager.ts @@ -33,14 +33,12 @@ import { skip } from 'rxjs/operators'; import { CoreStart } from 'opensearch-dashboards/public'; import { Dataset, DataStorage, Query, TimeRange, UI_SETTINGS } from '../../../common'; import { createHistory, QueryHistory } from './query_history'; -import { DatasetContract } from './dataset_manager'; -import { DatasetService, DatasetServiceContract, DatasetTypeConfig } from './dataset_service'; -import { LanguageConfig, LanguageService, LanguageServiceContract } from './language_service'; +import { DatasetService, DatasetServiceContract } from './dataset_service'; +import { LanguageService, LanguageServiceContract } from './language_service'; export class QueryStringManager { private query$: BehaviorSubject; private queryHistory: QueryHistory; - private datasetManager!: DatasetContract; private datasetService!: DatasetServiceContract; private languageService!: LanguageServiceContract; @@ -50,7 +48,6 @@ export class QueryStringManager { ) { this.query$ = new BehaviorSubject(this.getDefaultQuery()); this.queryHistory = createHistory({ storage }); - // this.datasetManager = new DatasetManager(uiSettings); this.datasetService = new DatasetService(uiSettings); this.languageService = new LanguageService(uiSettings); } @@ -135,31 +132,26 @@ export class QueryStringManager { public changeQueryHistory(listener: (reqs: any[]) => void) { return this.queryHistory.change(listener); } - /** - * TODO: verify if we want to just access the dataset manager directly or access each function - */ - public getDatasetManager = () => { - return this.datasetManager; - }; - public initDataset = async () => { - await this.datasetService.init(); + public getDatasetService = () => { + return this.datasetService; }; - public registerType = (type: DatasetTypeConfig) => { - this.datasetService.registerType(type); + public getLanguageService = () => { + return this.languageService; }; - public getDatasetType = (type: string) => { - return this.datasetService.getType(type); - }; - - public getDatasetTypes = () => { - return this.datasetService.getTypes(); - }; + public getInitialQuery = () => { + const curQuery = this.query$.getValue(); + const language = this.languageService.getLanguage(curQuery.language); + const dataset = curQuery.dataset; + const input = language?.searchBar?.queryStringInput?.initialValue || ''; - public getLanguage = (language: string) => { - return this.languageService.getLanguage(language); + return { + query: input || input.replace('', dataset?.title ?? ''), + language: curQuery.language, + dataset: curQuery.dataset, + }; }; private getDefaultLanguage() { @@ -168,14 +160,6 @@ export class QueryStringManager { this.uiSettings.get(UI_SETTINGS.SEARCH_QUERY_LANGUAGE) ); } - - public getLanguages = () => { - return this.languageService.getLanguages(); - }; - - public registerLanguage = (languageConfig: LanguageConfig) => { - this.languageService.registerLanguage(languageConfig); - }; } export type QueryStringContract = PublicMethodsOf; diff --git a/src/plugins/data/public/query/state_sync/create_global_query_observable.ts b/src/plugins/data/public/query/state_sync/create_global_query_observable.ts index 04f52daad90..8abcb3ece18 100644 --- a/src/plugins/data/public/query/state_sync/create_global_query_observable.ts +++ b/src/plugins/data/public/query/state_sync/create_global_query_observable.ts @@ -52,7 +52,6 @@ export function createQueryStateObservable({ refreshInterval: timefilter.getRefreshInterval(), filters: filterManager.getFilters(), query: queryString.getQuery(), - dataset: queryString.getDatasetManager().getDataset(), }); let currentChange: QueryStateChange = {}; @@ -61,13 +60,6 @@ export function createQueryStateObservable({ currentChange.query = true; state.set({ ...state.get(), query: queryString.getQuery() }); }), - queryString - .getDatasetManager() - .getUpdates$() - .subscribe(() => { - currentChange.dataset = true; - state.set({ ...state.get(), dataset: queryString.getDatasetManager().getDataset() }); - }), timefilter.getTimeUpdate$().subscribe(() => { currentChange.time = true; state.set({ ...state.get(), time: timefilter.getTime() }); diff --git a/src/plugins/data/public/search/search_service.ts b/src/plugins/data/public/search/search_service.ts index d07f63cbbba..1b5323d5fda 100644 --- a/src/plugins/data/public/search/search_service.ts +++ b/src/plugins/data/public/search/search_service.ts @@ -136,18 +136,18 @@ export class SearchService implements Plugin { ): ISearchStart { const search = ((request, options) => { const queryStringManager = getQueryService().queryString; - const languageId = queryStringManager.getQuery().language; - const selectedLanguage = queryStringManager.getLanguage(languageId); - getUiService().Settings.setUiOverridesByUserQueryLanguage(languageId); + const language = queryStringManager.getQuery().language; + const languageConfig = queryStringManager.getLanguageService().getLanguage(language); + getUiService().Settings.setUiOverridesByUserQueryLanguage(language); const isEnhancedEnabled = uiSettings.get(UI_SETTINGS.QUERY_ENHANCEMENTS_ENABLED); - if (selectedLanguage) { + if (languageConfig) { if (!isEnhancedEnabled) { notifications.toasts.addWarning( - `Query enhancements are disabled. Please enable to use: ${selectedLanguage.id}.` + `Query enhancements are disabled. Please enable to use: ${languageConfig.id}.` ); } - return selectedLanguage.search.search(request, options); + return languageConfig.search.search(request, options); } return this.defaultSearchInterceptor.search(request, options); }) as ISearchGeneric; diff --git a/src/plugins/data/public/ui/dataset_selector/__mocks__/utils.ts b/src/plugins/data/public/ui/dataset_selector/__mocks__/utils.ts index e373f33c4ae..68fc16ed4b1 100644 --- a/src/plugins/data/public/ui/dataset_selector/__mocks__/utils.ts +++ b/src/plugins/data/public/ui/dataset_selector/__mocks__/utils.ts @@ -3,347 +3,341 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { - Dataset, - DatasetManager, - DatasetTypeConfig, - DataSource, - DataStructure, -} from '../../../../common/datasets'; - -interface DatasetVariations { - dataSourceTypes: string[]; - dataSetTypes: string[]; - timeFields: string[]; -} - -export function generateRandomDatasets(count: number, variations: DatasetVariations): Dataset[] { - const datasets: Dataset[] = []; - - for (let i = 0; i < count; i++) { - const dataSource: DataSource = { - id: `ds-${Math.random().toString(36).substr(2, 9)}`, - title: `Data Source ${i + 1}`, - type: - variations.dataSourceTypes[Math.floor(Math.random() * variations.dataSourceTypes.length)], - }; - - const dataset: Dataset = { - id: `dataset-${Math.random().toString(36).substr(2, 9)}`, - title: `Dataset ${i + 1}`, - type: variations.dataSetTypes[Math.floor(Math.random() * variations.dataSetTypes.length)], - timeFieldName: - variations.timeFields[Math.floor(Math.random() * variations.timeFields.length)], - dataSource, - }; - - datasets.push(dataset); - } - - return datasets; -} - -class MockDatasetManager implements DatasetManager { - private types: { - [key: string]: DatasetTypeConfig; - } = {}; - - private dataset?: Dataset; - private recentDatasets: Dataset[] = []; - - constructor() { - this.registerType({ - id: 'index-pattern', - title: 'Index Pattern', - getOptions: this.mockGetIndexPatternOptions, - getDataset: this.mockGetIndexPatternDataset, - getFields: this.mockGetIndexPatternFields, - config: { - supportedLanguages: ['DQL', 'lucene'], - icon: { - type: 'indexPatternApp', - }, - }, - }); - - this.registerType({ - id: 'indices', - title: 'Indices', - getOptions: this.mockGetIndicesOptions, - getDataset: this.mockGetIndicesDataset, - getFields: this.mockGetIndicesFields, - config: { - supportedLanguages: ['DQL', 'lucene'], - icon: { - type: 'logoOpenSearch', - }, - hasTimeField: true, - }, - }); - - this.registerType({ - id: 's3', - title: 'S3', - getOptions: this.mockGetS3Options, - getDataset: this.mockGetS3Dataset, - getFields: this.mockGetS3Fields, - config: { - supportedLanguages: ['SQL'], - icon: { - type: 'logoAWS', - }, - hasTimeField: false, - }, - }); - } - - registerType = (props: DatasetTypeConfig) => { - this.types[props.id] = props; - }; - - getTypes = () => { - return Object.values(this.types); - }; - - getType = (type: string) => { - return this.types[type]; - }; - - getDataSet = () => { - return this.dataset; - }; - - setDataSet = (dataset: Dataset) => { - this.dataset = dataset; - // Store the top 10 recently used - this.recentDatasets = [dataset, ...this.recentDatasets].slice(0, 10); - }; - - getRecentlyUsed = () => { - return this.recentDatasets; - }; - - private mockGetIndexPatternOptions = async (path?: DataStructure[]) => { - const lastOption = path?.[path.length - 1]; - - if (lastOption?.id !== 'index-pattern') { - return { - options: [], - columnHeader: 'Index patterns', - }; - } - const mockData: DataStructure[] = [ - { id: 'pattern1', title: 'logs-*', type: 'index-pattern' }, - { id: 'pattern2', title: 'metrics-*', type: 'index-pattern' }, - { id: 'pattern3', title: 'events-*', type: 'index-pattern' }, - ]; - - return { - options: mockData, - columnHeader: 'Index patterns', - }; - }; - - private mockGetIndexPatternDataset = (path: DataStructure[]) => { - const lastOption = path?.[path.length - 1]; - - return { - id: lastOption?.id, - title: lastOption?.title, - type: 'index-pattern', - timeFieldName: 'timestamp', - dataSource: { - id: 'main-cluster', - title: 'Main OpenSearch Cluster', - type: 'OPENSEARCH', - }, - }; - }; - - private mockGetIndexPatternFields = async (dataset: Dataset) => { - return [ - { - name: 'timestamp', - type: 'date', - }, - { - name: 'bytes', - type: 'number', - }, - { - name: 'cpu', - type: 'number', - }, - { - name: 'memory', - type: 'number', - }, - { - name: 'log', - type: 'string', - }, - ]; - }; - - private mockGetIndicesOptions = async (path?: DataStructure[]) => { - const option = path?.[path.length - 1]; - if (option?.type === 'indices') { - // First level: List of clusters - const mockClusters: DataStructure[] = [ - { id: 'cluster1', title: 'Production Cluster', type: 'cluster' }, - { id: 'cluster2', title: 'Development Cluster', type: 'cluster' }, - { id: 'cluster3', title: 'Testing Cluster', type: 'cluster' }, - ]; - - return { - options: mockClusters, - isLoadable: true, - columnHeader: 'Clusters', - }; - } else if (option?.type === 'cluster') { - // Second level: List of indices within the selected cluster - const mockIndices: DataStructure[] = [ - { id: `${option.id}-index1`, title: `${option.title}-logs-2023.05`, type: 'index' }, - { id: `${option.id}-index2`, title: `${option.title}-metrics-2023.05`, type: 'index' }, - { id: `${option.id}-index3`, title: `${option.title}-events-2023.05`, type: 'index' }, - ]; - - return { - options: mockIndices, - columnHeader: 'Indices', - }; - } - - // If we reach here, it's an unexpected state - return { - options: [], - columnHeader: 'Indices', - }; - }; - - private mockGetIndicesDataset = (path: DataStructure[]) => { - const option = path?.[path.length - 1]; - - return { - id: option?.id, - title: option?.title, - type: 'indices', - timeFieldName: 'timestamp', - dataSource: { - id: 'main-cluster', - title: 'Main OpenSearch Cluster', - type: 'OPENSEARCH', - }, - }; - }; - - private mockGetIndicesFields = async (dataset: Dataset) => { - return [ - { - name: 'timestamp', - type: 'date', - }, - { - name: 'bytes', - type: 'number', - }, - { - name: 'cpu', - type: 'number', - }, - { - name: 'memory', - type: 'number', - }, - { - name: 'log', - type: 'string', - }, - ]; - }; - - private mockGetS3Options = async (path?: DataStructure[]) => { - const lastOption = path?.[path.length - 1]; - - if (!lastOption || lastOption.type === 's3') { - // First level: Connections (instantaneous) - const mockConnections: DataStructure[] = [ - { id: 'conn1', title: 'Production S3', type: 'connection' }, - { id: 'conn2', title: 'Development S3', type: 'connection' }, - { id: 'conn3', title: 'Testing S3', type: 'connection' }, - ]; - - return { - options: mockConnections, - columnHeader: 'S3 Connections', - isLoadable: true, - }; - } else if (lastOption.type === 'connection') { - // Second level: Databases (10s delay) - await new Promise((resolve) => setTimeout(resolve, 3000)); - - const mockDatabases: DataStructure[] = [ - { id: `${lastOption.id}-db1`, title: 'Customer Data', type: 'database' }, - { id: `${lastOption.id}-db2`, title: 'Product Catalog', type: 'database' }, - { id: `${lastOption.id}-db3`, title: 'Sales Records', type: 'database' }, - ]; - - return { - options: mockDatabases, - columnHeader: 'S3 Databases', - isLoadable: true, - }; - } else if (lastOption.type === 'database') { - // Third level: Tables (10s delay) - await new Promise((resolve) => setTimeout(resolve, 3000)); - - const mockTables: DataStructure[] = [ - { id: `${lastOption.id}-table1`, title: 'Users', type: 'table' }, - { id: `${lastOption.id}-table2`, title: 'Orders', type: 'table' }, - { id: `${lastOption.id}-table3`, title: 'Products', type: 'table' }, - ]; - - return { - options: mockTables, - columnHeader: 'S3 Tables', - isLoadable: false, - }; - } - - return { - options: [], - columnHeader: 'S3', - }; - }; - - private mockGetS3Dataset = (path: DataStructure[]) => { - const lastOption = path[path.length - 1]; - - return { - id: lastOption.id, - title: lastOption.title, - type: 's3', - dataSource: { - id: path[1].id, // Connection ID - title: path[1].title, // Connection Title - type: 'S3', - }, - }; - }; - - private mockGetS3Fields = async (dataset: Dataset) => { - // Simulate a delay for field fetching - await new Promise((resolve) => setTimeout(resolve, 5000)); - - return [ - { name: 'id', type: 'string' }, - { name: 'name', type: 'string' }, - { name: 'created_at', type: 'date' }, - { name: 'updated_at', type: 'date' }, - { name: 'value', type: 'number' }, - ]; - }; -} - -// Export an instance of the mock manager -export const mockDatasetManager = new MockDatasetManager(); +// import { Dataset, DataSource, DataStructure } from '../../../../common/datasets'; + +// interface DatasetVariations { +// dataSourceTypes: string[]; +// dataSetTypes: string[]; +// timeFields: string[]; +// } + +// export function generateRandomDatasets(count: number, variations: DatasetVariations): Dataset[] { +// const datasets: Dataset[] = []; + +// for (let i = 0; i < count; i++) { +// const dataSource: DataSource = { +// id: `ds-${Math.random().toString(36).substr(2, 9)}`, +// title: `Data Source ${i + 1}`, +// type: +// variations.dataSourceTypes[Math.floor(Math.random() * variations.dataSourceTypes.length)], +// }; + +// const dataset: Dataset = { +// id: `dataset-${Math.random().toString(36).substr(2, 9)}`, +// title: `Dataset ${i + 1}`, +// type: variations.dataSetTypes[Math.floor(Math.random() * variations.dataSetTypes.length)], +// timeFieldName: +// variations.timeFields[Math.floor(Math.random() * variations.timeFields.length)], +// dataSource, +// }; + +// datasets.push(dataset); +// } + +// return datasets; +// } + +// class MockDatasetManager implements DatasetManager { +// private types: { +// [key: string]: DatasetTypeConfig; +// } = {}; + +// private dataset?: Dataset; +// private recentDatasets: Dataset[] = []; + +// constructor() { +// this.registerType({ +// id: 'index-pattern', +// title: 'Index Pattern', +// getOptions: this.mockGetIndexPatternOptions, +// getDataset: this.mockGetIndexPatternDataset, +// getFields: this.mockGetIndexPatternFields, +// config: { +// supportedLanguages: ['DQL', 'lucene'], +// icon: { +// type: 'indexPatternApp', +// }, +// }, +// }); + +// this.registerType({ +// id: 'indices', +// title: 'Indices', +// getOptions: this.mockGetIndicesOptions, +// getDataset: this.mockGetIndicesDataset, +// getFields: this.mockGetIndicesFields, +// config: { +// supportedLanguages: ['DQL', 'lucene'], +// icon: { +// type: 'logoOpenSearch', +// }, +// hasTimeField: true, +// }, +// }); + +// this.registerType({ +// id: 's3', +// title: 'S3', +// getOptions: this.mockGetS3Options, +// getDataset: this.mockGetS3Dataset, +// getFields: this.mockGetS3Fields, +// config: { +// supportedLanguages: ['SQL'], +// icon: { +// type: 'logoAWS', +// }, +// hasTimeField: false, +// }, +// }); +// } + +// registerType = (props: DatasetTypeConfig) => { +// this.types[props.id] = props; +// }; + +// getTypes = () => { +// return Object.values(this.types); +// }; + +// getType = (type: string) => { +// return this.types[type]; +// }; + +// getDataSet = () => { +// return this.dataset; +// }; + +// setDataSet = (dataset: Dataset) => { +// this.dataset = dataset; +// // Store the top 10 recently used +// this.recentDatasets = [dataset, ...this.recentDatasets].slice(0, 10); +// }; + +// getRecentlyUsed = () => { +// return this.recentDatasets; +// }; + +// private mockGetIndexPatternOptions = async (path?: DataStructure[]) => { +// const lastOption = path?.[path.length - 1]; + +// if (lastOption?.id !== 'index-pattern') { +// return { +// options: [], +// columnHeader: 'Index patterns', +// }; +// } +// const mockData: DataStructure[] = [ +// { id: 'pattern1', title: 'logs-*', type: 'index-pattern' }, +// { id: 'pattern2', title: 'metrics-*', type: 'index-pattern' }, +// { id: 'pattern3', title: 'events-*', type: 'index-pattern' }, +// ]; + +// return { +// options: mockData, +// columnHeader: 'Index patterns', +// }; +// }; + +// private mockGetIndexPatternDataset = (path: DataStructure[]) => { +// const lastOption = path?.[path.length - 1]; + +// return { +// id: lastOption?.id, +// title: lastOption?.title, +// type: 'index-pattern', +// timeFieldName: 'timestamp', +// dataSource: { +// id: 'main-cluster', +// title: 'Main OpenSearch Cluster', +// type: 'OPENSEARCH', +// }, +// }; +// }; + +// private mockGetIndexPatternFields = async (dataset: Dataset) => { +// return [ +// { +// name: 'timestamp', +// type: 'date', +// }, +// { +// name: 'bytes', +// type: 'number', +// }, +// { +// name: 'cpu', +// type: 'number', +// }, +// { +// name: 'memory', +// type: 'number', +// }, +// { +// name: 'log', +// type: 'string', +// }, +// ]; +// }; + +// private mockGetIndicesOptions = async (path?: DataStructure[]) => { +// const option = path?.[path.length - 1]; +// if (option?.type === 'indices') { +// // First level: List of clusters +// const mockClusters: DataStructure[] = [ +// { id: 'cluster1', title: 'Production Cluster', type: 'cluster' }, +// { id: 'cluster2', title: 'Development Cluster', type: 'cluster' }, +// { id: 'cluster3', title: 'Testing Cluster', type: 'cluster' }, +// ]; + +// return { +// options: mockClusters, +// isLoadable: true, +// columnHeader: 'Clusters', +// }; +// } else if (option?.type === 'cluster') { +// // Second level: List of indices within the selected cluster +// const mockIndices: DataStructure[] = [ +// { id: `${option.id}-index1`, title: `${option.title}-logs-2023.05`, type: 'index' }, +// { id: `${option.id}-index2`, title: `${option.title}-metrics-2023.05`, type: 'index' }, +// { id: `${option.id}-index3`, title: `${option.title}-events-2023.05`, type: 'index' }, +// ]; + +// return { +// options: mockIndices, +// columnHeader: 'Indices', +// }; +// } + +// // If we reach here, it's an unexpected state +// return { +// options: [], +// columnHeader: 'Indices', +// }; +// }; + +// private mockGetIndicesDataset = (path: DataStructure[]) => { +// const option = path?.[path.length - 1]; + +// return { +// id: option?.id, +// title: option?.title, +// type: 'indices', +// timeFieldName: 'timestamp', +// dataSource: { +// id: 'main-cluster', +// title: 'Main OpenSearch Cluster', +// type: 'OPENSEARCH', +// }, +// }; +// }; + +// private mockGetIndicesFields = async (dataset: Dataset) => { +// return [ +// { +// name: 'timestamp', +// type: 'date', +// }, +// { +// name: 'bytes', +// type: 'number', +// }, +// { +// name: 'cpu', +// type: 'number', +// }, +// { +// name: 'memory', +// type: 'number', +// }, +// { +// name: 'log', +// type: 'string', +// }, +// ]; +// }; + +// private mockGetS3Options = async (path?: DataStructure[]) => { +// const lastOption = path?.[path.length - 1]; + +// if (!lastOption || lastOption.type === 's3') { +// // First level: Connections (instantaneous) +// const mockConnections: DataStructure[] = [ +// { id: 'conn1', title: 'Production S3', type: 'connection' }, +// { id: 'conn2', title: 'Development S3', type: 'connection' }, +// { id: 'conn3', title: 'Testing S3', type: 'connection' }, +// ]; + +// return { +// options: mockConnections, +// columnHeader: 'S3 Connections', +// isLoadable: true, +// }; +// } else if (lastOption.type === 'connection') { +// // Second level: Databases (10s delay) +// await new Promise((resolve) => setTimeout(resolve, 3000)); + +// const mockDatabases: DataStructure[] = [ +// { id: `${lastOption.id}-db1`, title: 'Customer Data', type: 'database' }, +// { id: `${lastOption.id}-db2`, title: 'Product Catalog', type: 'database' }, +// { id: `${lastOption.id}-db3`, title: 'Sales Records', type: 'database' }, +// ]; + +// return { +// options: mockDatabases, +// columnHeader: 'S3 Databases', +// isLoadable: true, +// }; +// } else if (lastOption.type === 'database') { +// // Third level: Tables (10s delay) +// await new Promise((resolve) => setTimeout(resolve, 3000)); + +// const mockTables: DataStructure[] = [ +// { id: `${lastOption.id}-table1`, title: 'Users', type: 'table' }, +// { id: `${lastOption.id}-table2`, title: 'Orders', type: 'table' }, +// { id: `${lastOption.id}-table3`, title: 'Products', type: 'table' }, +// ]; + +// return { +// options: mockTables, +// columnHeader: 'S3 Tables', +// isLoadable: false, +// }; +// } + +// return { +// options: [], +// columnHeader: 'S3', +// }; +// }; + +// private mockGetS3Dataset = (path: DataStructure[]) => { +// const lastOption = path[path.length - 1]; + +// return { +// id: lastOption.id, +// title: lastOption.title, +// type: 's3', +// dataSource: { +// id: path[1].id, // Connection ID +// title: path[1].title, // Connection Title +// type: 'S3', +// }, +// }; +// }; + +// private mockGetS3Fields = async (dataset: Dataset) => { +// // Simulate a delay for field fetching +// await new Promise((resolve) => setTimeout(resolve, 5000)); + +// return [ +// { name: 'id', type: 'string' }, +// { name: 'name', type: 'string' }, +// { name: 'created_at', type: 'date' }, +// { name: 'updated_at', type: 'date' }, +// { name: 'value', type: 'number' }, +// ]; +// }; +// } + +// // Export an instance of the mock manager +// export const mockDatasetManager = new MockDatasetManager(); 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 b7f80571b79..a319c9a376b 100644 --- a/src/plugins/data/public/ui/dataset_selector/advanced_selector.tsx +++ b/src/plugins/data/public/ui/dataset_selector/advanced_selector.tsx @@ -31,19 +31,21 @@ export const AdvancedSelector = ({ { ...DEFAULT_DATA.STRUCTURES.ROOT, columnHeader: 'Select data', - hasNext: false, - children: queryString.getDatasetTypes().map((typeId) => { - const type = queryString.getDatasetType(typeId); - return { - id: type!.id, - title: type!.title, - type: type!.id, - meta: { - ...type!.meta, - type: DATA_STRUCTURE_META_TYPES.TYPE, - }, - } as DataStructure; - }), + hasNext: true, + children: queryString + .getDatasetService() + .getTypes() + .map((type) => { + return { + id: type!.id, + title: type!.title, + type: type!.id, + meta: { + ...type!.meta, + type: DATA_STRUCTURE_META_TYPES.TYPE, + }, + } as DataStructure; + }), }, ]); const [selectedDataset, setSelectedDataset] = useState(); diff --git a/src/plugins/data/public/ui/dataset_selector/configurator.tsx b/src/plugins/data/public/ui/dataset_selector/configurator.tsx index 5832fde784e..ce55fe331d3 100644 --- a/src/plugins/data/public/ui/dataset_selector/configurator.tsx +++ b/src/plugins/data/public/ui/dataset_selector/configurator.tsx @@ -36,7 +36,8 @@ export const Configurator = ({ const queryService = getQueryService(); const queryString = queryService.queryString; const indexPatternsService = getIndexPatterns(); - const languages = queryString.getDatasetType(baseDataset.type)?.supportedLanguages() || []; + const languages = + queryString.getDatasetService().getType(baseDataset.type)?.supportedLanguages() || []; const [dataset, setDataset] = useState(baseDataset); const [fields, setFields] = useState(); @@ -47,7 +48,8 @@ export const Configurator = ({ useEffect(() => { const fetchFields = async () => { const datasetFields = await queryString - .getDatasetType(baseDataset.type) + .getDatasetService() + .getType(baseDataset.type) ?.fetchFields(baseDataset); setFields(datasetFields); 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 8271efc631d..b17a38ae829 100644 --- a/src/plugins/data/public/ui/dataset_selector/dataset_explorer.tsx +++ b/src/plugins/data/public/ui/dataset_selector/dataset_explorer.tsx @@ -45,23 +45,23 @@ export const DatasetExplorer = ({ const lastPathItem = newPath[newPath.length - 1]; const nextPath = [...newPath, item]; - const handler = queryString.getDatasetType(nextPath[1].id); - if (!handler) return; + const typeConfig = queryString.getDatasetService().getType(nextPath[1].id); + if (!typeConfig) return; - if (lastPathItem.hasNext) { - const dataset = handler!.toDataset(nextPath); + if (!lastPathItem.hasNext) { + const dataset = typeConfig!.toDataset(nextPath); setExplorerDataset(dataset as BaseDataset); return; } setLoading(true); - const nextDataStructure = await handler.fetch(savedObjects, nextPath); + const nextDataStructure = await typeConfig.fetch(savedObjects, nextPath); setLoading(false); setPath([...newPath, nextDataStructure]); }; - const columnCount = !path[path.length - 1]?.hasNext ? path.length + 1 : path.length; + const columnCount = path[path.length - 1]?.hasNext ? path.length + 1 : path.length; return ( <> @@ -97,7 +97,7 @@ export const DatasetExplorer = ({ > {path.map((current, index) => { const isLast = index === path.length - 1; - const isFinal = isLast && current.hasNext; + const isFinal = isLast && !current.hasNext; return (
-

{current.hasNext}

+

{current.columnHeader}

({ @@ -145,7 +145,7 @@ export const DatasetExplorer = ({
); })} - {!!!path[path.length - 1]?.hasNext && } + {!!path[path.length - 1]?.hasNext && } diff --git a/src/plugins/data/public/ui/dataset_selector/dataset_selector.tsx b/src/plugins/data/public/ui/dataset_selector/dataset_selector.tsx index 3b2ec154188..1d04698d781 100644 --- a/src/plugins/data/public/ui/dataset_selector/dataset_selector.tsx +++ b/src/plugins/data/public/ui/dataset_selector/dataset_selector.tsx @@ -37,24 +37,24 @@ export const DatasetSelector = ({ const closePopover = () => setIsOpen(false); const { overlays, savedObjects } = services; - const queryString = getQueryService().queryString; + const datasetService = getQueryService().queryString.getDatasetService(); const datasetIcon = - queryString.getDatasetType(selectedDataset?.type || '')?.meta.icon.type || 'database'; + datasetService.getType(selectedDataset?.type || '')?.meta.icon.type || 'database'; useEffect(() => { const init = async () => { setDatasets( ( - await queryString - .getDatasetType(DEFAULT_DATA.SET_TYPES.INDEX_PATTERN) + await datasetService + .getType(DEFAULT_DATA.SET_TYPES.INDEX_PATTERN) ?.fetch(savedObjects.client, [])! ).children || [] ); }; init(); - }, [queryString, savedObjects.client]); + }, [datasetService, savedObjects.client]); const options = useMemo(() => { const newOptions: EuiSelectableOption[] = []; @@ -69,12 +69,12 @@ export const DatasetSelector = ({ label: title, checked: id === selectedDataset?.id ? 'on' : undefined, key: id, - prepend: , + prepend: , }); }); return newOptions; - }, [queryString, datasets, selectedDataset?.id]); + }, [datasets, selectedDataset?.id, datasetService]); // Handle option change const handleOptionChange = (newOptions: EuiSelectableOption[]) => { diff --git a/src/plugins/data/public/ui/query_editor/language_selector.tsx b/src/plugins/data/public/ui/query_editor/language_selector.tsx index 08886647822..647c087b0dc 100644 --- a/src/plugins/data/public/ui/query_editor/language_selector.tsx +++ b/src/plugins/data/public/ui/query_editor/language_selector.tsx @@ -3,11 +3,6 @@ * SPDX-License-Identifier: Apache-2.0 */ -/* - * Copyright OpenSearch Contributors - * SPDX-License-Identifier: Apache-2.0 - */ - import React, { useState, useEffect } from 'react'; import { PopoverAnchorPosition, @@ -39,10 +34,11 @@ export const QueryLanguageSelector = (props: QueryLanguageSelectorProps) => { const [currentLanguage, setCurrentLanguage] = useState(props.query.language); const uiService = getUiService(); - const queryService = getQueryService(); + const queryString = getQueryService().queryString; + const languageService = queryString.getLanguageService(); useEffect(() => { - const subscription = queryService.queryString.getUpdates$().subscribe((query: Query) => { + const subscription = queryString.getUpdates$().subscribe((query: Query) => { if (query.language !== currentLanguage) { setCurrentLanguage(query.language); props.onSelectLanguage(query.language); @@ -52,7 +48,7 @@ export const QueryLanguageSelector = (props: QueryLanguageSelectorProps) => { return () => { subscription.unsubscribe(); }; - }, [queryService, currentLanguage, props.onSelectLanguage, props]); + }, [queryString, currentLanguage, props.onSelectLanguage, props]); const onButtonClick = () => { setPopover(!isPopoverOpen); @@ -60,9 +56,7 @@ export const QueryLanguageSelector = (props: QueryLanguageSelectorProps) => { const languageOptions: Array<{ label: string; value: string }> = []; - const languages = queryService.queryString.getLanguages(); - languages.forEach((languageId) => { - const language = queryService.queryString.getLanguage(languageId); + languageService.getLanguages().forEach((language) => { if ( (language && props.appName && !language.supportedAppNames.includes(props.appName)) || uiService.Settings.getUserQueryLanguageBlocklist().includes(language?.id) @@ -84,8 +78,8 @@ export const QueryLanguageSelector = (props: QueryLanguageSelectorProps) => { uiService.Settings.setUserQueryLanguage(newLanguage); // Update the query in the QueryStringManager - const currentQuery = queryService.queryString.getQuery(); - const input = queryService.queryString.getLanguage(newLanguage)?.searchBar?.queryStringInput + const currentQuery = queryString.getQuery(); + const input = languageService.getLanguage(newLanguage)?.searchBar?.queryStringInput ?.initialValue; if (!input) return ''; @@ -94,7 +88,7 @@ export const QueryLanguageSelector = (props: QueryLanguageSelectorProps) => { currentQuery.dataset?.title ?? currentQuery.dataset?.title ?? '' ); - queryService.queryString.setQuery({ + queryString.setQuery({ query: newQuery, language: newLanguage, dataset: currentQuery.dataset, diff --git a/src/plugins/data/public/ui/query_editor/query_editor.tsx b/src/plugins/data/public/ui/query_editor/query_editor.tsx index 508b9464f53..77a313184f0 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor.tsx @@ -86,7 +86,7 @@ export default class QueryEditorUI extends Component { public inputRef: monaco.editor.IStandaloneCodeEditor | null = null; - private queryService = getQueryService(); + private queryString = getQueryService().queryString; private persistedLog: PersistedLog | undefined; private abortController?: AbortController; @@ -181,7 +181,7 @@ export default class QueryEditorUI extends Component { body: JSON.stringify({ opt_in: languageId === 'kuery' }), }); - const language = this.queryService.queryString.getLanguage(this.props.query.language); + const language = this.queryString.getLanguageService().getLanguage(this.props.query.language); const newQuery = { query: this.props.getQueryStringInitialValue?.(language!) ?? '', language: language?.id || languageId, @@ -246,9 +246,9 @@ export default class QueryEditorUI extends Component { }; private fetchIndexPattern = async () => { - const dataSetTitle = this.queryService.queryString.getDatasetManager().getDataset()?.title; - if (!dataSetTitle) return undefined; - return getIndexPatterns().getByTitle(dataSetTitle); + const dataset = this.queryString.getQuery().dataset; + if (!dataset) return undefined; + return getIndexPatterns().getByTitle(dataset.title); }; provideCompletionItems = async ( diff --git a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx index e33a617f5d0..494f579efb4 100644 --- a/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx +++ b/src/plugins/data/public/ui/query_editor/query_editor_top_row.tsx @@ -22,11 +22,10 @@ import { withOpenSearchDashboards, } from '../../../../opensearch_dashboards_react/public'; import { UI_SETTINGS } from '../../../common'; -import { getQueryLog, LanguageConfig, PersistedLog } from '../../query'; +import { getQueryLog, PersistedLog } from '../../query'; import { Settings } from '../types'; import { NoDataPopover } from './no_data_popover'; import QueryEditorUI from './query_editor'; -import { useQueryStringManager } from '../search_bar/lib/use_query_string_manager'; const QueryEditor = withOpenSearchDashboards(QueryEditorUI); @@ -73,26 +72,20 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { query: { queryString }, }, } = opensearchDashboards.services; - const { - query: { dataset }, - } = useQueryStringManager({ queryStringManager: queryString }); - const queryLanguageId = props.query && props.query.language; - const queryLanguage = queryString.getLanguage(queryLanguageId!) || null; + const language = props.query && props.query.language; const parsedQuery = - !queryLanguageId || isValidQuery(props.query) - ? props.query! - : { query: getQueryStringInitialValue(queryLanguage!), language: queryLanguageId! }; + !language || isValidQuery(props.query) ? props.query! : queryString.getInitialQuery(); if (!isEqual(parsedQuery?.query, props.query?.query)) { onQueryChange(parsedQuery); onSubmit({ query: parsedQuery, dateRange: getDateRange() }); } const persistedLog: PersistedLog | undefined = React.useMemo( () => - queryLanguage && uiSettings && storage && appName - ? getQueryLog(uiSettings!, storage, appName, queryLanguageId!) + language && uiSettings && storage && appName + ? getQueryLog(uiSettings!, storage, appName, language!) : undefined, - [queryLanguage, uiSettings, storage, appName, queryLanguageId] + [language, uiSettings, storage, appName] ); function onClickSubmitButton(event: React.MouseEvent) { @@ -105,20 +98,22 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { function getDateRange() { const defaultTimeSetting = uiSettings!.get(UI_SETTINGS.TIMEPICKER_TIME_DEFAULTS); + const languageConfig = queryString.getLanguageService().getLanguage(language!); return { from: props.dateRangeFrom || - queryLanguage?.searchBar?.dateRange?.initialFrom || + languageConfig?.searchBar?.dateRange?.initialFrom || defaultTimeSetting.from, to: props.dateRangeTo || - queryLanguage?.searchBar?.dateRange?.initialTo || + languageConfig?.searchBar?.dateRange?.initialTo || defaultTimeSetting.to, }; } function onQueryChange(query: Query, dateRange?: TimeRange) { - if (queryLanguage && !isValidQuery(query)) return; + const languageConfig = queryString.getLanguageService().getLanguage(language!); + if (languageConfig && !isValidQuery(query)) return; props.onChange({ query, dateRange: dateRange ?? getDateRange(), @@ -195,21 +190,12 @@ export default function QueryEditorTopRow(props: QueryEditorTopRowProps) { if (query && query.query) return true; } - function getQueryStringInitialValue(language: LanguageConfig) { - const input = language?.searchBar?.queryStringInput?.initialValue; - - if (!input) return ''; - - return input.replace('', dataset?.title ?? dataset?.title ?? ''); - } - function renderQueryEditor() { if (!shouldRenderQueryEditor()) return; return ( { await data.indexPatterns.ensureDefaultIndexPattern(); const defaultIndexPattern = await data.indexPatterns.getDefault(); - const datasetManager = data.query.queryString.getDatasetManager(); - datasetManager.fetchDefaultDataset(); - const defaultDataset = datasetManager.getDefaultDataset(); + // TODO: ROCKY do we need this? + // const queryString = data.query.queryString; + // const defaultDataset = queryString.getDatasetService().getDefault(); if (!isMounted) return; setIndexPatterns(defaultIndexPattern ? [defaultIndexPattern] : undefined); - if (defaultDataset) { - datasetManager.setDataset(defaultDataset); - } + // if (defaultDataset) { + // datasetManager.setDataset(defaultDataset); + // } }; initializeDataset(); diff --git a/src/plugins/query_enhancements/public/plugin.tsx b/src/plugins/query_enhancements/public/plugin.tsx index 58e2d2ae694..aead8dff3cc 100644 --- a/src/plugins/query_enhancements/public/plugin.tsx +++ b/src/plugins/query_enhancements/public/plugin.tsx @@ -16,7 +16,6 @@ import { QueryEnhancementsPluginStart, QueryEnhancementsPluginStartDependencies, } from './types'; -import { UI_SETTINGS } from '../common'; import { LanguageConfig } from '../../data/public'; import { s3TypeConfig } from './datasets'; @@ -40,6 +39,7 @@ export class QueryEnhancementsPlugin core: CoreSetup, { data }: QueryEnhancementsPluginSetupDependencies ): QueryEnhancementsPluginSetup { + const { queryString } = data.query; const pplSearchInterceptor = new PPLSearchInterceptor({ toasts: core.notifications.toasts, http: core.http, @@ -78,7 +78,7 @@ export class QueryEnhancementsPlugin showDocLinks: false, supportedAppNames: ['discover'], }; - data.query.queryString.registerLanguage(pplLanguageConfig); + queryString.getLanguageService().registerLanguage(pplLanguageConfig); // Register SQL language const sqlLanguageConfig: LanguageConfig = { @@ -99,7 +99,7 @@ export class QueryEnhancementsPlugin showDocLinks: false, supportedAppNames: ['discover'], }; - data.query.queryString.registerLanguage(sqlLanguageConfig); + queryString.getLanguageService().registerLanguage(sqlLanguageConfig); data.__enhance({ ui: { @@ -107,7 +107,7 @@ export class QueryEnhancementsPlugin }, }); - data.query.queryString.registerType(s3TypeConfig); + queryString.getDatasetService().registerType(s3TypeConfig); return {}; }