Skip to content

Commit

Permalink
Merge branch 'main' into filter-left-menu-based-on-current-workspace
Browse files Browse the repository at this point in the history
Signed-off-by: SuZhou-Joe <[email protected]>
  • Loading branch information
SuZhou-Joe authored Apr 7, 2024
2 parents fd2598d + a0eaf84 commit 036db0d
Show file tree
Hide file tree
Showing 38 changed files with 967 additions and 137 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- [Workspace] Add workspace id in basePath ([#6060](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6060))
- Implement new home page ([#6065](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6065))
- Add sidecar service ([#5920](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5920))
- Allow the use of `ignoreVersionMismatch` in non-dev configuration ([#6347](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6347))
- [Multiple Datasource] Use data source filter function before rendering ([#6175](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6175))
- [Chrome] Introduce registerCollapsibleNavHeader to allow plugins to customize the rendering of nav menu header ([#5244](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5244))
- [Dynamic Configurations] Pass request headers when making application config calls ([#6164](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6164))
Expand All @@ -69,14 +70,18 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- [Multiple Datasource] Refactor data source menu and interface to allow cleaner selection of component and related configurations ([#6256](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6256))
- [Multiple Datasource] Allow top nav menu to mount data source menu for use case when both menus are mounted ([#6268](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6268))
- [Workspace] Add create workspace page ([#6179](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6179))
- [Workspace] Add update workspace page ([#6270](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6270))
- [Multiple Datasource] Make sure customer always have a default datasource ([#6237](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6237))
- [Workspace] Add workspace list page ([#6182](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6182))
- [Workspace] Add workspaces column to saved objects page ([#6225](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6225))
- [Multiple Datasource] Enhanced data source selector with default datasource shows as first choice ([#6293](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6293))
- [Multiple Datasource] Add multi data source support to sample vega visualizations ([#6218](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6218))
- [Multiple Datasource] Fetch data source title for DataSourceView when only id is provided ([#6315](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6315)
- [Workspace] Add permission control logic ([#6052](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6052))
- [Multiple Datasource] Add default icon for selectable component and make sure the default datasource shows automatically ([#6327](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6327))
- [Multiple Datasource] Pass selected data sources to plugin consumers when the multi-select component initially loads ([#6333](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6333))
- [Multiple Datasource] Add installedPlugins list to data source saved object ([#6348](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6348))
- [Workspace] Add APIs to support plugin state in request ([#6303](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6303))
- [Workspace] Filter left nav menu items according to the current workspace ([#6234](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6234))

### 🐛 Bug Fixes
Expand Down Expand Up @@ -170,6 +175,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- [Discover] Enhanced the data source selector with added sorting functionality ([#5719](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5719))
- [Multiple Datasource] Add datasource picker component and use it in devtools and tutorial page when multiple datasource is enabled ([#5756](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5756))
- [Multiple Datasource] Add datasource picker to import saved object flyout when multiple data source is enabled ([#5781](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5781))
- [Discover] Add extension group title to non-index data source groups to indicate log explorer redirection in discover data source selector. ([#5815](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/5815))

### 🐛 Bug Fixes

Expand Down Expand Up @@ -1107,4 +1113,4 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

### 🔩 Tests

- Update caniuse to fix failed integration tests ([#2322](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2322))
- Update caniuse to fix failed integration tests ([#2322](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2322))
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,7 @@
"@types/react-router-dom": "^5.3.2",
"@types/react-virtualized": "^9.18.7",
"@types/recompose": "^0.30.6",
"@types/redux-mock-store": "^1.0.6",
"@types/selenium-webdriver": "^4.0.9",
"@types/semver": "^7.5.0",
"@types/sinon": "^7.0.13",
Expand Down Expand Up @@ -451,6 +452,7 @@
"react-test-renderer": "^16.12.0",
"reactcss": "1.2.3",
"redux": "^4.0.5",
"redux-mock-store": "^1.5.4",
"regenerate": "^1.4.0",
"reselect": "^4.0.0",
"resize-observer-polyfill": "^1.5.1",
Expand Down
16 changes: 2 additions & 14 deletions src/core/server/opensearch/opensearch_config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,19 +129,7 @@ export const configSchema = schema.object({
),
apiVersion: schema.string({ defaultValue: DEFAULT_API_VERSION }),
healthCheck: schema.object({ delay: schema.duration({ defaultValue: 2500 }) }),
ignoreVersionMismatch: schema.conditional(
schema.contextRef('dev'),
false,
schema.boolean({
validate: (rawValue) => {
if (rawValue === true) {
return '"ignoreVersionMismatch" can only be set to true in development mode';
}
},
defaultValue: false,
}),
schema.boolean({ defaultValue: false })
),
ignoreVersionMismatch: schema.boolean({ defaultValue: false }),
disablePrototypePoisoningProtection: schema.maybe(schema.boolean({ defaultValue: false })),
});

Expand Down Expand Up @@ -216,7 +204,7 @@ export class OpenSearchConfig {
public readonly healthCheckDelay: Duration;

/**
* Whether to allow opensearch-dashboards to connect to a non-compatible opensearch node.
* Whether to allow opensearch-dashboards to connect to an opensearch node of a different version.
*/
public readonly ignoreVersionMismatch: boolean;

Expand Down
1 change: 1 addition & 0 deletions src/core/server/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ export * from './from_root';
export * from './package_json';
export * from './streams';
export { getWorkspaceIdFromUrl, cleanWorkspaceId } from '../../utils';
export { updateWorkspaceState, getWorkspaceState } from './workspace';
19 changes: 19 additions & 0 deletions src/core/server/utils/workspace.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { httpServerMock } from '../mocks';
import { getWorkspaceState, updateWorkspaceState } from './workspace';

describe('updateWorkspaceState', () => {
it('update with payload', () => {
const requestMock = httpServerMock.createOpenSearchDashboardsRequest();
updateWorkspaceState(requestMock, {
requestWorkspaceId: 'foo',
});
expect(getWorkspaceState(requestMock)).toEqual({
requestWorkspaceId: 'foo',
});
});
});
36 changes: 36 additions & 0 deletions src/core/server/utils/workspace.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import { OpenSearchDashboardsRequest, ensureRawRequest } from '../http/router';

export interface WorkspaceState {
requestWorkspaceId?: string;
}

/**
* This function will be used as a proxy
* because `ensureRequest` is only importable from core module.
*
* @param workspaceId string
* @returns void
*/
export const updateWorkspaceState = (
request: OpenSearchDashboardsRequest,
payload: Partial<WorkspaceState>
) => {
const rawRequest = ensureRawRequest(request);

rawRequest.app = {
...rawRequest.app,
...payload,
};
};

export const getWorkspaceState = (request: OpenSearchDashboardsRequest): WorkspaceState => {
const { requestWorkspaceId } = ensureRawRequest(request).app as WorkspaceState;
return {
requestWorkspaceId,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,17 @@ import { DataSourceGroup, DataSourceSelectableProps } from './types';
type DataSourceTypeKey = 'DEFAULT_INDEX_PATTERNS' | 's3glue' | 'spark';

// Mapping between datasource type and its display name.
// Temporary solution, will be removed along with refactoring of data source APIs
const DATASOURCE_TYPE_DISPLAY_NAME_MAP: Record<DataSourceTypeKey, string> = {
DEFAULT_INDEX_PATTERNS: 'Index patterns',
s3glue: 'Amazon S3',
spark: 'Spark',
DEFAULT_INDEX_PATTERNS: i18n.translate('dataExplorer.dataSourceSelector.indexPatternGroupTitle', {
defaultMessage: 'Index patterns',
}),
s3glue: i18n.translate('dataExplorer.dataSourceSelector.amazonS3GroupTitle', {
defaultMessage: 'Amazon S3',
}),
spark: i18n.translate('dataExplorer.dataSourceSelector.sparkGroupTitle', {
defaultMessage: 'Spark',
}),
};

type DataSetType = ISourceDataSet['data_sets'][number];
Expand Down Expand Up @@ -67,7 +74,19 @@ const getSourceList = (allDataSets: ISourceDataSet[]) => {
const finalList = [] as DataSourceGroup[];
allDataSets.forEach((curDataSet) => {
const typeKey = curDataSet.ds.getType() as DataSourceTypeKey;
const groupName = DATASOURCE_TYPE_DISPLAY_NAME_MAP[typeKey] || 'Default Group';
let groupName =
DATASOURCE_TYPE_DISPLAY_NAME_MAP[typeKey] ||
i18n.translate('dataExplorer.dataSourceSelector.defaultGroupTitle', {
defaultMessage: 'Default Group',
});

// add '- Opens in Log Explorer' to hint user that selecting these types of data sources
// will lead to redirection to log explorer
if (typeKey !== 'DEFAULT_INDEX_PATTERNS') {
groupName = `${groupName}${i18n.translate('dataExplorer.dataSourceSelector.redirectionHint', {
defaultMessage: ' - Opens in Log Explorer',
})}`;
}

const existingGroup = finalList.find((item) => item.label === groupName);
const mappedOptions = curDataSet.data_sets.map((dataSet) =>
Expand Down
110 changes: 110 additions & 0 deletions src/plugins/data_explorer/public/components/sidebar/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react';
import { Sidebar } from './index'; // Adjust the import path as necessary
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import { MockS3DataSource } from '../../../../discover/public/__mock__/index.test.mock';
import { createMemoryHistory } from 'history';
import { Router } from 'react-router-dom';

const mockStore = configureMockStore();
const initialState = {
metadata: { indexPattern: 'some-index-pattern-id' },
};
const store = mockStore(initialState);

jest.mock('../../../../opensearch_dashboards_react/public', () => {
return {
toMountPoint: jest.fn().mockImplementation((component) => () => component),
useOpenSearchDashboards: jest.fn().mockReturnValue({
services: {
data: {
indexPatterns: {},
dataSources: {
dataSourceService: {
dataSources$: {
subscribe: jest.fn((callback) => {
callback({
's3-prod-mock': new MockS3DataSource({
name: 's3-prod-mock',
type: 's3glue',
metadata: {},
}),
});
return { unsubscribe: jest.fn() };
}),
},
},
},
},
notifications: {
toasts: {
addError: jest.fn(),
},
},
application: {
navigateToUrl: jest.fn(),
},
overlays: {
openConfirm: jest.fn(),
},
},
}),
withOpenSearchDashboards: () => (Component: React.ComponentClass) => (props: any) => (
<Component {...props} />
),
};
});

jest.mock('../../../../data_explorer/public', () => ({
useTypedSelector: jest.fn(),
useTypedDispatch: jest.fn(),
}));

describe('Sidebar Component', () => {
it('renders without crashing', () => {
const { container, getByTestId } = render(
<Provider store={store}>
<Sidebar />
</Provider>
);
expect(container).toBeInTheDocument();
expect(getByTestId('dataExplorerDSSelect')).toBeInTheDocument();
});

it('shows title extensions on the non-index pattern data source', () => {
const { getByText, getByTestId } = render(
<Provider store={store}>
<Sidebar />
</Provider>
);

fireEvent.click(getByTestId('comboBoxToggleListButton'));
waitFor(() => {
expect(getByText('Open in Log Explorer')).toBeInTheDocument();
});
});

it('redirects to log explorer when clicking open-in-log-explorer button', () => {
const history = createMemoryHistory();
const { getByText, getByTestId } = render(
<Provider store={store}>
<Router history={history}>
<Sidebar />
</Router>
</Provider>
);

fireEvent.click(getByTestId('comboBoxToggleListButton'));
waitFor(() => {
expect(getByText('s3-prod-mock')).toBeInTheDocument();
fireEvent.click(getByText('s3-prod-mock'));
expect(history.location.pathname).toContain('observability-logs#/explorer');
});
});
});
20 changes: 14 additions & 6 deletions src/plugins/data_explorer/public/components/sidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,23 +59,31 @@ export const Sidebar: FC = ({ children }) => {
}
}, [indexPatternId, activeDataSources, dataSourceOptionList]);

const redirectToLogExplorer = useCallback(
(dsName: string, dsType: string) => {
return application.navigateToUrl(
`../observability-logs#/explorer?datasourceName=${dsName}&datasourceType=${dsType}`
);
},
[application]
);

const handleSourceSelection = useCallback(
(selectedDataSources: DataSourceOption[]) => {
if (selectedDataSources.length === 0) {
setSelectedSources(selectedDataSources);
return;
}
// Temporary redirection solution for 2.11, where clicking non-index-pattern datasource
// will redirect user to Observability event explorer
// Temporary redirection solution for 2.11, where clicking non-index-pattern data sources
// will prompt users with modal explaining they are being redirected to Observability log explorer
if (selectedDataSources[0]?.ds?.getType() !== 'DEFAULT_INDEX_PATTERNS') {
return application.navigateToUrl(
`../observability-logs#/explorer?datasourceName=${selectedDataSources[0].label}&datasourceType=${selectedDataSources[0].type}`
);
redirectToLogExplorer(selectedDataSources[0].label, selectedDataSources[0].type);
return;
}
setSelectedSources(selectedDataSources);
dispatch(setIndexPattern(selectedDataSources[0].value));
},
[application, dispatch]
[dispatch, redirectToLogExplorer, setSelectedSources]
);

const handleGetDataSetError = useCallback(
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/data_source/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { DATA_SOURCE_SAVED_OBJECT_TYPE } from '../common';
import { ensureRawRequest } from '../../../../src/core/server/http/router';
import { createDataSourceError } from './lib/error';
import { registerTestConnectionRoute } from './routes/test_connection';
import { registerFetchDataSourceVersionRoute } from './routes/fetch_data_source_version';
import { registerFetchDataSourceMetaDataRoute } from './routes/fetch_data_source_metadata';
import { AuthenticationMethodRegistry, IAuthenticationMethodRegistry } from './auth_registry';
import { CustomApiSchemaRegistry } from './schema_registry';

Expand Down Expand Up @@ -134,7 +134,7 @@ export class DataSourcePlugin implements Plugin<DataSourcePluginSetup, DataSourc
authRegistryPromise,
customApiSchemaRegistryPromise
);
registerFetchDataSourceVersionRoute(
registerFetchDataSourceMetaDataRoute(
router,
dataSourceService,
cryptographyServiceSetup,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,38 @@ describe('DataSourceManagement: data_source_connection_validator.ts', () => {
expect(fetchDataSourcesVersionResponse).toBe('2.11.0');
});

test('fetchInstalledPlugins - Success: opensearch client response code is 200 and response body have installed plugin list', async () => {
const opensearchClient = opensearchServiceMock.createOpenSearchClient();
opensearchClient.info.mockResolvedValue(
opensearchServiceMock.createApiResponse({
statusCode: 200,
body: [
{
name: 'b40f6833d895d3a95333e325e8bea79b',
component: ' analysis-icu',
version: '2.11.0',
},
{
name: 'b40f6833d895d3a95333e325e8bea79b',
component: 'analysis-ik',
version: '2.11.0',
},
{
name: 'b40f6833d895d3a95333e325e8bea79b',
component: 'analysis-seunjeon',
version: '2.11.0',
},
],
})
);
const dataSourceValidator = new DataSourceConnectionValidator(opensearchClient, {});
const fetchInstalledPluginsReponse = Array.from(
await dataSourceValidator.fetchInstalledPlugins()
);
const installedPlugins = ['analysis-icu', 'analysis-ik', 'analysis-seunjeon'];
fetchInstalledPluginsReponse.map((plugin) => expect(installedPlugins).toContain(plugin));
});

test('failure: opensearch client response code is 200 but response body not have cluster name', async () => {
try {
const opensearchClient = opensearchServiceMock.createOpenSearchClient();
Expand Down
Loading

0 comments on commit 036db0d

Please sign in to comment.