From a672ee1672ec199c3a94b5dfe37c53e1631099fa Mon Sep 17 00:00:00 2001 From: Ashwin P Chandran Date: Mon, 10 Oct 2022 16:41:14 -0700 Subject: [PATCH 1/2] Updates functional test readme (#2492) Updates the Readme to call out the change in functional testing for OpenSearch Dashboards Issue resolved: https://github.com/opensearch-project/OpenSearch-Dashboards/issues/2462 Signed-off-by: Ashwin P Chandran --- CHANGELOG.md | 1 + TESTING.md | 3 +++ 2 files changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 747ef858cb9c..f093d3d95278 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * Remove extra typo from README. ([#2403](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2403)) * Add sample config for multi data source feature in yml template. ([#2428](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2428)) * README.md for dataSource and dataSourceManagement Plugin ([#2448](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2448)) +* Updates functionl testing information in Testing.md ([#2492](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2492)) ### 🛠 Maintenance diff --git a/TESTING.md b/TESTING.md index d93475e4b317..68077547ef88 100644 --- a/TESTING.md +++ b/TESTING.md @@ -43,6 +43,9 @@ To run specific integration tests, pass the path to the test: `yarn test:jest_integration [test path]` ### Functional tests + +Functional testing in OpenSearch Dashboards is migrating to the [opensearch-dashboards-functional-test](https://github.com/opensearch-project/opensearch-dashboards-functional-test) repository. All new functional tests should be written there. When modifying a file that affects an existing functional test, the old test should be migrated to the new repository. The rest of this section outlines how to run the existing functional tests in the repository. + To run all functional tests: `yarn test:ftr` To run specific functional tests, you can run by CI group: From 2600196224214fac153c4f0380059bb2a48a8ce7 Mon Sep 17 00:00:00 2001 From: Kristen Tian <105667444+kristenTian@users.noreply.github.com> Date: Mon, 10 Oct 2022 17:32:36 -0700 Subject: [PATCH 2/2] Add column service to index pattern & Register data source column (#2542) Signed-off-by: Kristen Tian Signed-off-by: Kristen Tian --- CHANGELOG.md | 4 +- src/plugins/data_source/public/index.ts | 6 +- src/plugins/data_source/public/plugin.ts | 9 +-- src/plugins/data_source/public/types.ts | 4 +- .../opensearch_dashboards.json | 2 +- .../data_source_column/data_source_column.tsx | 73 +++++++++++++++++++ .../data_source_management/public/plugin.ts | 15 +++- .../__snapshots__/utils.test.ts.snap | 2 + .../index_pattern_table.tsx | 21 +++++- .../public/components/utils.ts | 4 + .../index_pattern_management/public/index.ts | 2 +- .../mount_management_section.tsx | 1 - .../service/column_service/column_service.ts | 41 +++++++++++ .../public/service/column_service/index.ts | 6 ++ .../index_pattern_management_service.ts | 5 ++ .../index_pattern_management/public/types.ts | 14 ++++ 16 files changed, 193 insertions(+), 16 deletions(-) create mode 100644 src/plugins/data_source_management/public/components/data_source_column/data_source_column.tsx create mode 100644 src/plugins/index_pattern_management/public/service/column_service/column_service.ts create mode 100644 src/plugins/index_pattern_management/public/service/column_service/index.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index f093d3d95278..eafd642ad858 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,9 +13,11 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * [MD] Support legacy client for data source ([#2204](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2204)) * [Plugin Helpers] Facilitate version changes ([#2398](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2398)) * [MD] Display error toast for create index pattern with data source ([#2506](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2506)) -* [Multi DataSource] UX enhacement on index pattern management stack ([#2505](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2505)) +* [Multi DataSource] UX enhancement on index pattern management stack ([#2505](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2505)) * [Multi DataSource] UX enhancement on Data source management stack ([#2521](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2521)) * [Multi DataSource] UX enhancement on Index Pattern management stack ([#2527](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2527)) +* [Multi DataSource] Add data source column into index pattern table ([#2542](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2542)) + ### 🐛 Bug Fixes * [Vis Builder] Fixes auto bounds for timeseries bar chart visualization ([2401](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2401)) * [Vis Builder] Fixes visualization shift when editing agg ([2401](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/2401)) diff --git a/src/plugins/data_source/public/index.ts b/src/plugins/data_source/public/index.ts index 411838d0b1bd..b69e07784ff4 100644 --- a/src/plugins/data_source/public/index.ts +++ b/src/plugins/data_source/public/index.ts @@ -3,12 +3,12 @@ * SPDX-License-Identifier: Apache-2.0 */ -import { DataSourcePublicPlugin } from './plugin'; +import { DataSourcePlugin } from './plugin'; // This exports static code and TypeScript types, // as well as, OpenSearch Dashboards Platform `plugin()` initializer. export function plugin() { - return new DataSourcePublicPlugin(); + return new DataSourcePlugin(); } -export { DataSourcePublicPluginSetup, DataSourcePublicPluginStart } from './types'; +export { DataSourcePluginSetup, DataSourcePluginStart } from './types'; diff --git a/src/plugins/data_source/public/plugin.ts b/src/plugins/data_source/public/plugin.ts index a84091be1d9a..672db78e15cb 100644 --- a/src/plugins/data_source/public/plugin.ts +++ b/src/plugins/data_source/public/plugin.ts @@ -4,15 +4,14 @@ */ import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; -import { DataSourcePublicPluginSetup, DataSourcePublicPluginStart } from './types'; +import { DataSourcePluginSetup, DataSourcePluginStart } from './types'; -export class DataSourcePublicPlugin - implements Plugin { - public setup(core: CoreSetup): DataSourcePublicPluginSetup { +export class DataSourcePlugin implements Plugin { + public setup(core: CoreSetup): DataSourcePluginSetup { return {}; } - public start(core: CoreStart): DataSourcePublicPluginStart { + public start(core: CoreStart): DataSourcePluginStart { return {}; } diff --git a/src/plugins/data_source/public/types.ts b/src/plugins/data_source/public/types.ts index 95bab57ed148..0c57dd1ea1bb 100644 --- a/src/plugins/data_source/public/types.ts +++ b/src/plugins/data_source/public/types.ts @@ -4,7 +4,7 @@ */ // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DataSourcePublicPluginSetup {} +export interface DataSourcePluginSetup {} // eslint-disable-next-line @typescript-eslint/no-empty-interface -export interface DataSourcePublicPluginStart {} +export interface DataSourcePluginStart {} diff --git a/src/plugins/data_source_management/opensearch_dashboards.json b/src/plugins/data_source_management/opensearch_dashboards.json index 39877108d9c3..e5b13f6c0a1f 100644 --- a/src/plugins/data_source_management/opensearch_dashboards.json +++ b/src/plugins/data_source_management/opensearch_dashboards.json @@ -3,7 +3,7 @@ "version": "opensearchDashboards", "server": false, "ui": true, - "requiredPlugins": ["management", "dataSource"], + "requiredPlugins": ["management", "dataSource", "indexPatternManagement"], "optionalPlugins": [], "requiredBundles": ["opensearchDashboardsReact"] } diff --git a/src/plugins/data_source_management/public/components/data_source_column/data_source_column.tsx b/src/plugins/data_source_management/public/components/data_source_column/data_source_column.tsx new file mode 100644 index 000000000000..6ac2258b7811 --- /dev/null +++ b/src/plugins/data_source_management/public/components/data_source_column/data_source_column.tsx @@ -0,0 +1,73 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { i18n } from '@osd/i18n'; +import { HttpStart, SavedObjectsStart } from 'opensearch-dashboards/public'; +import { EuiBadge, EuiLink } from '@elastic/eui'; +import React from 'react'; +import { + IndexPatternTableColumn, + IndexPatternTableRecord, +} from '../../../../index_pattern_management/public'; +import { getDataSources } from '../utils'; +import { DataSourceTableItem } from '../../types'; + +type DataSourceColumnItem = DataSourceTableItem & { relativeUrl: string }; +type DataSourceMap = Map | undefined; + +export class DataSourceColumn implements IndexPatternTableColumn { + public readonly id: string = 'data_source'; + public data: DataSourceMap; + + public euiColumn = { + field: 'referenceId', + name: i18n.translate('dataSource.management.dataSourceColumn', { + defaultMessage: 'Data Source', + }), + render: (referenceId: string, index: IndexPatternTableRecord) => { + if (!referenceId) { + return null; + } + + const dataSource = this.data?.get(referenceId); + if (!dataSource) { + // Index pattern has the referenceId but data source not found. + return Deleted; + } + + const { title, relativeUrl } = dataSource; + return {title}; + }, + }; + + constructor( + private readonly savedObjectPromise: Promise, + private readonly httpPromise: Promise + ) {} + + public loadData = async () => { + const savedObject = await this.savedObjectPromise; + const { basePath } = await this.httpPromise; + + return getDataSources(savedObject.client).then((dataSources?: DataSourceTableItem[]) => { + this.data = dataSources + ?.map((dataSource) => { + return { + ...dataSource, + relativeUrl: basePath.prepend( + `/app/management/opensearch-dashboards/dataSources/${encodeURIComponent( + dataSource.id + )}` + ), + }; + }) + ?.reduce( + (map, dataSource) => map.set(dataSource.id, dataSource), + new Map() + ); + return this.data; + }); + }; +} diff --git a/src/plugins/data_source_management/public/plugin.ts b/src/plugins/data_source_management/public/plugin.ts index 159295a5a808..9f22ac7d9bb8 100644 --- a/src/plugins/data_source_management/public/plugin.ts +++ b/src/plugins/data_source_management/public/plugin.ts @@ -8,22 +8,35 @@ import { CoreSetup, CoreStart, Plugin } from '../../../core/public'; import { PLUGIN_NAME } from '../common'; import { ManagementSetup } from '../../management/public'; +import { IndexPatternManagementSetup } from '../../index_pattern_management/public'; +import { DataSourceColumn } from './components/data_source_column/data_source_column'; export interface DataSourceManagementSetupDependencies { management: ManagementSetup; + indexPatternManagement: IndexPatternManagementSetup; } const DSM_APP_ID = 'dataSources'; export class DataSourceManagementPlugin implements Plugin { - public setup(core: CoreSetup, { management }: DataSourceManagementSetupDependencies) { + public setup( + core: CoreSetup, + { management, indexPatternManagement }: DataSourceManagementSetupDependencies + ) { const opensearchDashboardsSection = management.sections.section.opensearchDashboards; if (!opensearchDashboardsSection) { throw new Error('`opensearchDashboards` management section not found.'); } + const savedObjectPromise = core + .getStartServices() + .then(([coreStart]) => coreStart.savedObjects); + const httpPromise = core.getStartServices().then(([coreStart]) => coreStart.http); + const column = new DataSourceColumn(savedObjectPromise, httpPromise); + indexPatternManagement.columns.register(column); + opensearchDashboardsSection.registerApp({ id: DSM_APP_ID, title: PLUGIN_NAME, diff --git a/src/plugins/index_pattern_management/public/components/__snapshots__/utils.test.ts.snap b/src/plugins/index_pattern_management/public/components/__snapshots__/utils.test.ts.snap index 224a5c992d58..287e24f056b4 100644 --- a/src/plugins/index_pattern_management/public/components/__snapshots__/utils.test.ts.snap +++ b/src/plugins/index_pattern_management/public/components/__snapshots__/utils.test.ts.snap @@ -5,6 +5,7 @@ Array [ Object { "default": true, "id": "test", + "referenceId": undefined, "sort": "0test name", "tags": undefined, "title": "test name", @@ -12,6 +13,7 @@ Array [ Object { "default": false, "id": "test1", + "referenceId": undefined, "sort": "1test name 1", "tags": undefined, "title": "test name 1", diff --git a/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx b/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx index c19c9f5b0c2d..85b47b3a1ff8 100644 --- a/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx +++ b/src/plugins/index_pattern_management/public/components/index_pattern_table/index_pattern_table.tsx @@ -44,7 +44,7 @@ import { FormattedMessage } from '@osd/i18n/react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; import React, { useState, useEffect } from 'react'; import { i18n } from '@osd/i18n'; -import { useMount } from 'react-use'; +import { useEffectOnce, useMount } from 'react-use'; import { reactRouterNavigate, useOpenSearchDashboards, @@ -112,6 +112,9 @@ export const IndexPatternTable = ({ canSave, history }: Props) => { const [remoteClustersExist, setRemoteClustersExist] = useState(false); const [isLoadingSources, setIsLoadingSources] = useState(!dataSourceEnabled); const [isLoadingIndexPatterns, setIsLoadingIndexPatterns] = useState(true); + const [isColumnDataLoaded, setIsColumnDataLoaded] = useState(false); + + const { columns: columnRegistry } = indexPatternManagementStart; useMount(() => { setBreadcrumbs(getListBreadcrumbs()); @@ -153,6 +156,11 @@ export const IndexPatternTable = ({ canSave, history }: Props) => { ); }; + const loadColumnData = async () => { + await Promise.all(columnRegistry.getAll().map((column) => column.loadData())); + setIsColumnDataLoaded(true); + }; + useEffect(() => { if (!dataSourceEnabled) { getIndices({ http, pattern: '*', searchClient }).then((dataSources) => { @@ -165,6 +173,10 @@ export const IndexPatternTable = ({ canSave, history }: Props) => { } }, [http, creationOptions, searchClient, dataSourceEnabled]); + useEffectOnce(() => { + loadColumnData(); + }); + chrome.docTitle.change(title); const columns = [ @@ -197,6 +209,13 @@ export const IndexPatternTable = ({ canSave, history }: Props) => { dataType: 'string' as const, sortable: ({ sort }: { sort: string }) => sort, }, + ...columnRegistry.getAll().map((column) => { + return { + ...column.euiColumn, + sortable: false, + 'data-test-subj': `indexPatternTableColumn-${column.id}`, + }; + }), ]; const createButton = canSave ? ( diff --git a/src/plugins/index_pattern_management/public/components/utils.ts b/src/plugins/index_pattern_management/public/components/utils.ts index 3929563fdc1c..cb7edcdb99ff 100644 --- a/src/plugins/index_pattern_management/public/components/utils.ts +++ b/src/plugins/index_pattern_management/public/components/utils.ts @@ -50,18 +50,22 @@ export async function getIndexPatterns( .map((pattern) => { const id = pattern.id; const title = pattern.get('title'); + const references = pattern.references; const isDefault = defaultIndex === id; const tags = (indexPatternManagementStart as IndexPatternManagementStart).list.getIndexPatternTags( pattern, isDefault ); + const reference = Array.isArray(references) ? references[0] : undefined; + const referenceId = reference?.id; return { id, title, default: isDefault, tags, + referenceId, // the prepending of 0 at the default pattern takes care of prioritization // so the sorting will but the default index on top // or on bottom of a the table diff --git a/src/plugins/index_pattern_management/public/index.ts b/src/plugins/index_pattern_management/public/index.ts index 5df23bc6e346..55994b0ad334 100644 --- a/src/plugins/index_pattern_management/public/index.ts +++ b/src/plugins/index_pattern_management/public/index.ts @@ -55,4 +55,4 @@ export { export { DefaultFormatEditor } from './components/field_editor/components/field_format_editor'; -export { MlCardState } from './types'; +export { MlCardState, IndexPatternTableColumn, IndexPatternTableRecord } from './types'; diff --git a/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx b/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx index 8a9c1dc1e7d1..162d1d0876c6 100644 --- a/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx +++ b/src/plugins/index_pattern_management/public/management_app/mount_management_section.tsx @@ -36,7 +36,6 @@ import { i18n } from '@osd/i18n'; import { I18nProvider } from '@osd/i18n/react'; import { StartServicesAccessor } from 'src/core/public'; -import { EuiIconType } from '@elastic/eui/src/components/icon/icon'; import { OpenSearchDashboardsContextProvider } from '../../../opensearch_dashboards_react/public'; import { ManagementAppMountParams } from '../../../management/public'; import { diff --git a/src/plugins/index_pattern_management/public/service/column_service/column_service.ts b/src/plugins/index_pattern_management/public/service/column_service/column_service.ts new file mode 100644 index 000000000000..9bd1812d6107 --- /dev/null +++ b/src/plugins/index_pattern_management/public/service/column_service/column_service.ts @@ -0,0 +1,41 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +import { IndexPatternTableColumn } from '../../types'; + +export interface IndexPatternTableColumnServiceSetup { + /** + * register given column in the registry. + */ + register: (column: IndexPatternTableColumn) => void; +} + +export interface IndexPatternTableColumnServiceStart { + /** + * return all {@link IndexPatternTableColumn | columns} currently registered. + */ + getAll: () => Array>; +} + +export class IndexPatternTableColumnService { + private readonly columns = new Map>(); + + setup(): IndexPatternTableColumnServiceSetup { + return { + register: (column) => { + if (this.columns.has(column.id)) { + throw new Error(`Index Pattern Table Column with id '${column.id}' already exists`); + } + this.columns.set(column.id, column); + }, + }; + } + + start(): IndexPatternTableColumnServiceStart { + return { + getAll: () => [...this.columns.values()], + }; + } +} diff --git a/src/plugins/index_pattern_management/public/service/column_service/index.ts b/src/plugins/index_pattern_management/public/service/column_service/index.ts new file mode 100644 index 000000000000..0af136011372 --- /dev/null +++ b/src/plugins/index_pattern_management/public/service/column_service/index.ts @@ -0,0 +1,6 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +export * from './column_service'; diff --git a/src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts b/src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts index 784728bb8355..270e7dfe6d9a 100644 --- a/src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts +++ b/src/plugins/index_pattern_management/public/service/index_pattern_management_service.ts @@ -47,6 +47,7 @@ import { TruncateFormatEditor, UrlFormatEditor, } from '../components/field_editor/components/field_format_editor'; +import { IndexPatternTableColumnService } from './column_service'; interface SetupDependencies { httpClient: HttpSetup; @@ -62,12 +63,14 @@ export class IndexPatternManagementService { indexPatternListConfig: IndexPatternListManager; fieldFormatEditors: FieldFormatEditors; environmentService: EnvironmentService; + columnService: IndexPatternTableColumnService; constructor() { this.indexPatternCreationManager = new IndexPatternCreationManager(); this.indexPatternListConfig = new IndexPatternListManager(); this.fieldFormatEditors = new FieldFormatEditors(); this.environmentService = new EnvironmentService(); + this.columnService = new IndexPatternTableColumnService(); } public setup({ httpClient }: SetupDependencies) { @@ -98,6 +101,7 @@ export class IndexPatternManagementService { list: indexPatternListConfigSetup, fieldFormatEditors: fieldFormatEditorsSetup, environment: this.environmentService.setup(), + columns: this.columnService.setup(), }; } @@ -106,6 +110,7 @@ export class IndexPatternManagementService { creation: this.indexPatternCreationManager.start(), list: this.indexPatternListConfig.start(), fieldFormatEditors: this.fieldFormatEditors.start(), + columns: this.columnService.start(), }; } diff --git a/src/plugins/index_pattern_management/public/types.ts b/src/plugins/index_pattern_management/public/types.ts index bc96fc15c8ef..24dc2cd59c31 100644 --- a/src/plugins/index_pattern_management/public/types.ts +++ b/src/plugins/index_pattern_management/public/types.ts @@ -40,6 +40,7 @@ import { SavedObjectReference, } from 'src/core/public'; import { DataPublicPluginStart } from 'src/plugins/data/public'; +import { EuiTableFieldDataColumnType } from '@elastic/eui'; import { ManagementAppMountParams } from '../../management/public'; import { IndexPatternManagementStart } from './index'; import { OpenSearchDashboardsReactContextValue } from '../../opensearch_dashboards_react/public'; @@ -71,3 +72,16 @@ export enum MlCardState { } export type DataSourceRef = { title: string } & Pick; + +export interface IndexPatternTableRecord { + type: string; + id: string; + referenceId?: string; +} + +export interface IndexPatternTableColumn { + id: string; + euiColumn: Omit, 'sortable'>; + data?: T; + loadData: () => Promise; +}