Skip to content

Commit

Permalink
[Discover] Support data connections and multi-select table in dataset…
Browse files Browse the repository at this point in the history
… picker (#8255) (#8438)

* initial implementation of cloud watch data set type



* wip-- support selecting multiple datasets



* Revert "wip-- support selecting multiple datasets"

This reverts commit 4977cc9.



* add multi-select dataset table



* add token based pagination in dataset table



* display data connection in dataset title



* avoid overlap of dataset title and language selector



* Revert "avoid overlap of dataset title and language selector"

This reverts commit 87d599d.
Wait for #8204



* revert query enhancement changes for cloudwatch



* Changeset file for PR #8255 created/updated

* Revert "revert query enhancement changes for cloudwatch"

This reverts commit 5eb065a.



* address comments



* revert query enhancement changes for cloudwatch



* address comments



---------



(cherry picked from commit fd31398)

Signed-off-by: Joshua Li <[email protected]>
Signed-off-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: opensearch-changeset-bot[bot] <154024398+opensearch-changeset-bot[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Oct 2, 2024
1 parent 036496c commit d61dcea
Show file tree
Hide file tree
Showing 11 changed files with 348 additions and 49 deletions.
2 changes: 2 additions & 0 deletions changelogs/fragments/8255.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
feat:
- Support data connections and multi-select table in dataset picker ([#8255](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8255))
2 changes: 2 additions & 0 deletions src/plugins/data/common/datasets/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ export interface DataStructure {
/** Optional array of child data structures */
children?: DataStructure[];
hasNext?: boolean;
paginationToken?: string;
multiSelect?: boolean;
columnHeader?: string;
/** Optional metadata for the data structure */
meta?: DataStructureMeta;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
DataStorage,
CachedDataStructure,
} from '../../../../common';
import { DatasetTypeConfig } from './types';
import { DatasetTypeConfig, DataStructureFetchOptions } from './types';
import { indexPatternTypeConfig, indexTypeConfig } from './lib';
import { IndexPatternsContract } from '../../../index_patterns';
import { IDataPluginServices } from '../../../types';
Expand Down Expand Up @@ -91,22 +91,28 @@ export class DatasetService {
public async fetchOptions(
services: IDataPluginServices,
path: DataStructure[],
dataType: string
dataType: string,
options?: DataStructureFetchOptions
): Promise<DataStructure> {
const type = this.typesRegistry.get(dataType);
if (!type) {
throw new Error(`No handler found for type: ${dataType}`);
}

const lastPathItem = path[path.length - 1];
const cacheKey = `${dataType}.${lastPathItem.id}`;
const fetchOptionsKey = Object.entries(options || {})
.sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
.map(([key, value]) => `${key}=${value}`)
.join('&');
const cacheKey =
`${dataType}.${lastPathItem.id}` + (fetchOptionsKey.length ? `?${fetchOptionsKey}` : '');

const cachedDataStructure = this.sessionStorage.get<CachedDataStructure>(cacheKey);
if (cachedDataStructure?.children?.length > 0) {
return this.cacheToDataStructure(dataType, cachedDataStructure);
}

const fetchedDataStructure = await type.fetch(services, path);
const fetchedDataStructure = await type.fetch(services, path, options);
this.cacheDataStructure(dataType, fetchedDataStructure);
return fetchedDataStructure;
}
Expand Down Expand Up @@ -146,6 +152,8 @@ export class DatasetService {
parent: dataStructure.parent?.id || '',
children: dataStructure.children?.map((child) => child.id) || [],
hasNext: dataStructure.hasNext,
paginationToken: dataStructure.paginationToken,
multiSelect: dataStructure.multiSelect,
columnHeader: dataStructure.columnHeader,
meta: dataStructure.meta,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ import { EuiIconProps } from '@elastic/eui';
import { Dataset, DatasetField, DatasetSearchOptions, DataStructure } from '../../../../common';
import { IDataPluginServices } from '../../../types';

/**
* Options for fetching the data structure.
*/
export interface DataStructureFetchOptions {
/** Search string to filter results */
search?: string;
/** Token for paginated results */
paginationToken?: string;
}

/**
* Configuration for handling dataset operations.
*/
Expand All @@ -28,12 +38,17 @@ export interface DatasetTypeConfig {
*/
toDataset: (path: DataStructure[]) => Dataset;
/**
* Fetches child options for a given DataStructure.
* Fetches child data structures and populates corresponding properties for a given DataStructure.
* @param {IDataPluginServices} services - The data plugin services.
* @param {DataStructure} dataStructure - The parent DataStructure.
* @returns {Promise<DatasetHandlerFetchResponse>} A promise that resolves to a DatasetHandlerFetchResponse.
* @param {DataStructureFetchOptions} options - The fetch options for pagination and search.
* @returns {Promise<DataStructure>} A promise that resolves to the updated DataStructure.
*/
fetch: (services: IDataPluginServices, path: DataStructure[]) => Promise<DataStructure>;
fetch: (
services: IDataPluginServices,
path: DataStructure[],
options?: DataStructureFetchOptions
) => Promise<DataStructure>;
/**
* Fetches fields for the dataset.
* @returns {Promise<DatasetField[]>} A promise that resolves to an array of DatasetFields.
Expand Down
7 changes: 6 additions & 1 deletion src/plugins/data/public/query/query_string/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,12 @@
*/

export { QueryStringContract, QueryStringManager } from './query_string_manager';
export { DatasetServiceContract, DatasetService, DatasetTypeConfig } from './dataset_service';
export {
DataStructureFetchOptions,
DatasetService,
DatasetServiceContract,
DatasetTypeConfig,
} from './dataset_service';
export {
LanguageServiceContract,
LanguageService,
Expand Down
12 changes: 12 additions & 0 deletions src/plugins/data/public/ui/dataset_selector/_dataset_explorer.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

.datasetExplorer {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 240px)) minmax(300px, 1fr);
Expand Down Expand Up @@ -26,4 +31,11 @@
padding: $euiSizeS;
border-bottom: $euiBorderThin;
}

.datasetTable {
&__loadMore {
text-align: center;
padding: $euiSizeS;
}
}
}
5 changes: 5 additions & 0 deletions src/plugins/data/public/ui/dataset_selector/_index.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

@import "./dataset_explorer";
@import "./dataset_selector";
@import "./dataset_configurator";
101 changes: 61 additions & 40 deletions src/plugins/data/public/ui/dataset_selector/dataset_explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import {
import { FormattedMessage } from '@osd/i18n/react';
import moment from 'moment';
import { BaseDataset, DATA_STRUCTURE_META_TYPES, DataStructure } from '../../../common';
import { QueryStringContract } from '../../query';
import { DataStructureFetchOptions, QueryStringContract } from '../../query';
import { IDataPluginServices } from '../../types';
import { DatasetTable } from './dataset_table';

export const DatasetExplorer = ({
services,
Expand All @@ -44,12 +45,23 @@ export const DatasetExplorer = ({
const uiSettings = services.uiSettings;
const [explorerDataset, setExplorerDataset] = useState<BaseDataset | undefined>(undefined);
const [loading, setLoading] = useState<boolean>(false);
const datasetService = queryString.getDatasetService();

const selectDataStructure = async (item: DataStructure, newPath: DataStructure[]) => {
const fetchNextDataStructure = async (
nextPath: DataStructure[],
dataType: string,
options?: DataStructureFetchOptions
) => datasetService.fetchOptions(services, nextPath, dataType, options);

const selectDataStructure = async (item: DataStructure | undefined, newPath: DataStructure[]) => {
if (!item) {
setExplorerDataset(undefined);
return;
}
const lastPathItem = newPath[newPath.length - 1];
const nextPath = [...newPath, item];

const typeConfig = queryString.getDatasetService().getType(nextPath[1].id);
const typeConfig = datasetService.getType(nextPath[1].id);
if (!typeConfig) return;

if (!lastPathItem.hasNext) {
Expand All @@ -59,9 +71,7 @@ export const DatasetExplorer = ({
}

setLoading(true);
const nextDataStructure = await queryString
.getDatasetService()
.fetchOptions(services, nextPath, typeConfig.id);
const nextDataStructure = await fetchNextDataStructure(nextPath, typeConfig.id);
setLoading(false);

setPath([...newPath, nextDataStructure]);
Expand Down Expand Up @@ -161,41 +171,52 @@ export const DatasetExplorer = ({
<EuiTitle size="xxs" className="datasetExplorer__columnTitle">
<h3>{current.columnHeader}</h3>
</EuiTitle>
<EuiSelectable
options={(current.children || []).map((child) => ({
label: child.parent ? `${child.parent.title}::${child.title}` : child.title,
value: child.id,
prepend: child.meta?.type === DATA_STRUCTURE_META_TYPES.TYPE &&
child.meta?.icon && <EuiIcon {...child.meta.icon} />,
append: appendIcon(child),
checked: isChecked(child, index, path, explorerDataset),
}))}
onChange={(options) => {
const selected = options.find((option) => option.checked);
if (selected) {
const item = current.children?.find((child) => child.id === selected.value);
if (item) {
selectDataStructure(item, path.slice(0, index + 1));
{current.multiSelect ? (
<DatasetTable
path={path}
setPath={setPath}
index={index}
explorerDataset={explorerDataset}
selectDataStructure={selectDataStructure}
fetchNextDataStructure={fetchNextDataStructure}
/>
) : (
<EuiSelectable
options={(current.children || []).map((child) => ({
label: child.parent ? `${child.parent.title}::${child.title}` : child.title,
value: child.id,
prepend: child.meta?.type === DATA_STRUCTURE_META_TYPES.TYPE &&
child.meta?.icon && <EuiIcon {...child.meta.icon} />,
append: appendIcon(child),
checked: isChecked(child, index, path, explorerDataset),
}))}
onChange={(options) => {
const selected = options.find((option) => option.checked);
if (selected) {
const item = current.children?.find((child) => child.id === selected.value);
if (item) {
selectDataStructure(item, path.slice(0, index + 1));
}
}
}
}}
singleSelection
{...(isFinal && {
searchProps: {
compressed: true,
},
searchable: true,
})}
height="full"
className="datasetExplorer__selectable"
>
{(list, search) => (
<>
{isFinal && search}
{list}
</>
)}
</EuiSelectable>
}}
singleSelection
{...(isFinal && {
searchProps: {
compressed: true,
},
searchable: true,
})}
height="full"
className="datasetExplorer__selectable"
>
{(list, search) => (
<>
{isFinal && search}
{list}
</>
)}
</EuiSelectable>
)}
</div>
);
})}
Expand Down
Loading

0 comments on commit d61dcea

Please sign in to comment.