mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 01:13:23 -04:00
[Discover] Create Discover Shared plugin and features registry (#181952)
## 📓 Summary Closes #181528 Closes #181976 **N.B.** This work was initially reviewed on a separate PR at https://github.com/tonyghiani/kibana/pull/1. This implementation aims to have a stateful layer that allows the management of dependencies between Discover and other plugins, reducing the need for a direct dependency. Although the initial thought was to have a plugin to register features for Discover, there might be other cases in future where we need to prevent cyclic dependencies. With this in mind, I created the plugin as a more generic solution to hold stateful logic as a communication layer for Discover <-> Plugins. ## Discover Shared Based on some recurring naming in the Kibana codebase, `discover_shared` felt like the right place for owning these dependencies and exposing Discover functionalities to the external world. It is initially pretty simple and only exposes a registry for the Discover features, but might be a good place to implement other upcoming concepts related to Discover. Also, this plugin should ideally never depend on other solution plugins and keep its dependencies to a bare minimum of packages and core/data services. ```mermaid flowchart TD A(Discover) -- Get --> E[DiscoverShared] B(Logs Explorer) -- Set --> E[DiscoverShared] C(Security) -- Set --> E[DiscoverShared] D(Any app) -- Set --> E[DiscoverShared] ``` ## DiscoverFeaturesService This service initializes and exposes a strictly typed registry to allow consumer apps to register additional features and Discover and retrieve them. The **README** file explains a real use case of when we'd need to use it and how to do that step-by-step. Although it introduces a more nested folder structure, I decided to implement the service as a client-service and expose it through the plugin lifecycle methods to provide the necessary flexibility we might need: - We don't know yet if any of the features we register will be done on the setup/start steps, so having the registry available in both places opens the door to any case we face. - The service is client-only on purpose. My opinion is that if we ever need to register features such as server services or anything else, it should be scoped to a similar service dedicated for the server lifecycle and its environment. It should never be possible to register the ObsAIAssistant presentational component from the server, as it should not be permitted to register a server service in the client registry. A server DiscoverFeaturesService is not required yet for any feature, so I left it out to avoid overcomplicating the implementation. ## FeaturesRegistry To have a strictly typed utility that suggests the available features on a registry and adheres to a base contract, the registry exposed on the DiscoverFeaturesService is an instance of the `FeaturesRegistry` class, which implements the registration/retrieval logic such that: - If a feature is already registered, is not possible to override it and an error is thrown to notify the user of the collision. - In case we need to react to registry changes, is possible to subscribe to the registry or obtain it as an observable for more complex scenarios. The FeaturesRegistry already takes care of the required logic for the registry, so that `DiscoverFeaturesService`is left with the responsibility of instantiating/exposing an instance and provide the set of allowed features. --------- Co-authored-by: Marco Antonio Ghiani <marcoantonio.ghiani@elastic.co> Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Co-authored-by: Davis McPhee <davismcphee@hotmail.com>
This commit is contained in:
parent
a6491ab360
commit
747ecea18a
45 changed files with 550 additions and 369 deletions
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
|
@ -370,6 +370,7 @@ examples/developer_examples @elastic/appex-sharedux
|
|||
examples/discover_customization_examples @elastic/kibana-data-discovery
|
||||
x-pack/plugins/discover_enhanced @elastic/kibana-data-discovery
|
||||
src/plugins/discover @elastic/kibana-data-discovery
|
||||
src/plugins/discover_shared @elastic/kibana-data-discovery @elastic/obs-ux-logs-team
|
||||
packages/kbn-discover-utils @elastic/kibana-data-discovery
|
||||
packages/kbn-doc-links @elastic/docs
|
||||
packages/kbn-docs-utils @elastic/kibana-operations
|
||||
|
|
|
@ -94,6 +94,10 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a
|
|||
|Contains the Discover application and the saved search embeddable.
|
||||
|
||||
|
||||
|{kib-repo}blob/{branch}/src/plugins/discover_shared/README.md[discoverShared]
|
||||
|A stateful layer to register shared features and provide an access point to discover without a direct dependency.
|
||||
|
||||
|
||||
|{kib-repo}blob/{branch}/src/plugins/embeddable/README.md[embeddable]
|
||||
|The Embeddables Plugin provides an opportunity to expose reusable interactive widgets that can be embedded outside the original plugin.
|
||||
|
||||
|
|
|
@ -420,6 +420,7 @@
|
|||
"@kbn/discover-customization-examples-plugin": "link:examples/discover_customization_examples",
|
||||
"@kbn/discover-enhanced-plugin": "link:x-pack/plugins/discover_enhanced",
|
||||
"@kbn/discover-plugin": "link:src/plugins/discover",
|
||||
"@kbn/discover-shared-plugin": "link:src/plugins/discover_shared",
|
||||
"@kbn/discover-utils": "link:packages/kbn-discover-utils",
|
||||
"@kbn/doc-links": "link:packages/kbn-doc-links",
|
||||
"@kbn/dom-drag-drop": "link:packages/kbn-dom-drag-drop",
|
||||
|
|
|
@ -37,6 +37,7 @@ pageLoadAssetSize:
|
|||
devTools: 38637
|
||||
discover: 99999
|
||||
discoverEnhanced: 42730
|
||||
discoverShared: 17111
|
||||
embeddable: 87309
|
||||
embeddableEnhanced: 22107
|
||||
enterpriseSearch: 50858
|
||||
|
@ -143,7 +144,7 @@ pageLoadAssetSize:
|
|||
snapshotRestore: 79032
|
||||
spaces: 57868
|
||||
stackAlerts: 58316
|
||||
stackConnectors: 52131
|
||||
stackConnectors: 67227
|
||||
synthetics: 40958
|
||||
telemetry: 51957
|
||||
telemetryManagementSection: 38586
|
||||
|
|
90
src/plugins/discover_shared/README.md
Executable file
90
src/plugins/discover_shared/README.md
Executable file
|
@ -0,0 +1,90 @@
|
|||
# Discover Shared
|
||||
|
||||
A stateful layer to register shared features and provide an access point to discover without a direct dependency.
|
||||
|
||||
## Register new features
|
||||
|
||||
The plugin exposes a service to register features that can be opinionatedly used in Discover on both the setup and start lifecycle hooks.
|
||||
|
||||
Although this allows for greater flexibility, its purpose is not to customize Discover as a default choice but to be used as a solution to prevent cyclic dependency between plugins that interact with Discover.
|
||||
|
||||
To register a new feature, let's take a more practical case.
|
||||
|
||||
> _We want to introduce the LogsAIAssistant in the Discover flyout. Porting all the logic of the Observability AI Assistant into Discover is not an option, and we don't want Discover to directly depend on the AI Assistant codebase._
|
||||
|
||||
We can solve this case with some steps:
|
||||
|
||||
### Define a feature registration contract
|
||||
|
||||
First of all, we need to define an interface to which the plugin registering the AI Assistant and Discover can adhere.
|
||||
|
||||
The `DiscoverFeaturesService` already defines a union of available features and uses them to strictly type the exposed registry from the discover_shared plugin, so we can update it with the new feature:
|
||||
|
||||
```tsx
|
||||
// src/plugins/discover_shared/public/services/discover_features/types.ts
|
||||
|
||||
export interface SecurityAIAssistantFeature {
|
||||
id: 'security-ai-assistant';
|
||||
render: (/* Update with deps required for this integration */) => React.ReactNode;
|
||||
// Add any prop required for the feature
|
||||
}
|
||||
|
||||
export interface ObservabilityLogsAIAssistantFeature {
|
||||
id: 'observability-logs-ai-assistant';
|
||||
render: (deps: {doc: DataTableRecord}) => React.ReactNode;
|
||||
// Add any prop required for the feature
|
||||
}
|
||||
|
||||
// This should be a union of all the available client features.
|
||||
export type DiscoverFeature = SecurityAIAssistantFeature | ObservabilityLogsAIAssistantFeature;
|
||||
```
|
||||
|
||||
### Discover consumes the registered feature
|
||||
|
||||
Once we have an interface for the feature, Discover can now retrieve it and use its content if is registered by any app in Kibana.
|
||||
|
||||
```tsx
|
||||
// Somewhere in the unified doc viewer
|
||||
|
||||
function LogsOverviewAIAssistant ({ doc }) {
|
||||
const { discoverShared } = getUnifiedDocViewerServices();
|
||||
|
||||
const logsAIAssistantFeature = discoverShared.features.registry.getById('observability-logs-ai-assistant')
|
||||
|
||||
if (logsAIAssistantFeature) {
|
||||
return logsAIAssistantFeature.render({ doc })
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Register the feature
|
||||
|
||||
Having an interface for the feature and Discover consuming its definition, we are left with the registration part.
|
||||
|
||||
For our example, we'll go to the logs app that owns the LogsAIAssistant codebase and register the feature:
|
||||
|
||||
```tsx
|
||||
// x-pack/plugins/observability_solution/logs_shared/public/plugin.ts
|
||||
|
||||
export class LogsSharedPlugin implements LogsSharedClientPluginClass {
|
||||
// The rest of the plugin implementation is hidden for a cleaner example
|
||||
|
||||
public start(core: CoreStart, plugins: LogsSharedClientStartDeps) {
|
||||
const { observabilityAIAssistant } = plugins;
|
||||
|
||||
const LogAIAssistant = createLogAIAssistant({ observabilityAIAssistant });
|
||||
|
||||
// Strict typing on the registry will let you know which features you can register
|
||||
plugins.discoverShared.features.registry.register({
|
||||
id: 'observability-logs-ai-assistant',
|
||||
render: ({doc}) => <LogAIAssistant doc={doc}/>
|
||||
})
|
||||
|
||||
return {
|
||||
LogAIAssistant,
|
||||
};
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
At this point, the feature should work correctly when registered and we have not created any direct dependency between the Discover and LogsShared apps.
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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 { FeaturesRegistry } from './features_registry';
|
||||
|
||||
type TestFeature =
|
||||
| { id: 'feature-id-1'; adHocProperty1?: string }
|
||||
| { id: 'feature-id-2'; adHocProperty2?: string }
|
||||
| { id: 'feature-id-3'; adHocProperty3?: string };
|
||||
|
||||
describe('FeaturesRegistry', () => {
|
||||
describe('#register', () => {
|
||||
test('should add a feature to the registry', () => {
|
||||
const registry = new FeaturesRegistry<TestFeature>();
|
||||
|
||||
registry.register({ id: 'feature-id-1' });
|
||||
|
||||
expect(registry.getById('feature-id-1')).toBeDefined();
|
||||
});
|
||||
|
||||
test('should throw an error when a feature is already registered by the given id', () => {
|
||||
const registry = new FeaturesRegistry<TestFeature>();
|
||||
|
||||
registry.register({ id: 'feature-id-1' });
|
||||
|
||||
expect(() => registry.register({ id: 'feature-id-1' })).toThrow(
|
||||
'FeaturesRegistry#register: feature with id "feature-id-1" already exists in the registry.'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getById', () => {
|
||||
test('should retrieve a feature by its id', () => {
|
||||
const registry = new FeaturesRegistry<TestFeature>();
|
||||
|
||||
registry.register({ id: 'feature-id-1', adHocProperty1: 'test' });
|
||||
registry.register({ id: 'feature-id-2', adHocProperty2: 'test' });
|
||||
|
||||
expect(registry.getById('feature-id-1')).toEqual({
|
||||
id: 'feature-id-1',
|
||||
adHocProperty1: 'test',
|
||||
});
|
||||
});
|
||||
|
||||
test('should return undefined if there is no feature registered by the given id', () => {
|
||||
const registry = new FeaturesRegistry<TestFeature>();
|
||||
|
||||
registry.register({ id: 'feature-id-1' });
|
||||
|
||||
expect(registry.getById('feature-id-2')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 { BaseFeature } from './types';
|
||||
|
||||
export class FeaturesRegistry<Feature extends BaseFeature = BaseFeature> {
|
||||
private readonly features = new Map<Feature['id'], Feature>();
|
||||
|
||||
register(feature: Feature): void {
|
||||
if (this.features.has(feature.id)) {
|
||||
throw new Error(
|
||||
`FeaturesRegistry#register: feature with id "${feature.id}" already exists in the registry.`
|
||||
);
|
||||
}
|
||||
|
||||
this.features.set(feature.id, feature);
|
||||
}
|
||||
|
||||
getById<Id extends Feature['id']>(id: Id) {
|
||||
return this.features.get(id) as Extract<Feature, { id: Id }> | undefined;
|
||||
}
|
||||
}
|
|
@ -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 { FeaturesRegistry } from './features_registry';
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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 interface BaseFeature {
|
||||
id: string;
|
||||
}
|
9
src/plugins/discover_shared/common/index.ts
Normal file
9
src/plugins/discover_shared/common/index.ts
Normal file
|
@ -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 { FeaturesRegistry } from './features_registry';
|
18
src/plugins/discover_shared/jest.config.js
Normal file
18
src/plugins/discover_shared/jest.config.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
preset: '@kbn/test',
|
||||
rootDir: '../../..',
|
||||
roots: ['<rootDir>/src/plugins/discover_shared'],
|
||||
coverageDirectory: '<rootDir>/target/kibana-coverage/jest/src/plugins/discover_shared',
|
||||
coverageReporters: ['text', 'html'],
|
||||
collectCoverageFrom: [
|
||||
'<rootDir>/src/plugins/discover_shared/{common,public,server}/**/*.{js,ts,tsx}',
|
||||
],
|
||||
};
|
13
src/plugins/discover_shared/kibana.jsonc
Normal file
13
src/plugins/discover_shared/kibana.jsonc
Normal file
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"type": "plugin",
|
||||
"id": "@kbn/discover-shared-plugin",
|
||||
"owner": ["@elastic/kibana-data-discovery", "@elastic/obs-ux-logs-team"],
|
||||
"description": "A stateful layer to register shared features and provide an access point to discover without a direct dependency",
|
||||
"plugin": {
|
||||
"id": "discoverShared",
|
||||
"server": false,
|
||||
"browser": true,
|
||||
"requiredPlugins": [],
|
||||
"optionalPlugins": [],
|
||||
},
|
||||
}
|
20
src/plugins/discover_shared/public/index.ts
Normal file
20
src/plugins/discover_shared/public/index.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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 { DiscoverSharedPlugin } from './plugin';
|
||||
|
||||
export function plugin() {
|
||||
return new DiscoverSharedPlugin();
|
||||
}
|
||||
|
||||
export type { DiscoverSharedPublicSetup, DiscoverSharedPublicStart } from './types';
|
||||
export type {
|
||||
ObservabilityLogsAIAssistantFeatureRenderDeps,
|
||||
ObservabilityLogsAIAssistantFeature,
|
||||
DiscoverFeature,
|
||||
} from './services/discover_features';
|
33
src/plugins/discover_shared/public/mocks.ts
Normal file
33
src/plugins/discover_shared/public/mocks.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 {
|
||||
createDiscoverFeaturesServiceSetupMock,
|
||||
createDiscoverFeaturesServiceStartMock,
|
||||
} from './services/discover_features/discover_features_service.mock';
|
||||
import { DiscoverSharedPublicSetup, DiscoverSharedPublicStart } from './types';
|
||||
|
||||
export type Setup = jest.Mocked<DiscoverSharedPublicSetup>;
|
||||
export type Start = jest.Mocked<DiscoverSharedPublicStart>;
|
||||
|
||||
const createSetupContract = (): Setup => {
|
||||
return {
|
||||
features: createDiscoverFeaturesServiceSetupMock(),
|
||||
};
|
||||
};
|
||||
|
||||
const createStartContract = (): Start => {
|
||||
return {
|
||||
features: createDiscoverFeaturesServiceStartMock(),
|
||||
};
|
||||
};
|
||||
|
||||
export const discoverSharedPluginMock = {
|
||||
createSetupContract,
|
||||
createStartContract,
|
||||
};
|
26
src/plugins/discover_shared/public/plugin.ts
Normal file
26
src/plugins/discover_shared/public/plugin.ts
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 { DiscoverFeaturesService } from './services/discover_features';
|
||||
import { DiscoverSharedPublicPlugin } from './types';
|
||||
|
||||
export class DiscoverSharedPlugin implements DiscoverSharedPublicPlugin {
|
||||
private discoverFeaturesService: DiscoverFeaturesService = new DiscoverFeaturesService();
|
||||
|
||||
public setup() {
|
||||
return {
|
||||
features: this.discoverFeaturesService.setup(),
|
||||
};
|
||||
}
|
||||
|
||||
public start() {
|
||||
return {
|
||||
features: this.discoverFeaturesService.start(),
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* 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 { FeaturesRegistry } from '../../../common';
|
||||
import { DiscoverFeature } from './types';
|
||||
|
||||
const registry = new FeaturesRegistry<DiscoverFeature>();
|
||||
|
||||
export const createDiscoverFeaturesServiceSetupMock = () => ({ registry });
|
||||
export const createDiscoverFeaturesServiceStartMock = () => ({ registry });
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* 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 { FeaturesRegistry } from '../../../common';
|
||||
import { DiscoverFeature } from './types';
|
||||
|
||||
export class DiscoverFeaturesService {
|
||||
private registry: FeaturesRegistry<DiscoverFeature> = new FeaturesRegistry();
|
||||
|
||||
public setup() {
|
||||
return {
|
||||
registry: this.registry,
|
||||
};
|
||||
}
|
||||
|
||||
public start() {
|
||||
return {
|
||||
registry: this.registry,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* 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 * from './discover_features_service';
|
||||
export * from './types';
|
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
* 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 { FeaturesRegistry } from '../../../common';
|
||||
|
||||
/**
|
||||
* Features types
|
||||
* Here goes the contract definition for the client features that can be registered
|
||||
* and that will be consumed by Discover.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allow to register an AIAssistant scoped to investigate log entries.
|
||||
* It will be opinionatedly used as an additional tool to investigate a log document and
|
||||
* will be shown on the logs-overview preset tab of the UnifiedDocViewer.
|
||||
*/
|
||||
|
||||
export interface ObservabilityLogsAIAssistantFeatureRenderDeps {
|
||||
doc: DataTableRecord;
|
||||
}
|
||||
export interface ObservabilityLogsAIAssistantFeature {
|
||||
id: 'observability-logs-ai-assistant';
|
||||
render: (deps: ObservabilityLogsAIAssistantFeatureRenderDeps) => JSX.Element;
|
||||
}
|
||||
|
||||
// This should be a union of all the available client features.
|
||||
export type DiscoverFeature = ObservabilityLogsAIAssistantFeature;
|
||||
|
||||
/**
|
||||
* Service types
|
||||
*/
|
||||
export interface DiscoverFeaturesServiceSetup {
|
||||
registry: FeaturesRegistry<DiscoverFeature>;
|
||||
}
|
||||
|
||||
export interface DiscoverFeaturesServiceStart {
|
||||
registry: FeaturesRegistry<DiscoverFeature>;
|
||||
}
|
33
src/plugins/discover_shared/public/types.ts
Normal file
33
src/plugins/discover_shared/public/types.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 { Plugin } from '@kbn/core/public';
|
||||
import {
|
||||
DiscoverFeaturesServiceSetup,
|
||||
DiscoverFeaturesServiceStart,
|
||||
} from './services/discover_features';
|
||||
|
||||
export interface DiscoverSharedPublicSetup {
|
||||
features: DiscoverFeaturesServiceSetup;
|
||||
}
|
||||
|
||||
export interface DiscoverSharedPublicStart {
|
||||
features: DiscoverFeaturesServiceStart;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface DiscoverSharedPublicSetupDeps {}
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||
export interface DiscoverSharedPublicStartDeps {}
|
||||
|
||||
export type DiscoverSharedPublicPlugin = Plugin<
|
||||
DiscoverSharedPublicSetup,
|
||||
DiscoverSharedPublicStart,
|
||||
DiscoverSharedPublicSetupDeps,
|
||||
DiscoverSharedPublicStartDeps
|
||||
>;
|
17
src/plugins/discover_shared/tsconfig.json
Normal file
17
src/plugins/discover_shared/tsconfig.json
Normal file
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "target/types"
|
||||
},
|
||||
"include": [
|
||||
"common/**/*",
|
||||
"public/**/*",
|
||||
"server/**/*",
|
||||
"../../../typings/**/*",
|
||||
],
|
||||
"exclude": ["target/**/*"],
|
||||
"kbn_references": [
|
||||
"@kbn/discover-utils",
|
||||
"@kbn/core",
|
||||
]
|
||||
}
|
|
@ -8,6 +8,6 @@
|
|||
"server": false,
|
||||
"browser": true,
|
||||
"requiredBundles": ["kibanaUtils"],
|
||||
"requiredPlugins": ["data", "fieldFormats"],
|
||||
"requiredPlugins": ["data", "discoverShared", "fieldFormats"],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
import { analyticsServiceMock } from '@kbn/core-analytics-browser-mocks';
|
||||
import { dataPluginMock } from '@kbn/data-plugin/public/mocks';
|
||||
import { discoverSharedPluginMock } from '@kbn/discover-shared-plugin/public/mocks';
|
||||
import { fieldFormatsMock } from '@kbn/field-formats-plugin/common/mocks';
|
||||
import { uiSettingsServiceMock } from '@kbn/core-ui-settings-browser-mocks';
|
||||
import type { UnifiedDocViewerServices, UnifiedDocViewerStart } from '../types';
|
||||
|
@ -21,6 +22,7 @@ export const mockUnifiedDocViewer: jest.Mocked<UnifiedDocViewerStart> = {
|
|||
export const mockUnifiedDocViewerServices: jest.Mocked<UnifiedDocViewerServices> = {
|
||||
analytics: analyticsServiceMock.createAnalyticsServiceStart(),
|
||||
data: dataPluginMock.createStartContract(),
|
||||
discoverShared: discoverSharedPluginMock.createStartContract(),
|
||||
fieldFormats: fieldFormatsMock,
|
||||
storage: new Storage(localStorage),
|
||||
uiSettings: uiSettingsServiceMock.createStartContract(),
|
||||
|
|
|
@ -14,6 +14,7 @@ import { LogsOverviewHeader } from './logs_overview_header';
|
|||
import { LogsOverviewHighlights } from './logs_overview_highlights';
|
||||
import { FieldActionsProvider } from '../../hooks/use_field_actions';
|
||||
import { getUnifiedDocViewerServices } from '../../plugin';
|
||||
import { LogsOverviewAIAssistant } from './logs_overview_ai_assistant';
|
||||
|
||||
export function LogsOverview({
|
||||
columns,
|
||||
|
@ -37,6 +38,7 @@ export function LogsOverview({
|
|||
<LogsOverviewHeader doc={parsedDoc} />
|
||||
<EuiHorizontalRule margin="xs" />
|
||||
<LogsOverviewHighlights formattedDoc={parsedDoc} flattenedDoc={hit.flattened} />
|
||||
<LogsOverviewAIAssistant doc={hit} />
|
||||
</FieldActionsProvider>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 { DataTableRecord } from '@kbn/discover-utils';
|
||||
import { getUnifiedDocViewerServices } from '../../plugin';
|
||||
|
||||
export function LogsOverviewAIAssistant({ doc }: { doc: DataTableRecord }) {
|
||||
const { discoverShared } = getUnifiedDocViewerServices();
|
||||
|
||||
const logsAIAssistantFeature = discoverShared.features.registry.getById(
|
||||
'observability-logs-ai-assistant'
|
||||
);
|
||||
|
||||
if (!logsAIAssistantFeature) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return logsAIAssistantFeature.render({ doc });
|
||||
}
|
|
@ -17,6 +17,7 @@ import { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
|||
import { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
||||
import { CoreStart } from '@kbn/core/public';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { DiscoverSharedPublicStart } from '@kbn/discover-shared-plugin/public';
|
||||
import type { UnifiedDocViewerServices } from './types';
|
||||
|
||||
export const [getUnifiedDocViewerServices, setUnifiedDocViewerServices] =
|
||||
|
@ -47,6 +48,7 @@ export interface UnifiedDocViewerStart {
|
|||
|
||||
export interface UnifiedDocViewerStartDeps {
|
||||
data: DataPublicPluginStart;
|
||||
discoverShared: DiscoverSharedPublicStart;
|
||||
fieldFormats: FieldFormatsStart;
|
||||
}
|
||||
|
||||
|
@ -116,12 +118,20 @@ export class UnifiedDocViewerPublicPlugin
|
|||
|
||||
public start(core: CoreStart, deps: UnifiedDocViewerStartDeps) {
|
||||
const { analytics, uiSettings } = core;
|
||||
const { data, fieldFormats } = deps;
|
||||
const { data, discoverShared, fieldFormats } = deps;
|
||||
const storage = new Storage(localStorage);
|
||||
const unifiedDocViewer = {
|
||||
registry: this.docViewsRegistry,
|
||||
};
|
||||
const services = { analytics, data, fieldFormats, storage, uiSettings, unifiedDocViewer };
|
||||
const services = {
|
||||
analytics,
|
||||
data,
|
||||
discoverShared,
|
||||
fieldFormats,
|
||||
storage,
|
||||
uiSettings,
|
||||
unifiedDocViewer,
|
||||
};
|
||||
setUnifiedDocViewerServices(services);
|
||||
return unifiedDocViewer;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ export type { UnifiedDocViewerSetup, UnifiedDocViewerStart } from './plugin';
|
|||
|
||||
import type { AnalyticsServiceStart } from '@kbn/core-analytics-browser';
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import type { DiscoverSharedPublicStart } from '@kbn/discover-shared-plugin/public';
|
||||
import type { FieldFormatsStart } from '@kbn/field-formats-plugin/public';
|
||||
import type { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import type { IUiSettingsClient } from '@kbn/core-ui-settings-browser';
|
||||
|
@ -20,6 +21,7 @@ import type { UnifiedDocViewerStart } from './plugin';
|
|||
export interface UnifiedDocViewerServices {
|
||||
analytics: AnalyticsServiceStart;
|
||||
data: DataPublicPluginStart;
|
||||
discoverShared: DiscoverSharedPublicStart;
|
||||
fieldFormats: FieldFormatsStart;
|
||||
storage: Storage;
|
||||
uiSettings: IUiSettingsClient;
|
||||
|
|
|
@ -28,7 +28,8 @@
|
|||
"@kbn/code-editor-mock",
|
||||
"@kbn/custom-icons",
|
||||
"@kbn/react-field",
|
||||
"@kbn/ui-theme"
|
||||
"@kbn/ui-theme",
|
||||
"@kbn/discover-shared-plugin"
|
||||
],
|
||||
"exclude": [
|
||||
"target/**/*",
|
||||
|
|
|
@ -734,6 +734,8 @@
|
|||
"@kbn/discover-enhanced-plugin/*": ["x-pack/plugins/discover_enhanced/*"],
|
||||
"@kbn/discover-plugin": ["src/plugins/discover"],
|
||||
"@kbn/discover-plugin/*": ["src/plugins/discover/*"],
|
||||
"@kbn/discover-shared-plugin": ["src/plugins/discover_shared"],
|
||||
"@kbn/discover-shared-plugin/*": ["src/plugins/discover_shared/*"],
|
||||
"@kbn/discover-utils": ["packages/kbn-discover-utils"],
|
||||
"@kbn/discover-utils/*": ["packages/kbn-discover-utils/*"],
|
||||
"@kbn/doc-links": ["packages/kbn-doc-links"],
|
||||
|
|
|
@ -10,10 +10,6 @@ import { i18n } from '@kbn/i18n';
|
|||
import { EuiCode } from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
|
||||
export const flyoutContentLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.message', {
|
||||
defaultMessage: 'Content breakdown',
|
||||
});
|
||||
|
||||
export const contentLabel = i18n.translate('xpack.logsExplorer.dataTable.header.popover.content', {
|
||||
defaultMessage: 'Content',
|
||||
});
|
||||
|
@ -36,113 +32,6 @@ export const actionsLabelLowerCase = i18n.translate(
|
|||
}
|
||||
);
|
||||
|
||||
export const flyoutServiceLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.service', {
|
||||
defaultMessage: 'Service',
|
||||
});
|
||||
|
||||
export const flyoutTraceLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.trace', {
|
||||
defaultMessage: 'Trace',
|
||||
});
|
||||
|
||||
export const flyoutHostNameLabel = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.label.hostName',
|
||||
{
|
||||
defaultMessage: 'Host name',
|
||||
}
|
||||
);
|
||||
|
||||
export const serviceInfraAccordionTitle = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.accordion.title.serviceInfra',
|
||||
{
|
||||
defaultMessage: 'Service & Infrastructure',
|
||||
}
|
||||
);
|
||||
|
||||
export const cloudAccordionTitle = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.accordion.title.cloud',
|
||||
{
|
||||
defaultMessage: 'Cloud',
|
||||
}
|
||||
);
|
||||
|
||||
export const otherAccordionTitle = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.accordion.title.other',
|
||||
{
|
||||
defaultMessage: 'Other',
|
||||
}
|
||||
);
|
||||
|
||||
export const flyoutOrchestratorClusterNameLabel = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.label.orchestratorClusterName',
|
||||
{
|
||||
defaultMessage: 'Orchestrator cluster Name',
|
||||
}
|
||||
);
|
||||
|
||||
export const flyoutOrchestratorResourceIdLabel = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.label.orchestratorResourceId',
|
||||
{
|
||||
defaultMessage: 'Orchestrator resource ID',
|
||||
}
|
||||
);
|
||||
|
||||
export const flyoutCloudProviderLabel = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.label.cloudProvider',
|
||||
{
|
||||
defaultMessage: 'Cloud provider',
|
||||
}
|
||||
);
|
||||
|
||||
export const flyoutCloudRegionLabel = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.label.cloudRegion',
|
||||
{
|
||||
defaultMessage: 'Cloud region',
|
||||
}
|
||||
);
|
||||
|
||||
export const flyoutCloudAvailabilityZoneLabel = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.label.cloudAvailabilityZone',
|
||||
{
|
||||
defaultMessage: 'Cloud availability zone',
|
||||
}
|
||||
);
|
||||
|
||||
export const flyoutCloudProjectIdLabel = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.label.cloudProjectId',
|
||||
{
|
||||
defaultMessage: 'Cloud project ID',
|
||||
}
|
||||
);
|
||||
|
||||
export const flyoutCloudInstanceIdLabel = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.label.cloudInstanceId',
|
||||
{
|
||||
defaultMessage: 'Cloud instance ID',
|
||||
}
|
||||
);
|
||||
|
||||
export const flyoutLogPathFileLabel = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.label.logPathFile',
|
||||
{
|
||||
defaultMessage: 'Log path file',
|
||||
}
|
||||
);
|
||||
|
||||
export const flyoutNamespaceLabel = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.label.namespace',
|
||||
{
|
||||
defaultMessage: 'Namespace',
|
||||
}
|
||||
);
|
||||
|
||||
export const flyoutDatasetLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.dataset', {
|
||||
defaultMessage: 'Dataset',
|
||||
});
|
||||
|
||||
export const flyoutShipperLabel = i18n.translate('xpack.logsExplorer.flyoutDetail.label.shipper', {
|
||||
defaultMessage: 'Shipper',
|
||||
});
|
||||
|
||||
export const actionFilterForText = (text: string) =>
|
||||
i18n.translate('xpack.logsExplorer.flyoutDetail.value.hover.filterFor', {
|
||||
defaultMessage: 'Filter for this {value}',
|
||||
|
@ -167,27 +56,6 @@ export const filterForText = i18n.translate('xpack.logsExplorer.popoverAction.fi
|
|||
defaultMessage: 'Filter for',
|
||||
});
|
||||
|
||||
export const flyoutHoverActionFilterForFieldPresentText = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.value.hover.filterForFieldPresent',
|
||||
{
|
||||
defaultMessage: 'Filter for field present',
|
||||
}
|
||||
);
|
||||
|
||||
export const flyoutHoverActionToggleColumnText = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.value.hover.toggleColumn',
|
||||
{
|
||||
defaultMessage: 'Toggle column in table',
|
||||
}
|
||||
);
|
||||
|
||||
export const flyoutHoverActionCopyToClipboardText = i18n.translate(
|
||||
'xpack.logsExplorer.flyoutDetail.value.hover.copyToClipboard',
|
||||
{
|
||||
defaultMessage: 'Copy to clipboard',
|
||||
}
|
||||
);
|
||||
|
||||
export const copyValueText = i18n.translate('xpack.logsExplorer.popoverAction.copyValue', {
|
||||
defaultMessage: 'Copy value',
|
||||
});
|
||||
|
@ -200,14 +68,6 @@ export const copyValueAriaText = (fieldName: string) =>
|
|||
},
|
||||
});
|
||||
|
||||
export const flyoutAccordionShowMoreText = (count: number) =>
|
||||
i18n.translate('xpack.logsExplorer.flyoutDetail.section.showMore', {
|
||||
defaultMessage: '+ {hiddenCount} more',
|
||||
values: {
|
||||
hiddenCount: count,
|
||||
},
|
||||
});
|
||||
|
||||
export const openCellActionPopoverAriaText = i18n.translate(
|
||||
'xpack.logsExplorer.popoverAction.openPopover',
|
||||
{
|
||||
|
@ -227,7 +87,9 @@ export const contentHeaderTooltipParagraph1 = (
|
|||
id="xpack.logsExplorer.dataTable.header.content.tooltip.paragraph1"
|
||||
defaultMessage="Displays the document's {logLevel} and {message} fields."
|
||||
values={{
|
||||
// eslint-disable-next-line @kbn/i18n/strings_should_be_translated_with_i18n
|
||||
logLevel: <strong>log.level</strong>,
|
||||
// eslint-disable-next-line @kbn/i18n/strings_should_be_translated_with_i18n
|
||||
message: <strong>message</strong>,
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import React, { useMemo } from 'react';
|
||||
import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui';
|
||||
import { DocViewRenderProps } from '@kbn/unified-doc-viewer/types';
|
||||
import { LogsExplorerFlyoutContentProps } from './types';
|
||||
import { useLogsExplorerControllerContext } from '../controller';
|
||||
import { LogDocument } from '../../common/document';
|
||||
|
||||
const CustomFlyoutContent = ({
|
||||
filter,
|
||||
onAddColumn,
|
||||
onRemoveColumn,
|
||||
dataView,
|
||||
hit,
|
||||
}: DocViewRenderProps) => {
|
||||
const {
|
||||
customizations: { flyout },
|
||||
} = useLogsExplorerControllerContext();
|
||||
|
||||
const flyoutContentProps: LogsExplorerFlyoutContentProps = useMemo(
|
||||
() => ({
|
||||
actions: {
|
||||
addFilter: filter,
|
||||
addColumn: onAddColumn,
|
||||
removeColumn: onRemoveColumn,
|
||||
},
|
||||
dataView,
|
||||
doc: hit as LogDocument,
|
||||
}),
|
||||
[filter, onAddColumn, onRemoveColumn, dataView, hit]
|
||||
);
|
||||
|
||||
const renderCustomizedContent = useMemo(
|
||||
() => flyout?.renderContent?.(renderContent) ?? renderContent,
|
||||
[flyout]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<EuiSpacer size="m" />
|
||||
<EuiFlexGroup direction="column">
|
||||
{/* Apply custom Logs Explorer detail */}
|
||||
{renderCustomizedContent(flyoutContentProps)}
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const renderContent = ({ actions, dataView, doc }: LogsExplorerFlyoutContentProps) => (
|
||||
<EuiFlexItem>
|
||||
{/* TOREMOVE */}
|
||||
{/* <FlyoutDetail actions={actions} dataView={dataView} doc={doc} /> */}
|
||||
</EuiFlexItem>
|
||||
);
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default CustomFlyoutContent;
|
|
@ -5,27 +5,8 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { DocViewRenderProps } from '@kbn/unified-doc-viewer/src/services/types';
|
||||
import { LogDocument } from '../../common/document';
|
||||
import { LogsExplorerControllerContext } from '../state_machines/logs_explorer_controller';
|
||||
|
||||
export type RenderPreviousContent<Props> = (props: Props) => React.ReactNode;
|
||||
|
||||
export type RenderContentCustomization<Props> = (
|
||||
renderPreviousContent: RenderPreviousContent<Props>
|
||||
) => (props: Props) => React.ReactNode;
|
||||
|
||||
export interface LogsExplorerFlyoutContentProps {
|
||||
actions: {
|
||||
addFilter: DocViewRenderProps['filter'];
|
||||
addColumn: DocViewRenderProps['onAddColumn'];
|
||||
removeColumn: DocViewRenderProps['onRemoveColumn'];
|
||||
};
|
||||
dataView: DocViewRenderProps['dataView'];
|
||||
doc: LogDocument;
|
||||
}
|
||||
|
||||
export type OnUknownDataViewSelectionHandler = (context: LogsExplorerControllerContext) => void;
|
||||
|
||||
export interface LogsExplorerCustomizationEvents {
|
||||
|
@ -33,8 +14,5 @@ export interface LogsExplorerCustomizationEvents {
|
|||
}
|
||||
|
||||
export interface LogsExplorerCustomizations {
|
||||
flyout?: {
|
||||
renderContent?: RenderContentCustomization<LogsExplorerFlyoutContentProps>;
|
||||
};
|
||||
events?: LogsExplorerCustomizationEvents;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ export type {
|
|||
export type {
|
||||
LogsExplorerCustomizations,
|
||||
LogsExplorerCustomizationEvents,
|
||||
LogsExplorerFlyoutContentProps,
|
||||
} from './customizations/types';
|
||||
export type { LogsExplorerControllerContext } from './state_machines/logs_explorer_controller';
|
||||
export type { LogsExplorerPluginSetup, LogsExplorerPluginStart } from './types';
|
||||
|
|
|
@ -42,7 +42,6 @@
|
|||
"@kbn/shared-ux-utility",
|
||||
"@kbn/ui-theme",
|
||||
"@kbn/unified-data-table",
|
||||
"@kbn/unified-doc-viewer",
|
||||
"@kbn/unified-field-list",
|
||||
"@kbn/unified-search-plugin",
|
||||
"@kbn/xstate-utils",
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"requiredPlugins": [
|
||||
"data",
|
||||
"dataViews",
|
||||
"discoverShared",
|
||||
"usageCollection",
|
||||
"observabilityShared",
|
||||
"share"
|
||||
|
|
|
@ -4,9 +4,10 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import React, { useMemo } from 'react';
|
||||
import { dynamic } from '@kbn/shared-ux-utility';
|
||||
import { LogAIAssistantProps } from './log_ai_assistant';
|
||||
import { ObservabilityLogsAIAssistantFeatureRenderDeps } from '@kbn/discover-shared-plugin/public';
|
||||
import { LogAIAssistantDocument, LogAIAssistantProps } from './log_ai_assistant';
|
||||
|
||||
export const LogAIAssistant = dynamic(() => import('./log_ai_assistant'));
|
||||
|
||||
|
@ -17,3 +18,19 @@ export function createLogAIAssistant({
|
|||
<LogAIAssistant observabilityAIAssistant={observabilityAIAssistant} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
export const createLogsAIAssistantRenderer =
|
||||
(LogAIAssistantRender: ReturnType<typeof createLogAIAssistant>) =>
|
||||
({ doc }: ObservabilityLogsAIAssistantFeatureRenderDeps) => {
|
||||
const mappedDoc = useMemo(
|
||||
() => ({
|
||||
fields: Object.entries(doc.flattened).map(([field, value]) => ({
|
||||
field,
|
||||
value,
|
||||
})) as LogAIAssistantDocument['fields'],
|
||||
}),
|
||||
[doc]
|
||||
);
|
||||
|
||||
return <LogAIAssistantRender key={doc.id} doc={mappedDoc} />;
|
||||
};
|
||||
|
|
|
@ -11,7 +11,7 @@ import {
|
|||
NodeLogsLocatorDefinition,
|
||||
TraceLogsLocatorDefinition,
|
||||
} from '../common/locators';
|
||||
import { createLogAIAssistant } from './components/log_ai_assistant';
|
||||
import { createLogAIAssistant, createLogsAIAssistantRenderer } from './components/log_ai_assistant';
|
||||
import { LogViewsService } from './services/log_views';
|
||||
import {
|
||||
LogsSharedClientCoreSetup,
|
||||
|
@ -52,7 +52,7 @@ export class LogsSharedPlugin implements LogsSharedClientPluginClass {
|
|||
|
||||
public start(core: CoreStart, plugins: LogsSharedClientStartDeps) {
|
||||
const { http } = core;
|
||||
const { data, dataViews, observabilityAIAssistant } = plugins;
|
||||
const { data, dataViews, discoverShared, observabilityAIAssistant } = plugins;
|
||||
|
||||
const logViews = this.logViews.start({
|
||||
http,
|
||||
|
@ -68,6 +68,11 @@ export class LogsSharedPlugin implements LogsSharedClientPluginClass {
|
|||
|
||||
const LogAIAssistant = createLogAIAssistant({ observabilityAIAssistant });
|
||||
|
||||
discoverShared.features.registry.register({
|
||||
id: 'observability-logs-ai-assistant',
|
||||
render: createLogsAIAssistantRenderer(LogAIAssistant),
|
||||
});
|
||||
|
||||
return {
|
||||
logViews,
|
||||
LogAIAssistant,
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import type { CoreSetup, CoreStart, Plugin as PluginClass } from '@kbn/core/public';
|
||||
import type { DataPublicPluginStart } from '@kbn/data-plugin/public';
|
||||
import type { DataViewsPublicPluginStart } from '@kbn/data-views-plugin/public';
|
||||
import type { DiscoverSharedPublicStart } from '@kbn/discover-shared-plugin/public';
|
||||
import type { ObservabilityAIAssistantPublicStart } from '@kbn/observability-ai-assistant-plugin/public';
|
||||
import { SharePluginSetup } from '@kbn/share-plugin/public';
|
||||
import { UiActionsStart } from '@kbn/ui-actions-plugin/public';
|
||||
|
@ -35,6 +36,7 @@ export interface LogsSharedClientSetupDeps {
|
|||
export interface LogsSharedClientStartDeps {
|
||||
data: DataPublicPluginStart;
|
||||
dataViews: DataViewsPublicPluginStart;
|
||||
discoverShared: DiscoverSharedPublicStart;
|
||||
observabilityAIAssistant?: ObservabilityAIAssistantPublicStart;
|
||||
uiActions: UiActionsStart;
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
"@kbn/observability-ai-assistant-plugin",
|
||||
"@kbn/deeplinks-observability",
|
||||
"@kbn/share-plugin",
|
||||
"@kbn/shared-ux-utility"
|
||||
"@kbn/shared-ux-utility",
|
||||
"@kbn/discover-shared-plugin"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
import { EuiFlexItem } from '@elastic/eui';
|
||||
import {
|
||||
LogsExplorerCustomizations,
|
||||
LogsExplorerFlyoutContentProps,
|
||||
} from '@kbn/logs-explorer-plugin/public';
|
||||
import type {
|
||||
LogAIAssistantDocument,
|
||||
LogsSharedClientStartExports,
|
||||
} from '@kbn/logs-shared-plugin/public';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useKibanaContextForPlugin } from '../utils/use_kibana';
|
||||
|
||||
type RenderFlyoutContentCustomization =
|
||||
Required<LogsExplorerCustomizations>['flyout']['renderContent'];
|
||||
|
||||
const ObservabilityLogAIAssistant = ({
|
||||
doc,
|
||||
LogAIAssistant,
|
||||
}: LogsExplorerFlyoutContentProps & {
|
||||
LogAIAssistant: Required<LogsSharedClientStartExports>['LogAIAssistant'];
|
||||
}) => {
|
||||
const mappedDoc = useMemo(() => mapDocToAIAssistantFormat(doc), [doc]);
|
||||
|
||||
return <LogAIAssistant key={doc.id} doc={mappedDoc} />;
|
||||
};
|
||||
|
||||
export const renderFlyoutContent: RenderFlyoutContentCustomization =
|
||||
(renderPreviousContent) => (props) => {
|
||||
const { services } = useKibanaContextForPlugin();
|
||||
const { LogAIAssistant } = services.logsShared;
|
||||
|
||||
return (
|
||||
<>
|
||||
{renderPreviousContent(props)}
|
||||
{LogAIAssistant ? (
|
||||
<EuiFlexItem>
|
||||
<ObservabilityLogAIAssistant {...props} LogAIAssistant={LogAIAssistant} />
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Utils
|
||||
*/
|
||||
const mapDocToAIAssistantFormat = (doc: LogsExplorerFlyoutContentProps['doc']) => {
|
||||
if (!doc) return;
|
||||
|
||||
return {
|
||||
fields: Object.entries(doc.flattened).map(([field, value]) => ({
|
||||
field,
|
||||
value,
|
||||
})) as LogAIAssistantDocument['fields'],
|
||||
};
|
||||
};
|
|
@ -8,7 +8,6 @@
|
|||
import { CreateLogsExplorerController } from '@kbn/logs-explorer-plugin/public';
|
||||
import { PluginKibanaContextValue } from '../utils/use_kibana';
|
||||
import { createOnUknownDataViewSelectionHandler } from './discover_navigation_handler';
|
||||
import { renderFlyoutContent } from './flyout_content';
|
||||
|
||||
export const createLogsExplorerControllerWithCustomizations =
|
||||
(
|
||||
|
@ -23,8 +22,5 @@ export const createLogsExplorerControllerWithCustomizations =
|
|||
events: {
|
||||
onUknownDataViewSelection: createOnUknownDataViewSelectionHandler(services.discover),
|
||||
},
|
||||
flyout: {
|
||||
renderContent: renderFlyoutContent,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -24116,7 +24116,6 @@
|
|||
"xpack.lists.exceptions.field.mappingConflict.description": "Ce champ est défini avec différents types dans les index suivants ou il n'est pas mappé, ce qui peut entraîner des résultats inattendus lors des requêtes.",
|
||||
"xpack.lists.exceptions.orDescription": "OR",
|
||||
"xpack.logsExplorer.dataTable.header.content.tooltip.paragraph1": "Affiche le {logLevel} du document et les champs {message}.",
|
||||
"xpack.logsExplorer.flyoutDetail.section.showMore": "+ {hiddenCount} autres",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.filterFor": "Filtrer sur cette {value}",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.filterOut": "Exclure cette {value}",
|
||||
"xpack.logsExplorer.popoverAction.copyValueAriaText": "Copier la valeur de {fieldName}",
|
||||
|
@ -24131,28 +24130,7 @@
|
|||
"xpack.logsExplorer.dataTable.header.popover.content": "Contenu",
|
||||
"xpack.logsExplorer.dataTable.header.popover.resource": "Ressource",
|
||||
"xpack.logsExplorer.dataTable.header.resource.tooltip.paragraph": "Les champs fournissant des informations sur la source du document, comme :",
|
||||
"xpack.logsExplorer.flyoutDetail.accordion.title.cloud": "Cloud",
|
||||
"xpack.logsExplorer.flyoutDetail.accordion.title.other": "Autre",
|
||||
"xpack.logsExplorer.flyoutDetail.accordion.title.serviceInfra": "Service et Infrastructure",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudAvailabilityZone": "Zone de disponibilité du cloud",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudInstanceId": "ID d'instance du cloud",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudProjectId": "ID de projet du cloud",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudProvider": "Fournisseur cloud",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudRegion": "Région du cloud",
|
||||
"xpack.logsExplorer.flyoutDetail.label.dataset": "Ensemble de données",
|
||||
"xpack.logsExplorer.flyoutDetail.label.hostName": "Nom d'hôte",
|
||||
"xpack.logsExplorer.flyoutDetail.label.logPathFile": "Fichier de chemin d'accès au log",
|
||||
"xpack.logsExplorer.flyoutDetail.label.message": "Répartition du contenu",
|
||||
"xpack.logsExplorer.flyoutDetail.label.namespace": "Espace de nom",
|
||||
"xpack.logsExplorer.flyoutDetail.label.orchestratorClusterName": "Nom de cluster de l'orchestrateur",
|
||||
"xpack.logsExplorer.flyoutDetail.label.orchestratorResourceId": "ID de ressource de l'orchestrateur",
|
||||
"xpack.logsExplorer.flyoutDetail.label.service": "Service",
|
||||
"xpack.logsExplorer.flyoutDetail.label.shipper": "Agent de transfert",
|
||||
"xpack.logsExplorer.flyoutDetail.label.trace": "Trace",
|
||||
"xpack.logsExplorer.flyoutDetail.title": "Détails du log",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.copyToClipboard": "Copier dans le presse-papiers",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.filterForFieldPresent": "Filtrer sur le champ",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.toggleColumn": "Afficher/Masquer la colonne dans le tableau",
|
||||
"xpack.logsExplorer.grid.closePopover": "Fermer la fenêtre contextuelle",
|
||||
"xpack.logsExplorer.popoverAction.closePopover": "Fermer la fenêtre contextuelle",
|
||||
"xpack.logsExplorer.popoverAction.copyValue": "Copier la valeur",
|
||||
|
|
|
@ -24091,7 +24091,6 @@
|
|||
"xpack.lists.exceptions.field.mappingConflict.description": "このフィールドは、次のインデックスで別の型として定義されているか、マッピングされていません。これにより、予期しないクエリ結果になる場合があります。",
|
||||
"xpack.lists.exceptions.orDescription": "OR",
|
||||
"xpack.logsExplorer.dataTable.header.content.tooltip.paragraph1": "ドキュメントの{logLevel}と{message}フィールドを表示します。",
|
||||
"xpack.logsExplorer.flyoutDetail.section.showMore": "+ その他{hiddenCount}件",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.filterFor": "この{value}でフィルターを適用",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.filterOut": "この{value}を除外",
|
||||
"xpack.logsExplorer.popoverAction.copyValueAriaText": "{fieldName}の値をコピー",
|
||||
|
@ -24106,28 +24105,7 @@
|
|||
"xpack.logsExplorer.dataTable.header.popover.content": "コンテンツ",
|
||||
"xpack.logsExplorer.dataTable.header.popover.resource": "リソース",
|
||||
"xpack.logsExplorer.dataTable.header.resource.tooltip.paragraph": "次のようなドキュメントのソースに関する情報を提供するフィールド:",
|
||||
"xpack.logsExplorer.flyoutDetail.accordion.title.cloud": "クラウド",
|
||||
"xpack.logsExplorer.flyoutDetail.accordion.title.other": "その他",
|
||||
"xpack.logsExplorer.flyoutDetail.accordion.title.serviceInfra": "サービスとインフラストラクチャー",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudAvailabilityZone": "クラウドアベイラビリティゾーン",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudInstanceId": "クラウドインスタンスID",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudProjectId": "クラウドプロジェクトID",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudProvider": "クラウドプロバイダー",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudRegion": "クラウドリージョン",
|
||||
"xpack.logsExplorer.flyoutDetail.label.dataset": "データセット",
|
||||
"xpack.logsExplorer.flyoutDetail.label.hostName": "ホスト名",
|
||||
"xpack.logsExplorer.flyoutDetail.label.logPathFile": "ログパスファイル",
|
||||
"xpack.logsExplorer.flyoutDetail.label.message": "コンテンツの内訳",
|
||||
"xpack.logsExplorer.flyoutDetail.label.namespace": "名前空間",
|
||||
"xpack.logsExplorer.flyoutDetail.label.orchestratorClusterName": "オーケストレータークラスター名",
|
||||
"xpack.logsExplorer.flyoutDetail.label.orchestratorResourceId": "オーケストレーターリソースID",
|
||||
"xpack.logsExplorer.flyoutDetail.label.service": "サービス",
|
||||
"xpack.logsExplorer.flyoutDetail.label.shipper": "シッパー",
|
||||
"xpack.logsExplorer.flyoutDetail.label.trace": "トレース",
|
||||
"xpack.logsExplorer.flyoutDetail.title": "ログの詳細",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.copyToClipboard": "クリップボードにコピー",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.filterForFieldPresent": "フィールド表示のフィルター",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.toggleColumn": "表の列を切り替える",
|
||||
"xpack.logsExplorer.grid.closePopover": "ポップオーバーを閉じる",
|
||||
"xpack.logsExplorer.popoverAction.closePopover": "ポップオーバーを閉じる",
|
||||
"xpack.logsExplorer.popoverAction.copyValue": "値をコピー",
|
||||
|
|
|
@ -24124,7 +24124,6 @@
|
|||
"xpack.lists.exceptions.field.mappingConflict.description": "此字段在以下索引中定义为不同类型或未映射。这可能导致意外的查询结果。",
|
||||
"xpack.lists.exceptions.orDescription": "OR",
|
||||
"xpack.logsExplorer.dataTable.header.content.tooltip.paragraph1": "显示该文档的 {logLevel} 和 {message} 字段。",
|
||||
"xpack.logsExplorer.flyoutDetail.section.showMore": "+ 另外 {hiddenCount} 个",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.filterFor": "筛留此 {value}",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.filterOut": "筛除此 {value}",
|
||||
"xpack.logsExplorer.popoverAction.copyValueAriaText": "复制 {fieldName} 的值",
|
||||
|
@ -24139,28 +24138,7 @@
|
|||
"xpack.logsExplorer.dataTable.header.popover.content": "内容",
|
||||
"xpack.logsExplorer.dataTable.header.popover.resource": "资源",
|
||||
"xpack.logsExplorer.dataTable.header.resource.tooltip.paragraph": "提供有关文档来源信息的字段,例如:",
|
||||
"xpack.logsExplorer.flyoutDetail.accordion.title.cloud": "云",
|
||||
"xpack.logsExplorer.flyoutDetail.accordion.title.other": "其他",
|
||||
"xpack.logsExplorer.flyoutDetail.accordion.title.serviceInfra": "服务和基础设施",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudAvailabilityZone": "云可用区",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudInstanceId": "云实例 ID",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudProjectId": "云项目 ID",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudProvider": "云服务提供商",
|
||||
"xpack.logsExplorer.flyoutDetail.label.cloudRegion": "云区域",
|
||||
"xpack.logsExplorer.flyoutDetail.label.dataset": "数据集",
|
||||
"xpack.logsExplorer.flyoutDetail.label.hostName": "主机名",
|
||||
"xpack.logsExplorer.flyoutDetail.label.logPathFile": "日志路径文件",
|
||||
"xpack.logsExplorer.flyoutDetail.label.message": "内容细目",
|
||||
"xpack.logsExplorer.flyoutDetail.label.namespace": "命名空间",
|
||||
"xpack.logsExplorer.flyoutDetail.label.orchestratorClusterName": "Orchestrator 集群名称",
|
||||
"xpack.logsExplorer.flyoutDetail.label.orchestratorResourceId": "Orchestrator 资源 ID",
|
||||
"xpack.logsExplorer.flyoutDetail.label.service": "服务",
|
||||
"xpack.logsExplorer.flyoutDetail.label.shipper": "采集器",
|
||||
"xpack.logsExplorer.flyoutDetail.label.trace": "跟踪",
|
||||
"xpack.logsExplorer.flyoutDetail.title": "日志详情",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.copyToClipboard": "复制到剪贴板",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.filterForFieldPresent": "筛留存在的字段",
|
||||
"xpack.logsExplorer.flyoutDetail.value.hover.toggleColumn": "在表中切换列",
|
||||
"xpack.logsExplorer.grid.closePopover": "关闭弹出框",
|
||||
"xpack.logsExplorer.popoverAction.closePopover": "关闭弹出框",
|
||||
"xpack.logsExplorer.popoverAction.copyValue": "复制值",
|
||||
|
|
|
@ -4518,6 +4518,10 @@
|
|||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/discover-shared-plugin@link:src/plugins/discover_shared":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
"@kbn/discover-utils@link:packages/kbn-discover-utils":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue