mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Discover] Add support for context dependent profiles (#189229)
## Summary This PR adds support for passing higher level context objects to profile providers as part of the resolution process to support profiles which are context dependent (e.g. an `oblt-logs-data-source` profile which should only become active when viewing a logs data source _and_ in the `oblt` project type). ### Checklist - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [x] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] Any UI touched in this PR is usable by keyboard only (learn more about [keyboard accessibility](https://webaim.org/techniques/keyboard/)) - [ ] Any UI touched in this PR does not create any new axe failures (run axe in browser: [FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/), [Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US)) - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This renders correctly on smaller devices using a responsive layout. (You can test this [in your browser](https://www.browserstack.com/guide/responsive-testing-on-local-server)) - [ ] This was checked for [cross-browser compatibility](https://www.elastic.co/support/matrix#matrix_browsers) ### For maintainers - [ ] This was checked for breaking API changes and was [labeled appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) --------- Co-authored-by: Julia Rechkunova <julia.rechkunova@elastic.co>
This commit is contained in:
parent
3783e82490
commit
69de10f81f
7 changed files with 59 additions and 9 deletions
|
@ -8,7 +8,13 @@
|
|||
|
||||
import { buildDataTableRecord } from '@kbn/discover-utils';
|
||||
import { DocViewsRegistry } from '@kbn/unified-doc-viewer';
|
||||
import { DocumentType } from '../../profiles';
|
||||
import {
|
||||
DataSourceCategory,
|
||||
DataSourceContext,
|
||||
DocumentType,
|
||||
RootContext,
|
||||
SolutionType,
|
||||
} from '../../profiles';
|
||||
import { createContextAwarenessMocks } from '../../__mocks__';
|
||||
import { createLogDocumentProfileProvider } from './profile';
|
||||
|
||||
|
@ -16,6 +22,8 @@ const mockServices = createContextAwarenessMocks().profileProviderServices;
|
|||
|
||||
describe('logDocumentProfileProvider', () => {
|
||||
const logDocumentProfileProvider = createLogDocumentProfileProvider(mockServices);
|
||||
const ROOT_CONTEXT: RootContext = { solutionType: SolutionType.Default };
|
||||
const DATA_SOURCE_CONTEXT: DataSourceContext = { category: DataSourceCategory.Logs };
|
||||
const RESOLUTION_MATCH = {
|
||||
isMatch: true,
|
||||
context: {
|
||||
|
@ -29,6 +37,8 @@ describe('logDocumentProfileProvider', () => {
|
|||
it('matches records with the correct data stream type', () => {
|
||||
expect(
|
||||
logDocumentProfileProvider.resolve({
|
||||
rootContext: ROOT_CONTEXT,
|
||||
dataSourceContext: DATA_SOURCE_CONTEXT,
|
||||
record: buildMockRecord('logs-2000-01-01', {
|
||||
'data_stream.type': ['logs'],
|
||||
}),
|
||||
|
@ -39,6 +49,8 @@ describe('logDocumentProfileProvider', () => {
|
|||
it('matches records with fields prefixed with "log."', () => {
|
||||
expect(
|
||||
logDocumentProfileProvider.resolve({
|
||||
rootContext: ROOT_CONTEXT,
|
||||
dataSourceContext: DATA_SOURCE_CONTEXT,
|
||||
record: buildMockRecord('logs-2000-01-01', {
|
||||
'log.level': ['INFO'],
|
||||
}),
|
||||
|
@ -49,11 +61,15 @@ describe('logDocumentProfileProvider', () => {
|
|||
it('matches records with indices matching the allowed pattern', () => {
|
||||
expect(
|
||||
logDocumentProfileProvider.resolve({
|
||||
rootContext: ROOT_CONTEXT,
|
||||
dataSourceContext: DATA_SOURCE_CONTEXT,
|
||||
record: buildMockRecord('logs-2000-01-01'),
|
||||
})
|
||||
).toEqual(RESOLUTION_MATCH);
|
||||
expect(
|
||||
logDocumentProfileProvider.resolve({
|
||||
rootContext: ROOT_CONTEXT,
|
||||
dataSourceContext: DATA_SOURCE_CONTEXT,
|
||||
record: buildMockRecord('remote_cluster:filebeat'),
|
||||
})
|
||||
).toEqual(RESOLUTION_MATCH);
|
||||
|
@ -62,6 +78,8 @@ describe('logDocumentProfileProvider', () => {
|
|||
it('does not match records with neither characteristic', () => {
|
||||
expect(
|
||||
logDocumentProfileProvider.resolve({
|
||||
rootContext: ROOT_CONTEXT,
|
||||
dataSourceContext: DATA_SOURCE_CONTEXT,
|
||||
record: buildMockRecord('another-index'),
|
||||
})
|
||||
).toEqual(RESOLUTION_MISMATCH);
|
||||
|
|
|
@ -10,7 +10,7 @@ import { buildDataTableRecord } from '@kbn/discover-utils';
|
|||
import type { EuiThemeComputed } from '@elastic/eui';
|
||||
import { createStubIndexPattern } from '@kbn/data-views-plugin/common/data_view.stub';
|
||||
import { createDataViewDataSource, createEsqlDataSource } from '../../../../common/data_sources';
|
||||
import { DataSourceCategory } from '../../profiles';
|
||||
import { DataSourceCategory, RootContext, SolutionType } from '../../profiles';
|
||||
import { createContextAwarenessMocks } from '../../__mocks__';
|
||||
import { createLogsDataSourceProfileProvider } from './profile';
|
||||
|
||||
|
@ -21,7 +21,7 @@ describe('logsDataSourceProfileProvider', () => {
|
|||
const VALID_INDEX_PATTERN = 'logs-nginx.access-*';
|
||||
const MIXED_INDEX_PATTERN = 'logs-nginx.access-*,metrics-*';
|
||||
const INVALID_INDEX_PATTERN = 'my_source-access-*';
|
||||
|
||||
const ROOT_CONTEXT: RootContext = { solutionType: SolutionType.Default };
|
||||
const RESOLUTION_MATCH = {
|
||||
isMatch: true,
|
||||
context: { category: DataSourceCategory.Logs },
|
||||
|
@ -33,6 +33,7 @@ describe('logsDataSourceProfileProvider', () => {
|
|||
it('should match ES|QL sources with an allowed index pattern in its query', () => {
|
||||
expect(
|
||||
logsDataSourceProfileProvider.resolve({
|
||||
rootContext: ROOT_CONTEXT,
|
||||
dataSource: createEsqlDataSource(),
|
||||
query: { esql: `from ${VALID_INDEX_PATTERN}` },
|
||||
})
|
||||
|
@ -42,12 +43,14 @@ describe('logsDataSourceProfileProvider', () => {
|
|||
it('should NOT match ES|QL sources with a mixed or not allowed index pattern in its query', () => {
|
||||
expect(
|
||||
logsDataSourceProfileProvider.resolve({
|
||||
rootContext: ROOT_CONTEXT,
|
||||
dataSource: createEsqlDataSource(),
|
||||
query: { esql: `from ${INVALID_INDEX_PATTERN}` },
|
||||
})
|
||||
).toEqual(RESOLUTION_MISMATCH);
|
||||
expect(
|
||||
logsDataSourceProfileProvider.resolve({
|
||||
rootContext: ROOT_CONTEXT,
|
||||
dataSource: createEsqlDataSource(),
|
||||
query: { esql: `from ${MIXED_INDEX_PATTERN}` },
|
||||
})
|
||||
|
@ -57,6 +60,7 @@ describe('logsDataSourceProfileProvider', () => {
|
|||
it('should match data view sources with an allowed index pattern', () => {
|
||||
expect(
|
||||
logsDataSourceProfileProvider.resolve({
|
||||
rootContext: ROOT_CONTEXT,
|
||||
dataSource: createDataViewDataSource({ dataViewId: VALID_INDEX_PATTERN }),
|
||||
dataView: createStubIndexPattern({ spec: { title: VALID_INDEX_PATTERN } }),
|
||||
})
|
||||
|
@ -66,12 +70,14 @@ describe('logsDataSourceProfileProvider', () => {
|
|||
it('should NOT match data view sources with a mixed or not allowed index pattern', () => {
|
||||
expect(
|
||||
logsDataSourceProfileProvider.resolve({
|
||||
rootContext: ROOT_CONTEXT,
|
||||
dataSource: createDataViewDataSource({ dataViewId: INVALID_INDEX_PATTERN }),
|
||||
dataView: createStubIndexPattern({ spec: { title: INVALID_INDEX_PATTERN } }),
|
||||
})
|
||||
).toEqual(RESOLUTION_MISMATCH);
|
||||
expect(
|
||||
logsDataSourceProfileProvider.resolve({
|
||||
rootContext: ROOT_CONTEXT,
|
||||
dataSource: createDataViewDataSource({ dataViewId: MIXED_INDEX_PATTERN }),
|
||||
dataView: createStubIndexPattern({ spec: { title: MIXED_INDEX_PATTERN } }),
|
||||
})
|
||||
|
|
|
@ -62,10 +62,13 @@ describe('registerProfileProviders', () => {
|
|||
});
|
||||
const rootContext = await rootProfileServiceMock.resolve({ solutionNavId: null });
|
||||
const dataSourceContext = await dataSourceProfileServiceMock.resolve({
|
||||
rootContext,
|
||||
dataSource: createEsqlDataSource(),
|
||||
query: { esql: 'from my-example-logs' },
|
||||
});
|
||||
const documentContext = documentProfileServiceMock.resolve({
|
||||
rootContext,
|
||||
dataSourceContext,
|
||||
record: {
|
||||
id: 'test',
|
||||
flattened: { 'data_stream.type': 'example' },
|
||||
|
@ -94,10 +97,13 @@ describe('registerProfileProviders', () => {
|
|||
});
|
||||
const rootContext = await rootProfileServiceMock.resolve({ solutionNavId: null });
|
||||
const dataSourceContext = await dataSourceProfileServiceMock.resolve({
|
||||
rootContext,
|
||||
dataSource: createEsqlDataSource(),
|
||||
query: { esql: 'from my-example-logs' },
|
||||
});
|
||||
const documentContext = documentProfileServiceMock.resolve({
|
||||
rootContext,
|
||||
dataSourceContext,
|
||||
record: {
|
||||
id: 'test',
|
||||
flattened: { 'data_stream.type': 'example' },
|
||||
|
|
|
@ -10,7 +10,8 @@ import type { DataView } from '@kbn/data-views-plugin/common';
|
|||
import type { AggregateQuery, Query } from '@kbn/es-query';
|
||||
import type { DiscoverDataSource } from '../../../common/data_sources';
|
||||
import { AsyncProfileProvider, AsyncProfileService } from '../profile_service';
|
||||
import { Profile } from '../types';
|
||||
import type { Profile } from '../types';
|
||||
import type { RootContext } from './root_profile';
|
||||
|
||||
export enum DataSourceCategory {
|
||||
Logs = 'logs',
|
||||
|
@ -20,6 +21,7 @@ export enum DataSourceCategory {
|
|||
export type DataSourceProfile = Profile;
|
||||
|
||||
export interface DataSourceProfileProviderParams {
|
||||
rootContext: RootContext;
|
||||
dataSource?: DiscoverDataSource;
|
||||
dataView?: DataView;
|
||||
query?: Query | AggregateQuery;
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
import type { DataTableRecord } from '@kbn/discover-utils';
|
||||
import type { Profile } from '../types';
|
||||
import { ProfileProvider, ProfileService } from '../profile_service';
|
||||
import type { RootContext } from './root_profile';
|
||||
import type { DataSourceContext } from './data_source_profile';
|
||||
|
||||
export enum DocumentType {
|
||||
Log = 'log',
|
||||
|
@ -18,6 +20,8 @@ export enum DocumentType {
|
|||
export type DocumentProfile = Omit<Profile, 'getCellRenderers'>;
|
||||
|
||||
export interface DocumentProfileProviderParams {
|
||||
rootContext: RootContext;
|
||||
dataSourceContext: DataSourceContext;
|
||||
record: DataTableRecord;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import { firstValueFrom, Subject } from 'rxjs';
|
||||
import { createEsqlDataSource } from '../../common/data_sources';
|
||||
import { addLog } from '../utils/add_log';
|
||||
import { SolutionType } from './profiles/root_profile';
|
||||
import { createContextAwarenessMocks } from './__mocks__';
|
||||
|
||||
jest.mock('../utils/add_log');
|
||||
|
@ -220,10 +221,12 @@ describe('ProfilesManager', () => {
|
|||
|
||||
it('should cancel existing data source profile resolution when another is triggered', async () => {
|
||||
const context = await mocks.dataSourceProfileProviderMock.resolve({
|
||||
rootContext: { solutionType: SolutionType.Default },
|
||||
dataSource: createEsqlDataSource(),
|
||||
query: { esql: 'from *' },
|
||||
});
|
||||
const newContext = await mocks.dataSourceProfileProviderMock.resolve({
|
||||
rootContext: { solutionType: SolutionType.Default },
|
||||
dataSource: createEsqlDataSource(),
|
||||
query: { esql: 'from logs-*' },
|
||||
});
|
||||
|
|
|
@ -87,7 +87,9 @@ export class ProfilesManager {
|
|||
this.prevRootProfileParams = serializedParams;
|
||||
}
|
||||
|
||||
public async resolveDataSourceProfile(params: DataSourceProfileProviderParams) {
|
||||
public async resolveDataSourceProfile(
|
||||
params: Omit<DataSourceProfileProviderParams, 'rootContext'>
|
||||
) {
|
||||
const serializedParams = serializeDataSourceProfileParams(params);
|
||||
|
||||
if (isEqual(this.prevDataSourceProfileParams, serializedParams)) {
|
||||
|
@ -101,7 +103,10 @@ export class ProfilesManager {
|
|||
let context = this.dataSourceProfileService.defaultContext;
|
||||
|
||||
try {
|
||||
context = await this.dataSourceProfileService.resolve(params);
|
||||
context = await this.dataSourceProfileService.resolve({
|
||||
...params,
|
||||
rootContext: this.rootContext$.getValue(),
|
||||
});
|
||||
} catch (e) {
|
||||
logResolutionError(ContextType.DataSource, serializedParams, e);
|
||||
}
|
||||
|
@ -114,7 +119,9 @@ export class ProfilesManager {
|
|||
this.prevDataSourceProfileParams = serializedParams;
|
||||
}
|
||||
|
||||
public resolveDocumentProfile(params: DocumentProfileProviderParams) {
|
||||
public resolveDocumentProfile(
|
||||
params: Omit<DocumentProfileProviderParams, 'rootContext' | 'dataSourceContext'>
|
||||
) {
|
||||
let context: ContextWithProfileId<DocumentContext> | undefined;
|
||||
|
||||
return new Proxy(params.record, {
|
||||
|
@ -126,7 +133,11 @@ export class ProfilesManager {
|
|||
|
||||
if (!context) {
|
||||
try {
|
||||
context = this.documentProfileService.resolve(params);
|
||||
context = this.documentProfileService.resolve({
|
||||
...params,
|
||||
rootContext: this.rootContext$.getValue(),
|
||||
dataSourceContext: this.dataSourceContext$.getValue(),
|
||||
});
|
||||
} catch (e) {
|
||||
logResolutionError(ContextType.Document, { recordId: params.record.id }, e);
|
||||
context = this.documentProfileService.defaultContext;
|
||||
|
@ -164,7 +175,7 @@ const serializeRootProfileParams = (
|
|||
};
|
||||
|
||||
const serializeDataSourceProfileParams = (
|
||||
params: DataSourceProfileProviderParams
|
||||
params: Omit<DataSourceProfileProviderParams, 'rootContext'>
|
||||
): SerializedDataSourceProfileParams => {
|
||||
return {
|
||||
dataViewId: isDataSourceType(params.dataSource, DataSourceType.DataView)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue