mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[Discover] Add logs source and document contexts (#184601)
## 📝 Summary This PR adds basic implementations for resolving "logs" data source and document contexts to their respective profiles. Due to the limited set of profile customization points the new profiles are empty. - closes #184079 - closes #184080 ## 🔍 Implementation details - In order to organize these and future profiles this PR introduces the `profile_providers` folder in `context_awareness`. - For a more structured organization, utilities for resolving logs sources have been moved/implemented in the `@kbn/discover-utils` and `@kbn/data-view-utils` packages. - The code ownership for the two logs profiles is shared between the data discovery team and the obs ux logs team. ### Document Level Logs Resolution The document logs context resolution is performed with the following criteria, as far as one complies, the context will be evaluated as a match: - The `data_stream.type` field exists on the document and it's equal to `logs` - The document contains any field from the [ECS Log field set](https://www.elastic.co/guide/en/ecs/current/ecs-log.html) (fields staring with `log.`) - The `_index` field exists and tests positive against the allowed indices from the [built-in definition/ settings](https://github.com/elastic/kibana/pull/184601/files#diff-5e1646fa4ec758a92aa38910dc047b18cb826e287a36b43e811eb5fc7a3b0fe9R28). ### Data Source Logs Resolution The data source logs context resolution is performed with the following criteria, as far as one complies, the context will be evaluated as a match: - Being the source of a data view type, the related index tests positive against the allowed indices from the [built-in definition/ settings](https://github.com/elastic/kibana/pull/184601/files#diff-5e1646fa4ec758a92aa38910dc047b18cb826e287a36b43e811eb5fc7a3b0fe9R28). - Being the source of a ES|QL query type, the related index extracted from the query tests positive against the allowed indices from the [built-in definition/ settings](https://github.com/elastic/kibana/pull/184601/files#diff-5e1646fa4ec758a92aa38910dc047b18cb826e287a36b43e811eb5fc7a3b0fe9R28). ## 🕵️♀️ Review notes > [!NOTE] > Notes in this format have been left through the PR to give additional context about some choices, any further feedback is welcome --------- Co-authored-by: Davis McPhee <davis.mcphee@elastic.co> Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co> Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani01@gmail.com> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Kerry Gallagher <k.gallagher.05@gmail.com>
This commit is contained in:
parent
4a95ffbd93
commit
051b91a47f
30 changed files with 696 additions and 201 deletions
|
@ -7,3 +7,6 @@
|
|||
*/
|
||||
|
||||
export * from './src/constants';
|
||||
|
||||
export { createRegExpPatternFrom } from './src/utils/create_regexp_pattern_from';
|
||||
export { testPatternAgainstAllowedList } from './src/utils/test_pattern_against_allowed_list';
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { createRegExpPatternFrom } from './create_regexp_pattern_from';
|
||||
|
||||
describe('createRegExpPatternFrom should create a regular expression starting from a string that', () => {
|
||||
const regExpPattern = createRegExpPatternFrom('logs');
|
||||
|
||||
it('tests positive for single index patterns starting with the passed base pattern', () => {
|
||||
expect('logs*').toMatch(regExpPattern);
|
||||
expect('logs-*').toMatch(regExpPattern);
|
||||
expect('logs-*-*').toMatch(regExpPattern);
|
||||
expect('logs-system.syslog-*').toMatch(regExpPattern);
|
||||
|
||||
expect('logss*').not.toMatch(regExpPattern);
|
||||
expect('logss-*').not.toMatch(regExpPattern);
|
||||
expect('metrics*').not.toMatch(regExpPattern);
|
||||
expect('metrics-*').not.toMatch(regExpPattern);
|
||||
});
|
||||
|
||||
it('tests positive for single index patterns containing the passed base pattern', () => {
|
||||
expect('foo-logs*').toMatch(regExpPattern);
|
||||
expect('foo-logs-*').toMatch(regExpPattern);
|
||||
expect('foo-logs-*-*').toMatch(regExpPattern);
|
||||
expect('foo-logs-system.syslog-*').toMatch(regExpPattern);
|
||||
expect('.ds-kibana_sample_data_logs-2024.06.13-000001').toMatch(regExpPattern);
|
||||
|
||||
expect('foo-logss*').not.toMatch(regExpPattern);
|
||||
expect('foo-logss-*').not.toMatch(regExpPattern);
|
||||
expect('foo-metrics*').not.toMatch(regExpPattern);
|
||||
expect('foo-metrics-*').not.toMatch(regExpPattern);
|
||||
});
|
||||
|
||||
it('tests positive for single index patterns with CCS prefixes', () => {
|
||||
expect('cluster1:logs-*').toMatch(regExpPattern);
|
||||
expect('cluster1:logs-*-*').toMatch(regExpPattern);
|
||||
expect('cluster1:logs-system.syslog-*').toMatch(regExpPattern);
|
||||
expect('cluster1:logs-system.syslog-default').toMatch(regExpPattern);
|
||||
|
||||
expect('cluster1:logss*').not.toMatch(regExpPattern);
|
||||
expect('cluster1:logss-*').not.toMatch(regExpPattern);
|
||||
expect('cluster1:metrics*').not.toMatch(regExpPattern);
|
||||
expect('cluster1:metrics-*').not.toMatch(regExpPattern);
|
||||
});
|
||||
|
||||
it('tests positive for multiple index patterns comma-separated if all of them individually match the criteria', () => {
|
||||
expect('logs-*,cluster1:logs-*').toMatch(regExpPattern);
|
||||
expect('cluster1:logs-*,cluster2:logs-*').toMatch(regExpPattern);
|
||||
expect('*:logs-system.syslog-*,*:logs-system.errors-*').toMatch(regExpPattern);
|
||||
|
||||
expect('*:metrics-system.syslog-*,logs-system.errors-*').not.toMatch(regExpPattern);
|
||||
});
|
||||
|
||||
it('tests positive for patterns with trailing commas', () => {
|
||||
expect('logs-*,').toMatch(regExpPattern);
|
||||
expect('cluster1:logs-*,logs-*,').toMatch(regExpPattern);
|
||||
});
|
||||
|
||||
it('tests negative for patterns with spaces and unexpected commas', () => {
|
||||
expect('cluster1:logs-*,clust,er2:logs-*').not.toMatch(regExpPattern);
|
||||
expect('cluster1:logs-*, cluster2:logs-*').not.toMatch(regExpPattern);
|
||||
});
|
||||
});
|
|
@ -1,15 +1,17 @@
|
|||
/*
|
||||
* 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.
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const buildIndexPatternRegExp = (basePatterns: string[]) => {
|
||||
export const createRegExpPatternFrom = (basePatterns: string | string[]) => {
|
||||
const patterns = Array.isArray(basePatterns) ? basePatterns : [basePatterns];
|
||||
// Create the base patterns union with strict boundaries
|
||||
const basePatternGroup = `\\b(${basePatterns.join('|')})\\b([^,\\s]+)?`;
|
||||
const basePatternGroup = `[^,\\s]*(\\b|_)(${patterns.join('|')})(\\b|_)([^,\\s]*)?`;
|
||||
// Apply base patterns union for local and remote clusters
|
||||
const localAndRemotePatternGroup = `((${basePatternGroup})|([^:,\\s]+:${basePatternGroup}))`;
|
||||
const localAndRemotePatternGroup = `((${basePatternGroup})|([^:,\\s]*:${basePatternGroup}))`;
|
||||
// Handle trailing comma and multiple pattern concatenation
|
||||
return new RegExp(`^${localAndRemotePatternGroup}(,${localAndRemotePatternGroup})*(,$|$)`, 'i');
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { testPatternAgainstAllowedList } from './test_pattern_against_allowed_list';
|
||||
|
||||
describe('testPatternAgainstAllowedList', () => {
|
||||
const allowedList = ['foo-logs-bar', /^\b(logs)\b([^,\s]*)/i];
|
||||
|
||||
it('should return true if the passed input matches any string or regexp of the passed list', () => {
|
||||
expect(testPatternAgainstAllowedList(allowedList)('logs-*')).toBeTruthy();
|
||||
expect(testPatternAgainstAllowedList(allowedList)('logs-*-*')).toBeTruthy();
|
||||
expect(testPatternAgainstAllowedList(allowedList)('logs-system.syslog-*')).toBeTruthy();
|
||||
expect(testPatternAgainstAllowedList(allowedList)('foo-logs-bar')).toBeTruthy();
|
||||
|
||||
expect(testPatternAgainstAllowedList(allowedList)('logss-*')).toBeFalsy();
|
||||
expect(testPatternAgainstAllowedList(allowedList)('metrics*')).toBeFalsy();
|
||||
expect(testPatternAgainstAllowedList(allowedList)('metrics-*')).toBeFalsy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export const testPatternAgainstAllowedList =
|
||||
(allowedList: Array<string | RegExp>) => (value: string) => {
|
||||
for (const allowedItem of allowedList) {
|
||||
const isMatchingString = typeof allowedItem === 'string' && value === allowedItem;
|
||||
const isMatchingRegExp = allowedItem instanceof RegExp && allowedItem.test(value);
|
||||
|
||||
if (isMatchingString || isMatchingRegExp) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If no match is found in the allowedList, return false
|
||||
return false;
|
||||
};
|
|
@ -10,6 +10,7 @@ export {
|
|||
CONTEXT_DEFAULT_SIZE_SETTING,
|
||||
CONTEXT_STEP_SETTING,
|
||||
CONTEXT_TIE_BREAKER_FIELDS_SETTING,
|
||||
DEFAULT_ALLOWED_LOGS_BASE_PATTERNS,
|
||||
DEFAULT_COLUMNS_SETTING,
|
||||
DOC_HIDE_TIME_COLUMN_SETTING,
|
||||
DOC_TABLE_LEGACY,
|
||||
|
@ -30,6 +31,7 @@ export {
|
|||
IgnoredReason,
|
||||
buildDataTableRecord,
|
||||
buildDataTableRecordList,
|
||||
createLogsContextService,
|
||||
fieldConstants,
|
||||
formatFieldValue,
|
||||
formatHit,
|
||||
|
@ -43,4 +45,6 @@ export {
|
|||
usePager,
|
||||
} from './src';
|
||||
|
||||
export type { LogsContextService } from './src';
|
||||
|
||||
export * from './src/types';
|
||||
|
|
|
@ -8,3 +8,5 @@
|
|||
|
||||
export * from './types';
|
||||
export * from './utils';
|
||||
|
||||
export * from './logs_context_service';
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { createRegExpPatternFrom, testPatternAgainstAllowedList } from '@kbn/data-view-utils';
|
||||
|
||||
export interface LogsContextService {
|
||||
isLogsIndexPattern(indexPattern: unknown): boolean;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface LogsContextServiceDeps {
|
||||
// We will probably soon add uiSettings as a dependency
|
||||
// to consume user configured indices
|
||||
}
|
||||
|
||||
export const DEFAULT_ALLOWED_LOGS_BASE_PATTERNS = [
|
||||
'log',
|
||||
'logs',
|
||||
'logstash',
|
||||
'auditbeat',
|
||||
'filebeat',
|
||||
'winlogbeat',
|
||||
];
|
||||
|
||||
export const createLogsContextService = (_deps: LogsContextServiceDeps = {}) => {
|
||||
// This is initially an hard-coded set of well-known base patterns,
|
||||
// we can extend this allowed list with any setting coming from uiSettings
|
||||
const ALLOWED_LOGS_DATA_SOURCES = [createRegExpPatternFrom(DEFAULT_ALLOWED_LOGS_BASE_PATTERNS)];
|
||||
|
||||
const isLogsIndexPattern = (indexPattern: unknown) => {
|
||||
return (
|
||||
typeof indexPattern === 'string' &&
|
||||
testPatternAgainstAllowedList(ALLOWED_LOGS_DATA_SOURCES)(indexPattern)
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
isLogsIndexPattern,
|
||||
};
|
||||
};
|
|
@ -18,6 +18,7 @@
|
|||
"kbn_references": [
|
||||
"@kbn/data-service",
|
||||
"@kbn/data-views-plugin",
|
||||
"@kbn/data-view-utils",
|
||||
"@kbn/es-query",
|
||||
"@kbn/field-formats-plugin",
|
||||
"@kbn/field-types",
|
||||
|
|
|
@ -25,3 +25,11 @@ export const isDataSourceType = <T extends DataSourceType>(
|
|||
dataSource: DiscoverDataSource | undefined,
|
||||
type: T
|
||||
): dataSource is Extract<DiscoverDataSource, { type: T }> => dataSource?.type === type;
|
||||
|
||||
export const isDataViewSource = (
|
||||
dataSource: DiscoverDataSource | undefined
|
||||
): dataSource is DataViewDataSource => isDataSourceType(dataSource, DataSourceType.DataView);
|
||||
|
||||
export const isEsqlSource = (
|
||||
dataSource: DiscoverDataSource | undefined
|
||||
): dataSource is EsqlDataSource => isDataSourceType(dataSource, DataSourceType.Esql);
|
||||
|
|
|
@ -56,7 +56,7 @@ import { memoize, noop } from 'lodash';
|
|||
import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public';
|
||||
import type { AiopsPluginStart } from '@kbn/aiops-plugin/public';
|
||||
import type { DataVisualizerPluginStart } from '@kbn/data-visualizer-plugin/public';
|
||||
import type { DiscoverStartPlugins } from './plugin';
|
||||
import type { DiscoverStartPlugins } from './types';
|
||||
import type { DiscoverContextAppLocator } from './application/context/services/locator';
|
||||
import type { DiscoverSingleDocLocator } from './application/doc/locator';
|
||||
import type { DiscoverAppLocator } from '../common';
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
RootProfileService,
|
||||
SolutionType,
|
||||
} from '../profiles';
|
||||
import { createProfileProviderServices } from '../profiles/profile_provider_services';
|
||||
import { ProfilesManager } from '../profiles_manager';
|
||||
|
||||
export const createContextAwarenessMocks = () => {
|
||||
|
@ -107,6 +108,8 @@ export const createContextAwarenessMocks = () => {
|
|||
documentProfileServiceMock
|
||||
);
|
||||
|
||||
const profileProviderServices = createProfileProviderServices();
|
||||
|
||||
return {
|
||||
rootProfileProviderMock,
|
||||
dataSourceProfileProviderMock,
|
||||
|
@ -114,5 +117,6 @@ export const createContextAwarenessMocks = () => {
|
|||
contextRecordMock,
|
||||
contextRecordMock2,
|
||||
profilesManagerMock,
|
||||
profileProviderServices,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { createLogDocumentProfileProvider } from './profile';
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { buildDataTableRecord } from '@kbn/discover-utils';
|
||||
import { DocumentType } from '../../profiles';
|
||||
import { createContextAwarenessMocks } from '../../__mocks__';
|
||||
import { createLogDocumentProfileProvider } from './profile';
|
||||
|
||||
const mockServices = createContextAwarenessMocks().profileProviderServices;
|
||||
|
||||
describe('logDocumentProfileProvider', () => {
|
||||
const logDocumentProfileProvider = createLogDocumentProfileProvider(mockServices);
|
||||
const RESOLUTION_MATCH = {
|
||||
isMatch: true,
|
||||
context: {
|
||||
type: DocumentType.Log,
|
||||
},
|
||||
};
|
||||
const RESOLUTION_MISMATCH = {
|
||||
isMatch: false,
|
||||
};
|
||||
|
||||
it('matches records with the correct data stream type', () => {
|
||||
expect(
|
||||
logDocumentProfileProvider.resolve({
|
||||
record: buildMockRecord('logs-2000-01-01', {
|
||||
'data_stream.type': ['logs'],
|
||||
}),
|
||||
})
|
||||
).toEqual(RESOLUTION_MATCH);
|
||||
});
|
||||
|
||||
it('matches records with fields prefixed with "log."', () => {
|
||||
expect(
|
||||
logDocumentProfileProvider.resolve({
|
||||
record: buildMockRecord('logs-2000-01-01', {
|
||||
'log.level': ['INFO'],
|
||||
}),
|
||||
})
|
||||
).toEqual(RESOLUTION_MATCH);
|
||||
});
|
||||
|
||||
it('matches records with indices matching the allowed pattern', () => {
|
||||
expect(
|
||||
logDocumentProfileProvider.resolve({
|
||||
record: buildMockRecord('logs-2000-01-01'),
|
||||
})
|
||||
).toEqual(RESOLUTION_MATCH);
|
||||
expect(
|
||||
logDocumentProfileProvider.resolve({
|
||||
record: buildMockRecord('remote_cluster:filebeat'),
|
||||
})
|
||||
).toEqual(RESOLUTION_MATCH);
|
||||
});
|
||||
|
||||
it('does not match records with neither characteristic', () => {
|
||||
expect(
|
||||
logDocumentProfileProvider.resolve({
|
||||
record: buildMockRecord('another-index'),
|
||||
})
|
||||
).toEqual(RESOLUTION_MISMATCH);
|
||||
});
|
||||
});
|
||||
|
||||
const buildMockRecord = (index: string, fields: Record<string, unknown[]> = {}) =>
|
||||
buildDataTableRecord({
|
||||
_id: '',
|
||||
_index: index,
|
||||
fields: {
|
||||
_index: index,
|
||||
...fields,
|
||||
},
|
||||
});
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { DataTableRecord } from '@kbn/discover-utils';
|
||||
import { DocumentProfileProvider, DocumentType } from '../../profiles';
|
||||
import { ProfileProviderServices } from '../../profiles/profile_provider_services';
|
||||
|
||||
export const createLogDocumentProfileProvider = (
|
||||
services: ProfileProviderServices
|
||||
): DocumentProfileProvider => ({
|
||||
profileId: 'log-document-profile',
|
||||
profile: {},
|
||||
resolve: ({ record }) => {
|
||||
const isLogRecord = getIsLogRecord(record, services.logsContextService.isLogsIndexPattern);
|
||||
|
||||
if (!isLogRecord) {
|
||||
return { isMatch: false };
|
||||
}
|
||||
|
||||
return {
|
||||
isMatch: true,
|
||||
context: {
|
||||
type: DocumentType.Log,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const getIsLogRecord = (
|
||||
record: DataTableRecord,
|
||||
isLogsIndexPattern: ProfileProviderServices['logsContextService']['isLogsIndexPattern']
|
||||
) => {
|
||||
return (
|
||||
getDataStreamType(record).includes('logs') ||
|
||||
hasFieldsWithPrefix('log.')(record) ||
|
||||
getIndices(record).some(isLogsIndexPattern)
|
||||
);
|
||||
};
|
||||
|
||||
const getFieldValues =
|
||||
(field: string) =>
|
||||
(record: DataTableRecord): unknown[] => {
|
||||
const value = record.flattened[field];
|
||||
return Array.isArray(value) ? value : [value];
|
||||
};
|
||||
|
||||
const getDataStreamType = getFieldValues('data_stream.type');
|
||||
const getIndices = getFieldValues('_index');
|
||||
|
||||
const hasFieldsWithPrefix = (prefix: string) => (record: DataTableRecord) => {
|
||||
return Object.keys(record.flattened).some((field) => field.startsWith(prefix));
|
||||
};
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
export { createLogsDataSourceProfileProvider } from './profile';
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { createStubIndexPattern } from '@kbn/data-views-plugin/common/data_view.stub';
|
||||
import { createDataViewDataSource, createEsqlDataSource } from '../../../../common/data_sources';
|
||||
import { DataSourceCategory } from '../../profiles';
|
||||
import { createContextAwarenessMocks } from '../../__mocks__';
|
||||
import { createLogsDataSourceProfileProvider } from './profile';
|
||||
|
||||
const mockServices = createContextAwarenessMocks().profileProviderServices;
|
||||
|
||||
describe('logsDataSourceProfileProvider', () => {
|
||||
const logsDataSourceProfileProvider = createLogsDataSourceProfileProvider(mockServices);
|
||||
const VALID_INDEX_PATTERN = 'logs-nginx.access-*';
|
||||
const MIXED_INDEX_PATTERN = 'logs-nginx.access-*,metrics-*';
|
||||
const INVALID_INDEX_PATTERN = 'my_source-access-*';
|
||||
|
||||
const RESOLUTION_MATCH = {
|
||||
isMatch: true,
|
||||
context: { category: DataSourceCategory.Logs },
|
||||
};
|
||||
const RESOLUTION_MISMATCH = {
|
||||
isMatch: false,
|
||||
};
|
||||
|
||||
it('should match ES|QL sources with an allowed index pattern in its query', () => {
|
||||
expect(
|
||||
logsDataSourceProfileProvider.resolve({
|
||||
dataSource: createEsqlDataSource(),
|
||||
query: { esql: `from ${VALID_INDEX_PATTERN}` },
|
||||
})
|
||||
).toEqual(RESOLUTION_MATCH);
|
||||
});
|
||||
|
||||
it('should NOT match ES|QL sources with a mixed or not allowed index pattern in its query', () => {
|
||||
expect(
|
||||
logsDataSourceProfileProvider.resolve({
|
||||
dataSource: createEsqlDataSource(),
|
||||
query: { esql: `from ${INVALID_INDEX_PATTERN}` },
|
||||
})
|
||||
).toEqual(RESOLUTION_MISMATCH);
|
||||
expect(
|
||||
logsDataSourceProfileProvider.resolve({
|
||||
dataSource: createEsqlDataSource(),
|
||||
query: { esql: `from ${MIXED_INDEX_PATTERN}` },
|
||||
})
|
||||
).toEqual(RESOLUTION_MISMATCH);
|
||||
});
|
||||
|
||||
it('should match data view sources with an allowed index pattern', () => {
|
||||
expect(
|
||||
logsDataSourceProfileProvider.resolve({
|
||||
dataSource: createDataViewDataSource({ dataViewId: VALID_INDEX_PATTERN }),
|
||||
dataView: createStubIndexPattern({ spec: { title: VALID_INDEX_PATTERN } }),
|
||||
})
|
||||
).toEqual(RESOLUTION_MATCH);
|
||||
});
|
||||
|
||||
it('should NOT match data view sources with a mixed or not allowed index pattern', () => {
|
||||
expect(
|
||||
logsDataSourceProfileProvider.resolve({
|
||||
dataSource: createDataViewDataSource({ dataViewId: INVALID_INDEX_PATTERN }),
|
||||
dataView: createStubIndexPattern({ spec: { title: INVALID_INDEX_PATTERN } }),
|
||||
})
|
||||
).toEqual(RESOLUTION_MISMATCH);
|
||||
expect(
|
||||
logsDataSourceProfileProvider.resolve({
|
||||
dataSource: createDataViewDataSource({ dataViewId: MIXED_INDEX_PATTERN }),
|
||||
dataView: createStubIndexPattern({ spec: { title: MIXED_INDEX_PATTERN } }),
|
||||
})
|
||||
).toEqual(RESOLUTION_MISMATCH);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { isOfAggregateQueryType } from '@kbn/es-query';
|
||||
import { getIndexPatternFromESQLQuery } from '@kbn/esql-utils';
|
||||
import { isDataViewSource, isEsqlSource } from '../../../../common/data_sources';
|
||||
import {
|
||||
DataSourceCategory,
|
||||
DataSourceProfileProvider,
|
||||
DataSourceProfileProviderParams,
|
||||
} from '../../profiles';
|
||||
import { ProfileProviderServices } from '../../profiles/profile_provider_services';
|
||||
|
||||
export const createLogsDataSourceProfileProvider = (
|
||||
services: ProfileProviderServices
|
||||
): DataSourceProfileProvider => ({
|
||||
profileId: 'logs-data-source-profile',
|
||||
profile: {},
|
||||
resolve: (params) => {
|
||||
const indexPattern = extractIndexPatternFrom(params);
|
||||
|
||||
if (!services.logsContextService.isLogsIndexPattern(indexPattern)) {
|
||||
return { isMatch: false };
|
||||
}
|
||||
|
||||
return {
|
||||
isMatch: true,
|
||||
context: { category: DataSourceCategory.Logs },
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
const extractIndexPatternFrom = ({
|
||||
dataSource,
|
||||
dataView,
|
||||
query,
|
||||
}: DataSourceProfileProviderParams) => {
|
||||
if (isEsqlSource(dataSource) && isOfAggregateQueryType(query)) {
|
||||
return getIndexPatternFromESQLQuery(query.esql);
|
||||
} else if (isDataViewSource(dataSource) && dataView) {
|
||||
return dataView.getIndexPattern();
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { createLogsContextService, LogsContextService } from '@kbn/discover-utils';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface ProfileProviderDeps {
|
||||
// We will probably soon add uiSettings as a dependency
|
||||
// to consume user configured indices
|
||||
}
|
||||
|
||||
export interface ProfileProviderServices {
|
||||
logsContextService: LogsContextService;
|
||||
}
|
||||
|
||||
export const createProfileProviderServices = (
|
||||
_deps: ProfileProviderDeps = {}
|
||||
): ProfileProviderServices => {
|
||||
return {
|
||||
logsContextService: createLogsContextService(),
|
||||
};
|
||||
};
|
|
@ -9,7 +9,7 @@
|
|||
import type { PluginInitializerContext } from '@kbn/core/public';
|
||||
import { DiscoverPlugin } from './plugin';
|
||||
|
||||
export type { DiscoverSetup, DiscoverStart } from './plugin';
|
||||
export type { DiscoverSetup, DiscoverStart } from './types';
|
||||
export function plugin(initializerContext: PluginInitializerContext) {
|
||||
return new DiscoverPlugin(initializerContext);
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import React, { ComponentType } from 'react';
|
||||
import React from 'react';
|
||||
import { BehaviorSubject, map, Observable } from 'rxjs';
|
||||
import {
|
||||
AppMountParameters,
|
||||
|
@ -17,42 +17,10 @@ import {
|
|||
PluginInitializerContext,
|
||||
ScopedHistory,
|
||||
} from '@kbn/core/public';
|
||||
import { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public';
|
||||
import { ExpressionsSetup, ExpressionsStart } from '@kbn/expressions-plugin/public';
|
||||
import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public';
|
||||
import { ChartsPluginStart } from '@kbn/charts-plugin/public';
|
||||
import type { GlobalSearchPluginSetup } from '@kbn/global-search-plugin/public';
|
||||
import { NavigationPublicPluginStart as NavigationStart } from '@kbn/navigation-plugin/public';
|
||||
import { SharePluginStart, SharePluginSetup } from '@kbn/share-plugin/public';
|
||||
import { UrlForwardingSetup, UrlForwardingStart } from '@kbn/url-forwarding-plugin/public';
|
||||
import { HomePublicPluginSetup } from '@kbn/home-plugin/public';
|
||||
import { Start as InspectorPublicPluginStart } from '@kbn/inspector-plugin/public';
|
||||
import { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/public';
|
||||
import { ENABLE_ESQL } from '@kbn/esql-utils';
|
||||
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
|
||||
import { IndexPatternFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public';
|
||||
import { DataViewsServicePublic } from '@kbn/data-views-plugin/public';
|
||||
import type { SpacesPluginStart } from '@kbn/spaces-plugin/public';
|
||||
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
||||
import { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public';
|
||||
import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public';
|
||||
import { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public';
|
||||
import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public';
|
||||
import type { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public';
|
||||
import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
|
||||
import type { UnifiedDocViewerStart } from '@kbn/unified-doc-viewer-plugin/public';
|
||||
import { setStateToKbnUrl } from '@kbn/kibana-utils-plugin/public';
|
||||
import type { LensPublicStart } from '@kbn/lens-plugin/public';
|
||||
import { TRUNCATE_MAX_HEIGHT } from '@kbn/discover-utils';
|
||||
import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public';
|
||||
import type {
|
||||
ObservabilityAIAssistantPublicSetup,
|
||||
ObservabilityAIAssistantPublicStart,
|
||||
} from '@kbn/observability-ai-assistant-plugin/public';
|
||||
import type { AiopsPluginStart } from '@kbn/aiops-plugin/public';
|
||||
import type { DataVisualizerPluginStart } from '@kbn/data-visualizer-plugin/public';
|
||||
import { PLUGIN_ID } from '../common';
|
||||
import { registerFeature } from './register_feature';
|
||||
import { buildServices, UrlTracker } from './build_services';
|
||||
|
@ -88,132 +56,10 @@ import {
|
|||
ProfilesManager,
|
||||
RootProfileService,
|
||||
} from './context_awareness';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface DiscoverSetup {
|
||||
/**
|
||||
* `share` plugin URL locator for Discover app. Use it to generate links into
|
||||
* Discover application, for example, navigate:
|
||||
*
|
||||
* ```ts
|
||||
* await plugins.discover.locator.navigate({
|
||||
* savedSearchId: '571aaf70-4c88-11e8-b3d7-01146121b73d',
|
||||
* indexPatternId: 'c367b774-a4c2-11ea-bb37-0242ac130002',
|
||||
* timeRange: {
|
||||
* to: 'now',
|
||||
* from: 'now-15m',
|
||||
* mode: 'relative',
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Generate a location:
|
||||
*
|
||||
* ```ts
|
||||
* const location = await plugins.discover.locator.getLocation({
|
||||
* savedSearchId: '571aaf70-4c88-11e8-b3d7-01146121b73d',
|
||||
* indexPatternId: 'c367b774-a4c2-11ea-bb37-0242ac130002',
|
||||
* timeRange: {
|
||||
* to: 'now',
|
||||
* from: 'now-15m',
|
||||
* mode: 'relative',
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
readonly locator: undefined | DiscoverAppLocator;
|
||||
readonly showInlineTopNav: () => void;
|
||||
readonly configureInlineTopNav: (
|
||||
projectNavId: string,
|
||||
options: DiscoverCustomizationContext['inlineTopNav']
|
||||
) => void;
|
||||
}
|
||||
|
||||
export interface DiscoverStart {
|
||||
/**
|
||||
* `share` plugin URL locator for Discover app. Use it to generate links into
|
||||
* Discover application, for example, navigate:
|
||||
*
|
||||
* ```ts
|
||||
* await plugins.discover.locator.navigate({
|
||||
* savedSearchId: '571aaf70-4c88-11e8-b3d7-01146121b73d',
|
||||
* indexPatternId: 'c367b774-a4c2-11ea-bb37-0242ac130002',
|
||||
* timeRange: {
|
||||
* to: 'now',
|
||||
* from: 'now-15m',
|
||||
* mode: 'relative',
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Generate a location:
|
||||
*
|
||||
* ```ts
|
||||
* const location = await plugins.discover.locator.getLocation({
|
||||
* savedSearchId: '571aaf70-4c88-11e8-b3d7-01146121b73d',
|
||||
* indexPatternId: 'c367b774-a4c2-11ea-bb37-0242ac130002',
|
||||
* timeRange: {
|
||||
* to: 'now',
|
||||
* from: 'now-15m',
|
||||
* mode: 'relative',
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
readonly locator: undefined | DiscoverAppLocator;
|
||||
readonly DiscoverContainer: ComponentType<DiscoverContainerProps>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface DiscoverSetupPlugins {
|
||||
dataViews: DataViewsServicePublic;
|
||||
share?: SharePluginSetup;
|
||||
uiActions: UiActionsSetup;
|
||||
embeddable: EmbeddableSetup;
|
||||
urlForwarding: UrlForwardingSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
data: DataPublicPluginSetup;
|
||||
expressions: ExpressionsSetup;
|
||||
globalSearch?: GlobalSearchPluginSetup;
|
||||
observabilityAIAssistant?: ObservabilityAIAssistantPublicSetup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface DiscoverStartPlugins {
|
||||
aiops?: AiopsPluginStart;
|
||||
dataViews: DataViewsServicePublic;
|
||||
dataViewEditor: DataViewEditorStart;
|
||||
dataVisualizer?: DataVisualizerPluginStart;
|
||||
uiActions: UiActionsStart;
|
||||
embeddable: EmbeddableStart;
|
||||
navigation: NavigationStart;
|
||||
charts: ChartsPluginStart;
|
||||
data: DataPublicPluginStart;
|
||||
fieldFormats: FieldFormatsStart;
|
||||
share?: SharePluginStart;
|
||||
urlForwarding: UrlForwardingStart;
|
||||
inspector: InspectorPublicPluginStart;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
dataViewFieldEditor: IndexPatternFieldEditorStart;
|
||||
spaces?: SpacesPluginStart;
|
||||
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
|
||||
expressions: ExpressionsStart;
|
||||
savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart;
|
||||
savedObjectsManagement: SavedObjectsManagementPluginStart;
|
||||
savedSearch: SavedSearchPublicPluginStart;
|
||||
unifiedSearch: UnifiedSearchPublicPluginStart;
|
||||
unifiedDocViewer: UnifiedDocViewerStart;
|
||||
lens: LensPublicStart;
|
||||
contentManagement: ContentManagementPublicStart;
|
||||
noDataPage?: NoDataPagePluginStart;
|
||||
observabilityAIAssistant?: ObservabilityAIAssistantPublicStart;
|
||||
}
|
||||
import { createProfileProviderServices } from './context_awareness/profiles/profile_provider_services';
|
||||
import { DiscoverSetup, DiscoverSetupPlugins, DiscoverStart, DiscoverStartPlugins } from './types';
|
||||
import { createLogsDataSourceProfileProvider } from './context_awareness/profile_providers/logs_data_source_profile';
|
||||
import { createLogDocumentProfileProvider } from './context_awareness/profile_providers/log_document_profile';
|
||||
|
||||
/**
|
||||
* Contains Discover, one of the oldest parts of Kibana
|
||||
|
@ -459,10 +305,14 @@ export class DiscoverPlugin
|
|||
}
|
||||
|
||||
private registerProfiles() {
|
||||
// TODO: Conditionally register example profiles for functional testing in a follow up PR
|
||||
// this.rootProfileService.registerProvider(o11yRootProfileProvider);
|
||||
// this.dataSourceProfileService.registerProvider(logsDataSourceProfileProvider);
|
||||
// this.documentProfileService.registerProvider(logDocumentProfileProvider);
|
||||
const providerServices = createProfileProviderServices();
|
||||
|
||||
this.dataSourceProfileService.registerProvider(
|
||||
createLogsDataSourceProfileProvider(providerServices)
|
||||
);
|
||||
this.documentProfileService.registerProvider(
|
||||
createLogDocumentProfileProvider(providerServices)
|
||||
);
|
||||
}
|
||||
|
||||
private createProfilesManager() {
|
||||
|
|
170
src/plugins/discover/public/types.ts
Normal file
170
src/plugins/discover/public/types.ts
Normal file
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { ComponentType } from 'react';
|
||||
import { UiActionsSetup, UiActionsStart } from '@kbn/ui-actions-plugin/public';
|
||||
import { ExpressionsSetup, ExpressionsStart } from '@kbn/expressions-plugin/public';
|
||||
import { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public';
|
||||
import { ChartsPluginStart } from '@kbn/charts-plugin/public';
|
||||
import type { GlobalSearchPluginSetup } from '@kbn/global-search-plugin/public';
|
||||
import { NavigationPublicPluginStart as NavigationStart } from '@kbn/navigation-plugin/public';
|
||||
import { SharePluginStart, SharePluginSetup } from '@kbn/share-plugin/public';
|
||||
import { UrlForwardingSetup, UrlForwardingStart } from '@kbn/url-forwarding-plugin/public';
|
||||
import { HomePublicPluginSetup } from '@kbn/home-plugin/public';
|
||||
import { Start as InspectorPublicPluginStart } from '@kbn/inspector-plugin/public';
|
||||
import { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { UsageCollectionSetup } from '@kbn/usage-collection-plugin/public';
|
||||
import { IndexPatternFieldEditorStart } from '@kbn/data-view-field-editor-plugin/public';
|
||||
import { DataViewsServicePublic } from '@kbn/data-views-plugin/public';
|
||||
import type { SpacesPluginStart } from '@kbn/spaces-plugin/public';
|
||||
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
||||
import { DataViewEditorStart } from '@kbn/data-view-editor-plugin/public';
|
||||
import { ContentManagementPublicStart } from '@kbn/content-management-plugin/public';
|
||||
import { TriggersAndActionsUIPublicPluginStart } from '@kbn/triggers-actions-ui-plugin/public';
|
||||
import type { SavedObjectTaggingOssPluginStart } from '@kbn/saved-objects-tagging-oss-plugin/public';
|
||||
import type { SavedObjectsManagementPluginStart } from '@kbn/saved-objects-management-plugin/public';
|
||||
import type { SavedSearchPublicPluginStart } from '@kbn/saved-search-plugin/public';
|
||||
import type { UnifiedSearchPublicPluginStart } from '@kbn/unified-search-plugin/public';
|
||||
import type { UnifiedDocViewerStart } from '@kbn/unified-doc-viewer-plugin/public';
|
||||
import type { LensPublicStart } from '@kbn/lens-plugin/public';
|
||||
import type { NoDataPagePluginStart } from '@kbn/no-data-page-plugin/public';
|
||||
import type {
|
||||
ObservabilityAIAssistantPublicSetup,
|
||||
ObservabilityAIAssistantPublicStart,
|
||||
} from '@kbn/observability-ai-assistant-plugin/public';
|
||||
import type { AiopsPluginStart } from '@kbn/aiops-plugin/public';
|
||||
import type { DataVisualizerPluginStart } from '@kbn/data-visualizer-plugin/public';
|
||||
import { DiscoverAppLocator } from '../common';
|
||||
import { DiscoverCustomizationContext } from './customizations';
|
||||
import { type DiscoverContainerProps } from './components/discover_container';
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface DiscoverSetup {
|
||||
/**
|
||||
* `share` plugin URL locator for Discover app. Use it to generate links into
|
||||
* Discover application, for example, navigate:
|
||||
*
|
||||
* ```ts
|
||||
* await plugins.discover.locator.navigate({
|
||||
* savedSearchId: '571aaf70-4c88-11e8-b3d7-01146121b73d',
|
||||
* indexPatternId: 'c367b774-a4c2-11ea-bb37-0242ac130002',
|
||||
* timeRange: {
|
||||
* to: 'now',
|
||||
* from: 'now-15m',
|
||||
* mode: 'relative',
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Generate a location:
|
||||
*
|
||||
* ```ts
|
||||
* const location = await plugins.discover.locator.getLocation({
|
||||
* savedSearchId: '571aaf70-4c88-11e8-b3d7-01146121b73d',
|
||||
* indexPatternId: 'c367b774-a4c2-11ea-bb37-0242ac130002',
|
||||
* timeRange: {
|
||||
* to: 'now',
|
||||
* from: 'now-15m',
|
||||
* mode: 'relative',
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
readonly locator: undefined | DiscoverAppLocator;
|
||||
readonly showInlineTopNav: () => void;
|
||||
readonly configureInlineTopNav: (
|
||||
projectNavId: string,
|
||||
options: DiscoverCustomizationContext['inlineTopNav']
|
||||
) => void;
|
||||
}
|
||||
|
||||
export interface DiscoverStart {
|
||||
/**
|
||||
* `share` plugin URL locator for Discover app. Use it to generate links into
|
||||
* Discover application, for example, navigate:
|
||||
*
|
||||
* ```ts
|
||||
* await plugins.discover.locator.navigate({
|
||||
* savedSearchId: '571aaf70-4c88-11e8-b3d7-01146121b73d',
|
||||
* indexPatternId: 'c367b774-a4c2-11ea-bb37-0242ac130002',
|
||||
* timeRange: {
|
||||
* to: 'now',
|
||||
* from: 'now-15m',
|
||||
* mode: 'relative',
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* Generate a location:
|
||||
*
|
||||
* ```ts
|
||||
* const location = await plugins.discover.locator.getLocation({
|
||||
* savedSearchId: '571aaf70-4c88-11e8-b3d7-01146121b73d',
|
||||
* indexPatternId: 'c367b774-a4c2-11ea-bb37-0242ac130002',
|
||||
* timeRange: {
|
||||
* to: 'now',
|
||||
* from: 'now-15m',
|
||||
* mode: 'relative',
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*/
|
||||
readonly locator: undefined | DiscoverAppLocator;
|
||||
readonly DiscoverContainer: ComponentType<DiscoverContainerProps>;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface DiscoverSetupPlugins {
|
||||
data: DataPublicPluginSetup;
|
||||
dataViews: DataViewsServicePublic;
|
||||
embeddable: EmbeddableSetup;
|
||||
expressions: ExpressionsSetup;
|
||||
globalSearch?: GlobalSearchPluginSetup;
|
||||
home?: HomePublicPluginSetup;
|
||||
observabilityAIAssistant?: ObservabilityAIAssistantPublicSetup;
|
||||
share?: SharePluginSetup;
|
||||
uiActions: UiActionsSetup;
|
||||
urlForwarding: UrlForwardingSetup;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export interface DiscoverStartPlugins {
|
||||
aiops?: AiopsPluginStart;
|
||||
charts: ChartsPluginStart;
|
||||
contentManagement: ContentManagementPublicStart;
|
||||
data: DataPublicPluginStart;
|
||||
dataViewEditor: DataViewEditorStart;
|
||||
dataViewFieldEditor: IndexPatternFieldEditorStart;
|
||||
dataViews: DataViewsServicePublic;
|
||||
dataVisualizer?: DataVisualizerPluginStart;
|
||||
embeddable: EmbeddableStart;
|
||||
expressions: ExpressionsStart;
|
||||
fieldFormats: FieldFormatsStart;
|
||||
inspector: InspectorPublicPluginStart;
|
||||
lens: LensPublicStart;
|
||||
navigation: NavigationStart;
|
||||
noDataPage?: NoDataPagePluginStart;
|
||||
observabilityAIAssistant?: ObservabilityAIAssistantPublicStart;
|
||||
savedObjectsManagement: SavedObjectsManagementPluginStart;
|
||||
savedObjectsTaggingOss?: SavedObjectTaggingOssPluginStart;
|
||||
savedSearch: SavedSearchPublicPluginStart;
|
||||
share?: SharePluginStart;
|
||||
spaces?: SpacesPluginStart;
|
||||
triggersActionsUi: TriggersAndActionsUIPublicPluginStart;
|
||||
uiActions: UiActionsStart;
|
||||
unifiedDocViewer: UnifiedDocViewerStart;
|
||||
unifiedSearch: UnifiedSearchPublicPluginStart;
|
||||
urlForwarding: UrlForwardingStart;
|
||||
usageCollection?: UsageCollectionSetup;
|
||||
}
|
|
@ -8,7 +8,7 @@
|
|||
import { AppUpdater } from '@kbn/core/public';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { coreMock, scopedHistoryMock } from '@kbn/core/public/mocks';
|
||||
import { DiscoverSetupPlugins } from '../plugin';
|
||||
import { DiscoverSetupPlugins } from '../types';
|
||||
import { initializeKbnUrlTracking } from './initialize_kbn_url_tracking';
|
||||
|
||||
describe('initializeKbnUrlTracking', () => {
|
||||
|
|
|
@ -12,7 +12,7 @@ import { createKbnUrlTracker } from '@kbn/kibana-utils-plugin/public';
|
|||
import { replaceUrlHashQuery } from '@kbn/kibana-utils-plugin/common';
|
||||
import { isFilterPinned } from '@kbn/es-query';
|
||||
import { SEARCH_SESSION_ID_QUERY_PARAM } from '../constants';
|
||||
import type { DiscoverSetupPlugins } from '../plugin';
|
||||
import type { DiscoverSetupPlugins } from '../types';
|
||||
|
||||
/**
|
||||
* It creates the kbn url tracker for Discover to listens to history changes and optionally to global state
|
||||
|
|
|
@ -20,7 +20,7 @@ import {
|
|||
} from '@kbn/core/public';
|
||||
import type { DataPublicPluginSetup, DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
|
||||
import { DiscoverSetup, DiscoverStart } from '@kbn/discover-plugin/public/plugin';
|
||||
import { DiscoverSetup, DiscoverStart } from '@kbn/discover-plugin/public';
|
||||
import type { EmbeddableSetup, EmbeddableStart } from '@kbn/embeddable-plugin/public';
|
||||
import type { ExploratoryViewPublicSetup } from '@kbn/exploratory-view-plugin/public';
|
||||
import type { FeaturesPluginSetup } from '@kbn/features-plugin/public';
|
||||
|
|
|
@ -59,6 +59,3 @@ export const FILTER_OUT_FIELDS_PREFIXES_FOR_CONTENT = [
|
|||
'log.',
|
||||
'service.',
|
||||
];
|
||||
|
||||
export const DEFAULT_ALLOWED_DATA_VIEWS = ['logs', 'auditbeat', 'filebeat', 'winlogbeat'];
|
||||
export const DEFAULT_ALLOWED_LOGS_DATA_VIEWS = ['logs', 'auditbeat', 'filebeat', 'winlogbeat'];
|
||||
|
|
|
@ -5,15 +5,13 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { DEFAULT_ALLOWED_LOGS_DATA_VIEWS } from '../../constants';
|
||||
import { createRegExpPatternFrom, testPatternAgainstAllowedList } from '@kbn/data-view-utils';
|
||||
import { DEFAULT_ALLOWED_LOGS_BASE_PATTERNS } from '@kbn/discover-utils';
|
||||
import { DataViewSpecWithId } from '../../data_source_selection';
|
||||
import { DataViewDescriptorType } from '../types';
|
||||
import { buildIndexPatternRegExp } from '../utils';
|
||||
|
||||
type AllowedList = Array<string | RegExp>;
|
||||
|
||||
const LOGS_ALLOWED_LIST: AllowedList = [
|
||||
buildIndexPatternRegExp(DEFAULT_ALLOWED_LOGS_DATA_VIEWS),
|
||||
const LOGS_ALLOWED_LIST = [
|
||||
createRegExpPatternFrom(DEFAULT_ALLOWED_LOGS_BASE_PATTERNS),
|
||||
// Add more strings or regex patterns as needed
|
||||
];
|
||||
|
||||
|
@ -60,7 +58,9 @@ export class DataViewDescriptor {
|
|||
}
|
||||
|
||||
testAgainstAllowedList(allowedList: string[]) {
|
||||
return this.title ? isAllowed(this.title, [buildIndexPatternRegExp(allowedList)]) : false;
|
||||
return this.title
|
||||
? testPatternAgainstAllowedList([createRegExpPatternFrom(allowedList)])(this.title)
|
||||
: false;
|
||||
}
|
||||
|
||||
public static create({ id, namespaces, title, type, name }: DataViewDescriptorFactoryParams) {
|
||||
|
@ -79,7 +79,7 @@ export class DataViewDescriptor {
|
|||
}
|
||||
|
||||
static #extractDataType(title: string): DataViewDescriptorType['dataType'] {
|
||||
if (isAllowed(title, LOGS_ALLOWED_LIST)) {
|
||||
if (testPatternAgainstAllowedList(LOGS_ALLOWED_LIST)(title)) {
|
||||
return 'logs';
|
||||
}
|
||||
|
||||
|
@ -98,17 +98,3 @@ export class DataViewDescriptor {
|
|||
return this.dataType === 'unresolved';
|
||||
}
|
||||
}
|
||||
|
||||
function isAllowed(value: string, allowList: AllowedList) {
|
||||
for (const allowedItem of allowList) {
|
||||
if (typeof allowedItem === 'string') {
|
||||
return value === allowedItem;
|
||||
}
|
||||
if (allowedItem instanceof RegExp) {
|
||||
return allowedItem.test(value);
|
||||
}
|
||||
}
|
||||
|
||||
// If no match is found in the allowList, return false
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { UiSettingsParams } from '@kbn/core-ui-settings-common';
|
||||
import { DEFAULT_ALLOWED_LOGS_BASE_PATTERNS } from '@kbn/discover-utils';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { OBSERVABILITY_LOGS_EXPLORER_ALLOWED_DATA_VIEWS_ID } from '@kbn/management-settings-ids';
|
||||
import { DEFAULT_ALLOWED_DATA_VIEWS } from './constants';
|
||||
|
||||
/**
|
||||
* uiSettings definitions for Logs Explorer.
|
||||
|
@ -20,7 +20,7 @@ export const uiSettings: Record<string, UiSettingsParams> = {
|
|||
name: i18n.translate('xpack.logsExplorer.allowedDataViews', {
|
||||
defaultMessage: 'Logs Explorer allowed data views',
|
||||
}),
|
||||
value: DEFAULT_ALLOWED_DATA_VIEWS,
|
||||
value: DEFAULT_ALLOWED_LOGS_BASE_PATTERNS,
|
||||
description: i18n.translate('xpack.logsExplorer.allowedDataViewsDescription', {
|
||||
defaultMessage:
|
||||
'A list of base patterns to match and explore data views in Logs Explorer. Remote clusters will be automatically matched for the provided base patterns.',
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
"@kbn/unified-search-plugin",
|
||||
"@kbn/xstate-utils",
|
||||
"@kbn/esql-utils",
|
||||
"@kbn/data-view-utils",
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*"
|
||||
|
|
|
@ -654,7 +654,7 @@ export default function ({ getService, getPageObjects }: FtrProviderContext) {
|
|||
PageObjects.observabilityLogsExplorer.getPanelEntries(menu)
|
||||
);
|
||||
|
||||
expect(menuEntries.length).to.be(1);
|
||||
expect(menuEntries.length).to.be(2);
|
||||
expect(await menuEntries[0].getVisibleText()).to.be(sortedExpectedDataViews[0]);
|
||||
});
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue