Skip to content
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

[Explorer] Data selector enhancement and refactoring adoptions #1759

Merged
merged 7 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions common/constants/data_sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,7 @@ export const ACCELERATION_AGGREGRATION_FUNCTIONS = [
];

export const SPARK_PARTITION_INFO = `# Partition Information`;
export const OBS_DEFAULT_CLUSTER = 'observability-default'; // prefix key for generating data source id for default cluster in data selector
export const OBS_S3_DATA_SOURCE = 'observability-s3'; // prefix key for generating data source id for s3 data sources in data selector
export const S3_DATA_SOURCE_GROUP_DISPLAY_NAME = 'Amazon S3'; // display group name for Amazon-managed-s3 data sources in data selector
export const S3_DATA_SOURCE_GROUP_SPARK_DISPLAY_NAME = 'OpenSearch Spark'; // display group name for OpenSearch-spark-s3 data sources in data selector
2 changes: 1 addition & 1 deletion common/constants/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ export const VISUALIZATION_ERROR = {
NO_METRIC: 'Invalid Metric MetaData',
};

export const S3_DATASOURCE_TYPE = 'S3_DATASOURCE';
export const S3_DATA_SOURCE_TYPE = 's3glue';
mengweieric marked this conversation as resolved.
Show resolved Hide resolved

export const ASYNC_QUERY_SESSION_ID = 'async-query-session-id';
export const ASYNC_QUERY_DATASOURCE_CACHE = 'async-query-catalog-cache';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@

import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
import { batch, useDispatch, useSelector } from 'react-redux';
import { htmlIdGenerator } from '@elastic/eui';
import { LogExplorerRouterContext } from '../..';
import {
DataSource,
DataSourceGroup,
DataSourceSelectable,
DataSourceType,
Expand All @@ -20,6 +22,7 @@
DEFAULT_DATA_SOURCE_TYPE,
DEFAULT_DATA_SOURCE_TYPE_NAME,
INDEX_URL_PARAM_KEY,
OBS_DEFAULT_CLUSTER,
OLLY_QUESTION_URL_PARAM_KEY,
QUERY_LANGUAGE,
} from '../../../../../common/constants/data_sources';
Expand Down Expand Up @@ -90,6 +93,20 @@
}
};

const getMatchedOption = (
dataSourceList: DataSourceGroup[],
dataSourceName: string,
dataSourceType: string
) => {
for (const dsGroup of dataSourceList) {
const matchedOption = dsGroup.options.find(
(item) => item.type === dataSourceType && item.name === dataSourceName
);
if (matchedOption !== undefined) return [matchedOption];
}
return [];
};

export const DataSourceSelection = ({ tabId }: { tabId: string }) => {
const { dataSources, http } = coreRefs;
const sqlService = new SQLService(http!);
Expand All @@ -99,7 +116,11 @@
const [activeDataSources, setActiveDataSources] = useState<DataSourceType[]>([]);
const [dataSourceOptionList, setDataSourceOptionList] = useState<DataSourceGroup[]>([]);
const [selectedSources, setSelectedSources] = useState<SelectedDataSource[]>(
getDataSourceState(explorerSearchMetadata.datasources)
getMatchedOption(
dataSourceOptionList,
explorerSearchMetadata.datasources[0].name,
explorerSearchMetadata.datasources[0].type
)
);

/**
Expand Down Expand Up @@ -149,8 +170,14 @@
};

useEffect(() => {
setSelectedSources(getDataSourceState(explorerSearchMetadata.datasources));
}, [explorerSearchMetadata.datasources]);
setSelectedSources(
getMatchedOption(
memorizedDataSourceOptionList,
explorerSearchMetadata.datasources[0].name,
explorerSearchMetadata.datasources[0].type
)
);
}, [explorerSearchMetadata.datasources, dataSourceOptionList]);

Check warning on line 180 in public/components/event_analytics/explorer/datasources/datasources_selection.tsx

View workflow job for this annotation

GitHub Actions / Lint

React Hook useEffect has a missing dependency: 'memorizedDataSourceOptionList'. Either include it or remove the dependency array

const handleDataSetFetchError = useCallback(() => {
return (error: Error) => {
Expand All @@ -162,25 +189,36 @@
* Subscribe to data source updates and manage the active data sources state.
*/
useEffect(() => {
const subscription = dataSources.dataSourceService.dataSources$.subscribe(
(currentDataSources) => {
const subscription = dataSources.dataSourceService
.getDataSources$()
.subscribe((currentDataSources: DataSource[]) => {
// temporary solution for 2.11 to render OpenSearch / default cluster for observability
// local indices and index patterns, while keep listing all index patterns for data explorer
// it filters the registered index pattern data sources in data plugin, and attach default cluster
// for all indices
setActiveDataSources([
new ObservabilityDefaultDataSource({
id: htmlIdGenerator(OBS_DEFAULT_CLUSTER)(DEFAULT_DATA_SOURCE_TYPE),
name: DEFAULT_DATA_SOURCE_NAME,
type: DEFAULT_DATA_SOURCE_TYPE,
metadata: null,
metadata: {
ui: {
label: DEFAULT_DATA_SOURCE_OBSERVABILITY_DISPLAY_NAME,
groupType: DEFAULT_DATA_SOURCE_OBSERVABILITY_DISPLAY_NAME,
selector: {
displayDatasetsAsSource: false, // when true, selector UI will render data sets with source by calling getDataSets()
},
},
},
}),
...Object.values(currentDataSources).filter((ds) => ds.type !== DEFAULT_DATA_SOURCE_TYPE),
...Object.values(currentDataSources).filter(
(ds) => ds.getType() !== DEFAULT_DATA_SOURCE_TYPE
),
]);
}
);
});

return () => subscription.unsubscribe();
}, []);

Check warning on line 221 in public/components/event_analytics/explorer/datasources/datasources_selection.tsx

View workflow job for this annotation

GitHub Actions / Lint

React Hook useEffect has a missing dependency: 'dataSources.dataSourceService'. Either include it or remove the dependency array

/**
* Check for URL parameters to update the data source if redirected from discover.
Expand Down Expand Up @@ -228,7 +266,7 @@
);
}
}
}, []);

Check warning on line 269 in public/components/event_analytics/explorer/datasources/datasources_selection.tsx

View workflow job for this annotation

GitHub Actions / Lint

React Hook useEffect has missing dependencies: 'dispatch', 'resetStateOnDataSourceChange', 'routerContext?.searchParams', and 'tabId'. Either include them or remove the dependency array

useEffect(() => {
// Execute a dummy query to initialize the cluster and obtain a sessionId for subsequent queries.
Expand All @@ -241,7 +279,7 @@
) {
runDummyQuery(dsName);
}
}, [explorerSearchMetadata.datasources]);

Check warning on line 282 in public/components/event_analytics/explorer/datasources/datasources_selection.tsx

View workflow job for this annotation

GitHub Actions / Lint

React Hook useEffect has a missing dependency: 'runDummyQuery'. Either include it or remove the dependency array

/**
* Process the data source options to display different than discover's group names.
Expand All @@ -259,6 +297,10 @@
});
}, [dataSourceOptionList]);

const onRefresh = useCallback(() => {
dataSources.dataSourceService.reload();
}, [dataSources.dataSourceService]);

return (
<DataSourceSelectable
className="dsc-selector"
Expand All @@ -267,9 +309,10 @@
setDataSourceOptionList={setDataSourceOptionList}
selectedSources={selectedSources}
onDataSourceSelect={handleSourceChange}
onFetchDataSetError={handleDataSetFetchError}
singleSelection={{ asPlainText: true }}
dataSourceSelectorConfigs={DATA_SOURCE_SELECTOR_CONFIGS}
onGetDataSetError={handleDataSetFetchError}
onRefresh={onRefresh}
/>
);
};
25 changes: 2 additions & 23 deletions public/components/event_analytics/explorer/log_explorer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { isEmpty } from 'lodash';
import React, { useContext, useEffect, useRef, useState } from 'react';
import { batch, useSelector, useDispatch } from 'react-redux';
import { useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { LogExplorerRouterContext } from '..';
import {
Expand All @@ -26,14 +26,6 @@ import {
} from '../../../../common/constants/shared';
import { coreRefs } from '../../../../public/framework/core_refs';

import { init as initFields } from '../../event_analytics/redux/slices/field_slice';
import { init as initPatterns } from '../../event_analytics/redux/slices/patterns_slice';
import { init as initQueryResult } from '../../event_analytics/redux/slices/query_result_slice';
import { init as initQuery } from '../../event_analytics/redux/slices/query_slice';
import { init as initVisualizationConfig } from '../../event_analytics/redux/slices/viualization_config_slice';
import { resetSummary as initQueryAssistSummary } from '../../event_analytics/redux/slices/query_assistant_summarization_slice';
import { init as initSearchMetaData } from '../../event_analytics/redux/slices/search_meta_data_slice';

const searchBarConfigs = {
[TAB_EVENT_ID]: {
showSaveButton: true,
Expand All @@ -60,7 +52,6 @@ export const LogExplorer = ({
dataSourcePluggables,
}: ILogExplorerProps) => {
const history = useHistory();
const dispatch = useDispatch();
const routerContext = useContext(LogExplorerRouterContext);
const tabIds = useSelector(selectQueryTabs).queryTabIds.filter(
(tabid: string) => !tabid.match(APP_ANALYTICS_TAB_ID_REGEX)
Expand Down Expand Up @@ -97,20 +88,8 @@ export const LogExplorer = ({
useEffect(() => {
if (!isEmpty(savedObjectId)) {
dispatchSavedObjectId();
} else {
// below piece of code was added to simulate creating a new tab if saved obj isn't being loaded,
// since tabs being visually removed means 'new tab' cannot be created any other way
const tabId = tabIds[0];
batch(() => {
dispatch(initQuery({ tabId }));
dispatch(initQueryResult({ tabId }));
dispatch(initFields({ tabId }));
dispatch(initVisualizationConfig({ tabId }));
dispatch(initPatterns({ tabId }));
dispatch(initQueryAssistSummary({ tabId }));
dispatch(initSearchMetaData({ tabId }));
});
}

if (routerContext && routerContext.searchParams.has(CREATE_TAB_PARAM_KEY)) {
// need to wait for current redux event loop to finish
setImmediate(() => {
Expand Down
9 changes: 5 additions & 4 deletions public/framework/datasources/obs_opensearch_datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,25 +6,26 @@
import { DataSource } from '../../../../../src/plugins/data/public';

interface DataSourceConfig {
id: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a list of labels (string array) for future filtering and association support ?

name: string;
type: string;
metadata: any;

Check warning on line 12 in public/framework/datasources/obs_opensearch_datasource.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
}

export class ObservabilityDefaultDataSource extends DataSource<any, any, any, any, any> {

Check warning on line 15 in public/framework/datasources/obs_opensearch_datasource.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check warning on line 15 in public/framework/datasources/obs_opensearch_datasource.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check warning on line 15 in public/framework/datasources/obs_opensearch_datasource.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check warning on line 15 in public/framework/datasources/obs_opensearch_datasource.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type

Check warning on line 15 in public/framework/datasources/obs_opensearch_datasource.ts

View workflow job for this annotation

GitHub Actions / Lint

Unexpected any. Specify a different type
constructor({ name, type, metadata }: DataSourceConfig) {
super(name, type, metadata);
constructor({ id, name, type, metadata }: DataSourceConfig) {
super({ id, name, type, metadata });
}

async getDataSet(dataSetParams?: any) {
async getDataSet() {
return ['Default data source'];
}

async testConnection(): Promise<boolean> {
return true;
}

async runQuery(queryParams: any) {
async runQuery() {
return null;
}
}
19 changes: 10 additions & 9 deletions public/framework/datasources/s3_datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,23 @@ interface DataSourceConfig {
name: string;
type: string;
metadata: any;
id: string;
}

export class S3DataSource extends DataSource<any, any, any, any, any> {
constructor({ name, type, metadata }: DataSourceConfig) {
super(name, type, metadata);
export class S3DataSource extends DataSource {
constructor({ id, name, type, metadata }: DataSourceConfig) {
super({ id, name, type, metadata });
}

async getDataSet(dataSetParams?: any) {
return [this.getName()];
async getDataSet() {
return { dataSets: [this.getName()] };
}

async testConnection(): Promise<void> {
throw new Error('This operation is not supported for this class.');
async testConnection(): Promise<boolean> {
return true;
}

async runQuery(queryParams: any) {
return null;
async runQuery() {
return { data: {} };
}
}
43 changes: 34 additions & 9 deletions public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { i18n } from '@osd/i18n';
import React from 'react';
import { i18n } from '@osd/i18n';
import { htmlIdGenerator } from '@elastic/eui';
import {
AppCategory,
AppMountParameters,
Expand Down Expand Up @@ -46,7 +47,7 @@ import {
observabilityTracesID,
observabilityTracesPluginOrder,
observabilityTracesTitle,
S3_DATASOURCE_TYPE,
S3_DATA_SOURCE_TYPE,
} from '../common/constants/shared';
import { QueryManager } from '../common/query_manager';
import { AssociatedObject, CachedAcceleration } from '../common/types/data_connections';
Expand Down Expand Up @@ -95,6 +96,12 @@ import {
ObservabilityStart,
SetupDependencies,
} from './types';
import {
DATA_SOURCE_TYPES,
OBS_S3_DATA_SOURCE,
S3_DATA_SOURCE_GROUP_DISPLAY_NAME,
S3_DATA_SOURCE_GROUP_SPARK_DISPLAY_NAME,
} from '../common/constants/data_sources';

interface PublicConfig {
query_assist: {
Expand Down Expand Up @@ -387,38 +394,56 @@ export class ObservabilityPlugin
coreRefs.overlays = core.overlays;

const { dataSourceService, dataSourceFactory } = startDeps.data.dataSources;
dataSourceFactory.registerDataSourceType(S3_DATA_SOURCE_TYPE, S3DataSource);

// register all s3 datasources
const registerS3Datasource = () => {
dataSourceFactory.registerDataSourceType(S3_DATASOURCE_TYPE, S3DataSource);
const registerDataSources = () => {
core.http.get(`${DATACONNECTIONS_BASE}`).then((s3DataSources) => {
s3DataSources.map((s3ds) => {
dataSourceService.registerDataSource(
dataSourceFactory.getDataSourceInstance(S3_DATASOURCE_TYPE, {
dataSourceFactory.getDataSourceInstance(S3_DATA_SOURCE_TYPE, {
id: htmlIdGenerator(OBS_S3_DATA_SOURCE)(DATA_SOURCE_TYPES.S3Glue),
name: s3ds.name,
type: s3ds.connector.toLowerCase(),
metadata: s3ds,
metadata: {
...s3ds.properties,
ui: {
label: s3ds.name,
typeLabel:
s3ds.connector.toLowerCase() === DATA_SOURCE_TYPES.S3Glue
? S3_DATA_SOURCE_GROUP_DISPLAY_NAME
: S3_DATA_SOURCE_GROUP_SPARK_DISPLAY_NAME,
groupType: s3ds.connector.toLowerCase(),
selector: {
displayDatasetsAsSource: false,
},
},
},
})
);
});
});
};

dataSourceService.registerDataSourceFetchers([
{ type: S3_DATA_SOURCE_TYPE, registerDataSources },
]);

if (startDeps.securityDashboards) {
core.http
.get(SECURITY_PLUGIN_ACCOUNT_API)
.then(() => {
registerS3Datasource();
registerDataSources();
})
.catch((e) => {
if (e?.response?.status !== 401) {
// accounts api should not return any error status other than 401 if security installed,
// this datasource register is included just in case
registerS3Datasource();
registerDataSources();
}
});
} else {
registerS3Datasource();
registerDataSources();
}

core.http.intercept({
Expand Down
Loading