From 4d771c328f3bf4b20e1faee67538cb74b66edd88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alejandro=20Fern=C3=A1ndez=20G=C3=B3mez?= Date: Tue, 18 Jan 2022 16:40:33 +0100 Subject: [PATCH] [Unified observability] Extract `ObservabilityIndexPatterns` from exploratory view (#122132) --- .../exploratory_view/embeddable/index.tsx | 6 +- .../exploratory_view.test.tsx | 6 +- .../hooks/use_app_index_pattern.tsx | 6 +- .../utils/observability_data_views/index.ts | 8 +++ .../observability_data_views.test.ts} | 24 +++---- .../observability_data_views.ts} | 69 ++++++++++--------- 6 files changed, 64 insertions(+), 55 deletions(-) create mode 100644 x-pack/plugins/observability/public/utils/observability_data_views/index.ts rename x-pack/plugins/observability/public/{components/shared/exploratory_view/utils/observability_index_patterns.test.ts => utils/observability_data_views/observability_data_views.test.ts} (81%) rename x-pack/plugins/observability/public/{components/shared/exploratory_view/utils/observability_index_patterns.ts => utils/observability_data_views/observability_data_views.ts} (63%) diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx index e68ddfe55e6f50..7b0e2d10c0d3bc 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/embeddable/index.tsx @@ -9,7 +9,7 @@ import React, { useCallback, useEffect, useState } from 'react'; import { EuiLoadingSpinner } from '@elastic/eui'; import { CoreStart } from 'kibana/public'; import type { ExploratoryEmbeddableProps, ExploratoryEmbeddableComponentProps } from './embeddable'; -import { ObservabilityIndexPatterns } from '../utils/observability_index_patterns'; +import { ObservabilityDataViews } from '../../../../utils/observability_data_views'; import { ObservabilityPublicPluginsStart } from '../../../../plugin'; import type { IndexPatternState } from '../hooks/use_app_index_pattern'; import { EuiThemeProvider } from '../../../../../../../../src/plugins/kibana_react/common'; @@ -43,8 +43,8 @@ export function getExploratoryViewEmbeddable( setLoading(true); try { - const obsvIndexP = new ObservabilityIndexPatterns(plugins.data); - const indPattern = await obsvIndexP.getIndexPattern( + const obsvIndexP = new ObservabilityDataViews(plugins.data); + const indPattern = await obsvIndexP.getDataView( dataType, dataTypesIndexPatterns?.[dataType] ); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx index 87ae00b3c72e75..458bcc0ea4a5f9 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/exploratory_view.test.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { screen } from '@testing-library/dom'; import { render, mockAppIndexPattern } from './rtl_helpers'; import { ExploratoryView } from './exploratory_view'; -import * as obsvInd from './utils/observability_index_patterns'; +import * as obsvDataViews from '../../../utils/observability_data_views/observability_data_views'; import * as pluginHook from '../../../hooks/use_plugin_context'; import { createStubIndexPattern } from '../../../../../../../src/plugins/data/common/stubs'; @@ -40,8 +40,8 @@ describe('ExploratoryView', () => { }, }); - jest.spyOn(obsvInd, 'ObservabilityIndexPatterns').mockReturnValue({ - getIndexPattern: jest.fn().mockReturnValue(indexPattern), + jest.spyOn(obsvDataViews, 'ObservabilityDataViews').mockReturnValue({ + getDataView: jest.fn().mockReturnValue(indexPattern), } as any); }); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx index d9c1c28a6a727a..c865d8e6dca5e0 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx +++ b/x-pack/plugins/observability/public/components/shared/exploratory_view/hooks/use_app_index_pattern.tsx @@ -11,7 +11,7 @@ import { IndexPattern } from '../../../../../../../../src/plugins/data/common'; import { AppDataType } from '../types'; import { useKibana } from '../../../../../../../../src/plugins/kibana_react/public'; import { ObservabilityPublicPluginsStart } from '../../../../plugin'; -import { ObservabilityIndexPatterns } from '../utils/observability_index_patterns'; +import { ObservabilityDataViews } from '../../../../utils/observability_data_views'; import { getDataHandler } from '../../../../data_handler'; import { useExploratoryView } from '../contexts/exploratory_view_config'; import { DataViewInsufficientAccessError } from '../../../../../../../../src/plugins/data_views/common'; @@ -83,8 +83,8 @@ export function IndexPatternContextProvider({ children }: ProviderProps) { setHasAppData((prevState) => ({ ...prevState, [dataType]: hasDataT })); if (hasDataT && indices) { - const obsvIndexP = new ObservabilityIndexPatterns(data); - const indPattern = await obsvIndexP.getIndexPattern(dataType, indices); + const obsvIndexP = new ObservabilityDataViews(data); + const indPattern = await obsvIndexP.getDataView(dataType, indices); setIndexPatterns((prevState) => ({ ...prevState, [dataType]: indPattern })); } diff --git a/x-pack/plugins/observability/public/utils/observability_data_views/index.ts b/x-pack/plugins/observability/public/utils/observability_data_views/index.ts new file mode 100644 index 00000000000000..571f5ea4c11a25 --- /dev/null +++ b/x-pack/plugins/observability/public/utils/observability_data_views/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export * from './observability_data_views'; diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.test.ts b/x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.test.ts similarity index 81% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.test.ts rename to x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.test.ts index a8c5c1a0a3b79e..78899e9cabca0e 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.test.ts +++ b/x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.test.ts @@ -5,9 +5,9 @@ * 2.0. */ -import { indexPatternList, ObservabilityIndexPatterns } from './observability_index_patterns'; -import { mockCore, mockIndexPattern } from '../rtl_helpers'; -import { SavedObjectNotFound } from '../../../../../../../../src/plugins/kibana_utils/public'; +import { dataViewList, ObservabilityDataViews } from './observability_data_views'; +import { mockCore, mockIndexPattern } from '../../components/shared/exploratory_view/rtl_helpers'; +import { SavedObjectNotFound } from '../../../../../../src/plugins/kibana_utils/public'; const fieldFormats = { 'transaction.duration.us': { @@ -70,13 +70,13 @@ const fieldFormats = { describe('ObservabilityIndexPatterns', function () { const { data } = mockCore(); data!.indexPatterns.get = jest.fn().mockReturnValue({ title: 'index-*' }); - data!.indexPatterns.createAndSave = jest.fn().mockReturnValue({ id: indexPatternList.ux }); + data!.indexPatterns.createAndSave = jest.fn().mockReturnValue({ id: dataViewList.ux }); data!.indexPatterns.updateSavedObject = jest.fn(); it('should return index pattern for app', async function () { - const obsv = new ObservabilityIndexPatterns(data!); + const obsv = new ObservabilityDataViews(data!); - const indexP = await obsv.getIndexPattern('ux', 'heartbeat-8*,synthetics-*'); + const indexP = await obsv.getDataView('ux', 'heartbeat-8*,synthetics-*'); expect(indexP).toEqual({ id: 'rum_static_index_pattern_id' }); @@ -91,13 +91,13 @@ describe('ObservabilityIndexPatterns', function () { throw new SavedObjectNotFound('index_pattern'); }); - data!.indexPatterns.createAndSave = jest.fn().mockReturnValue({ id: indexPatternList.ux }); + data!.indexPatterns.createAndSave = jest.fn().mockReturnValue({ id: dataViewList.ux }); - const obsv = new ObservabilityIndexPatterns(data!); + const obsv = new ObservabilityDataViews(data!); - const indexP = await obsv.getIndexPattern('ux', 'trace-*,apm-*'); + const indexP = await obsv.getDataView('ux', 'trace-*,apm-*'); - expect(indexP).toEqual({ id: indexPatternList.ux }); + expect(indexP).toEqual({ id: dataViewList.ux }); expect(data?.indexPatterns.createAndSave).toHaveBeenCalledWith({ fieldFormats, @@ -110,7 +110,7 @@ describe('ObservabilityIndexPatterns', function () { }); it('should return getFieldFormats', function () { - const obsv = new ObservabilityIndexPatterns(data!); + const obsv = new ObservabilityDataViews(data!); expect(obsv.getFieldFormats('ux')).toEqual(fieldFormats); }); @@ -118,7 +118,7 @@ describe('ObservabilityIndexPatterns', function () { it('should validate field formats', async function () { mockIndexPattern.getFormatterForField = jest.fn().mockReturnValue({ params: () => {} }); - const obsv = new ObservabilityIndexPatterns(data!); + const obsv = new ObservabilityDataViews(data!); await obsv.validateFieldFormats('ux', mockIndexPattern); diff --git a/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts b/x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts similarity index 63% rename from x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts rename to x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts index b35acaae6fe80c..eb66894772dfd8 100644 --- a/x-pack/plugins/observability/public/components/shared/exploratory_view/utils/observability_index_patterns.ts +++ b/x-pack/plugins/observability/public/utils/observability_data_views/observability_data_views.ts @@ -6,18 +6,19 @@ */ import type { FieldFormat as IFieldFormat } from 'src/plugins/field_formats/common'; -import { SavedObjectNotFound } from '../../../../../../../../src/plugins/kibana_utils/public'; +import { SavedObjectNotFound } from '../../../../../../src/plugins/kibana_utils/public'; +import { DataPublicPluginStart } from '../../../../../../src/plugins/data/public'; +import type { DataView, DataViewSpec } from '../../../../../../src/plugins/data/common'; +import { rumFieldFormats } from '../../components/shared/exploratory_view/configurations/rum/field_formats'; +import { syntheticsFieldFormats } from '../../components/shared/exploratory_view/configurations/synthetics/field_formats'; import { - DataPublicPluginStart, - IndexPattern, - IndexPatternSpec, -} from '../../../../../../../../src/plugins/data/public'; -import { rumFieldFormats } from '../configurations/rum/field_formats'; -import { syntheticsFieldFormats } from '../configurations/synthetics/field_formats'; -import { AppDataType, FieldFormat, FieldFormatParams } from '../types'; -import { apmFieldFormats } from '../configurations/apm/field_formats'; -import { getDataHandler } from '../../../../data_handler'; -import { infraMetricsFieldFormats } from '../configurations/infra_metrics/field_formats'; + AppDataType, + FieldFormat, + FieldFormatParams, +} from '../../components/shared/exploratory_view/types'; +import { apmFieldFormats } from '../../components/shared/exploratory_view/configurations/apm/field_formats'; +import { getDataHandler } from '../../data_handler'; +import { infraMetricsFieldFormats } from '../../components/shared/exploratory_view/configurations/infra_metrics/field_formats'; const appFieldFormats: Record = { infra_logs: null, @@ -32,7 +33,7 @@ function getFieldFormatsForApp(app: AppDataType) { return appFieldFormats[app]; } -export const indexPatternList: Record = { +export const dataViewList: Record = { synthetics: 'synthetics_static_index_pattern_id', apm: 'apm_static_index_pattern_id', ux: 'rum_static_index_pattern_id', @@ -54,11 +55,11 @@ const getAppIndicesWithPattern = (app: AppDataType, indices: string) => { return `${appToPatternMap?.[app] ?? app},${indices}`; }; -const getAppIndexPatternId = (app: AppDataType, indices: string) => { +const getAppDataViewId = (app: AppDataType, indices: string) => { // Replace characters / ? , " < > | * with _ const postfix = indices.replace(/[^A-Z0-9]+/gi, '_').toLowerCase(); - return `${indexPatternList?.[app] ?? app}_${postfix}`; + return `${dataViewList?.[app] ?? app}_${postfix}`; }; export function isParamsSame(param1: IFieldFormat['_params'], param2: FieldFormatParams) { @@ -75,50 +76,50 @@ export function isParamsSame(param1: IFieldFormat['_params'], param2: FieldForma return isSame; } -export class ObservabilityIndexPatterns { +export class ObservabilityDataViews { data?: DataPublicPluginStart; constructor(data: DataPublicPluginStart) { this.data = data; } - async createIndexPattern(app: AppDataType, indices: string) { + async createDataView(app: AppDataType, indices: string) { if (!this.data) { throw new Error('data is not defined'); } const appIndicesPattern = getAppIndicesWithPattern(app, indices); - return await this.data.indexPatterns.createAndSave({ + return await this.data.dataViews.createAndSave({ title: appIndicesPattern, - id: getAppIndexPatternId(app, indices), + id: getAppDataViewId(app, indices), timeFieldName: '@timestamp', fieldFormats: this.getFieldFormats(app), }); } // we want to make sure field formats remain same - async validateFieldFormats(app: AppDataType, indexPattern: IndexPattern) { + async validateFieldFormats(app: AppDataType, dataView: DataView) { const defaultFieldFormats = getFieldFormatsForApp(app); if (defaultFieldFormats && defaultFieldFormats.length > 0) { let isParamsDifferent = false; defaultFieldFormats.forEach(({ field, format }) => { - const fieldByName = indexPattern.getFieldByName(field); + const fieldByName = dataView.getFieldByName(field); if (fieldByName) { - const fieldFormat = indexPattern.getFormatterForField(fieldByName); + const fieldFormat = dataView.getFormatterForField(fieldByName); const params = fieldFormat.params(); if (!isParamsSame(params, format.params) || format.id !== fieldFormat.type.id) { - indexPattern.setFieldFormat(field, format); + dataView.setFieldFormat(field, format); isParamsDifferent = true; } } }); if (isParamsDifferent) { - await this.data?.indexPatterns.updateSavedObject(indexPattern); + await this.data?.dataViews.updateSavedObject(dataView); } } } getFieldFormats(app: AppDataType) { - const fieldFormatMap: IndexPatternSpec['fieldFormats'] = {}; + const fieldFormatMap: DataViewSpec['fieldFormats'] = {}; (appFieldFormats?.[app] ?? []).forEach(({ field, format }) => { fieldFormatMap[field] = format; @@ -140,7 +141,7 @@ export class ObservabilityIndexPatterns { } } - async getIndexPattern(app: AppDataType, indices?: string): Promise { + async getDataView(app: AppDataType, indices?: string): Promise { if (!this.data) { throw new Error('data is not defined'); } @@ -151,22 +152,22 @@ export class ObservabilityIndexPatterns { if (appIndices) { try { - const indexPatternId = getAppIndexPatternId(app, appIndices); - const indexPatternTitle = getAppIndicesWithPattern(app, appIndices); - // we will get index pattern by id - const indexPattern = await this.data?.indexPatterns.get(indexPatternId); + const dataViewId = getAppDataViewId(app, appIndices); + const dataViewTitle = getAppIndicesWithPattern(app, appIndices); + // we will get the data view by id + const dataView = await this.data?.indexPatterns.get(dataViewId); // and make sure title matches, otherwise, we will need to create it - if (indexPattern.title !== indexPatternTitle) { - return await this.createIndexPattern(app, appIndices); + if (dataView.title !== dataViewTitle) { + return await this.createDataView(app, appIndices); } // this is intentional a non blocking call, so no await clause - this.validateFieldFormats(app, indexPattern); - return indexPattern; + this.validateFieldFormats(app, dataView); + return dataView; } catch (e: unknown) { if (e instanceof SavedObjectNotFound) { - return await this.createIndexPattern(app, appIndices); + return await this.createDataView(app, appIndices); } } }