Skip to content

Commit

Permalink
[Discover] Extend EBT context with a list of activated context-aware …
Browse files Browse the repository at this point in the history
…profiles (elastic#192908)

- Closes elastic#186109

## Summary

This PR extends EBT context with `dscProfiles` - a list of active
context-aware profiles.

<img width="981" alt="Screenshot 2024-09-16 at 17 30 47"
src="https://github.com/user-attachments/assets/64d49abc-3ee0-4d5a-8283-cdca5d78f963">

## Testing

Enable "Usage collection" global setting.

Navigate to Discover and observe `kibana-browser` requests in Network
tab.

### Checklist

- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios

---------

Co-authored-by: kibanamachine <[email protected]>
(cherry picked from commit c28af87)
  • Loading branch information
jughosta committed Sep 24, 2024
1 parent bdfccd2 commit 53f3e28
Show file tree
Hide file tree
Showing 11 changed files with 361 additions and 12 deletions.
5 changes: 5 additions & 0 deletions src/plugins/discover/public/build_services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import type { DiscoverContextAppLocator } from './application/context/services/l
import type { DiscoverSingleDocLocator } from './application/doc/locator';
import type { DiscoverAppLocator } from '../common';
import type { ProfilesManager } from './context_awareness';
import type { DiscoverEBTContextManager } from './services/discover_ebt_context_manager';

/**
* Location state of internal Discover history instance
Expand Down Expand Up @@ -130,6 +131,7 @@ export interface DiscoverServices {
noDataPage?: NoDataPagePluginStart;
observabilityAIAssistant?: ObservabilityAIAssistantPublicStart;
profilesManager: ProfilesManager;
ebtContextManager: DiscoverEBTContextManager;
fieldsMetadata?: FieldsMetadataPublicStart;
}

Expand All @@ -145,6 +147,7 @@ export const buildServices = memoize(
scopedHistory,
urlTracker,
profilesManager,
ebtContextManager,
setHeaderActionMenu = noop,
}: {
core: CoreStart;
Expand All @@ -157,6 +160,7 @@ export const buildServices = memoize(
scopedHistory?: ScopedHistory;
urlTracker: UrlTracker;
profilesManager: ProfilesManager;
ebtContextManager: DiscoverEBTContextManager;
setHeaderActionMenu?: AppMountParameters['setHeaderActionMenu'];
}): DiscoverServices => {
const { usageCollection } = plugins;
Expand Down Expand Up @@ -217,6 +221,7 @@ export const buildServices = memoize(
noDataPage: plugins.noDataPage,
observabilityAIAssistant: plugins.observabilityAIAssistant,
profilesManager,
ebtContextManager,
fieldsMetadata: plugins.fieldsMetadata,
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
SolutionType,
} from '../profiles';
import { ProfilesManager } from '../profiles_manager';
import { DiscoverEBTContextManager } from '../../services/discover_ebt_context_manager';
import { createLogsContextServiceMock } from '@kbn/discover-utils/src/__mocks__';

export const createContextAwarenessMocks = ({
Expand Down Expand Up @@ -150,10 +151,12 @@ export const createContextAwarenessMocks = ({
documentProfileServiceMock.registerProvider(documentProfileProviderMock);
}

const ebtContextManagerMock = new DiscoverEBTContextManager();
const profilesManagerMock = new ProfilesManager(
rootProfileServiceMock,
dataSourceProfileServiceMock,
documentProfileServiceMock
documentProfileServiceMock,
ebtContextManagerMock
);

const profileProviderServices = createProfileProviderServicesMock();
Expand All @@ -169,6 +172,7 @@ export const createContextAwarenessMocks = ({
contextRecordMock2,
profilesManagerMock,
profileProviderServices,
ebtContextManagerMock,
};
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ describe('ProfilesManager', () => {
beforeEach(() => {
jest.clearAllMocks();
mocks = createContextAwarenessMocks();
jest.spyOn(mocks.ebtContextManagerMock, 'updateProfilesContextWith');
});

it('should return default profiles', () => {
Expand Down Expand Up @@ -60,6 +61,11 @@ describe('ProfilesManager', () => {
mocks.dataSourceProfileProviderMock.profile,
mocks.documentProfileProviderMock.profile,
]);

expect(mocks.ebtContextManagerMock.updateProfilesContextWith).toHaveBeenCalledWith([
'root-profile',
'data-source-profile',
]);
});

it('should expose profiles as an observable', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type {
DocumentContext,
} from './profiles';
import type { ContextWithProfileId } from './profile_service';
import { DiscoverEBTContextManager } from '../services/discover_ebt_context_manager';

interface SerializedRootProfileParams {
solutionNavId: RootProfileProviderParams['solutionNavId'];
Expand Down Expand Up @@ -52,6 +53,7 @@ export interface GetProfilesOptions {
export class ProfilesManager {
private readonly rootContext$: BehaviorSubject<ContextWithProfileId<RootContext>>;
private readonly dataSourceContext$: BehaviorSubject<ContextWithProfileId<DataSourceContext>>;
private readonly ebtContextManager: DiscoverEBTContextManager;

private prevRootProfileParams?: SerializedRootProfileParams;
private prevDataSourceProfileParams?: SerializedDataSourceProfileParams;
Expand All @@ -61,10 +63,12 @@ export class ProfilesManager {
constructor(
private readonly rootProfileService: RootProfileService,
private readonly dataSourceProfileService: DataSourceProfileService,
private readonly documentProfileService: DocumentProfileService
private readonly documentProfileService: DocumentProfileService,
ebtContextManager: DiscoverEBTContextManager
) {
this.rootContext$ = new BehaviorSubject(rootProfileService.defaultContext);
this.dataSourceContext$ = new BehaviorSubject(dataSourceProfileService.defaultContext);
this.ebtContextManager = ebtContextManager;
}

/**
Expand Down Expand Up @@ -130,6 +134,7 @@ export class ProfilesManager {
return;
}

this.trackActiveProfiles(this.rootContext$.getValue().profileId, context.profileId);
this.dataSourceContext$.next(context);
this.prevDataSourceProfileParams = serializedParams;
}
Expand Down Expand Up @@ -194,6 +199,15 @@ export class ProfilesManager {
map(() => this.getProfiles(options))
);
}

/**
* Tracks the active profiles in the EBT context
*/
private trackActiveProfiles(rootContextProfileId: string, dataSourceContextProfileId: string) {
const dscProfiles = [rootContextProfileId, dataSourceContextProfileId];

this.ebtContextManager.updateProfilesContextWith(dscProfiles);
}
}

const serializeRootProfileParams = (
Expand Down
46 changes: 38 additions & 8 deletions src/plugins/discover/public/plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ import { RootProfileService } from './context_awareness/profiles/root_profile';
import { DataSourceProfileService } from './context_awareness/profiles/data_source_profile';
import { DocumentProfileService } from './context_awareness/profiles/document_profile';
import { ProfilesManager } from './context_awareness/profiles_manager';
import { DiscoverEBTContextManager } from './services/discover_ebt_context_manager';

/**
* Contains Discover, one of the oldest parts of Kibana
Expand Down Expand Up @@ -149,6 +150,9 @@ export class DiscoverPlugin
this.urlTracker = { setTrackedUrl, restorePreviousUrl, setTrackingEnabled };
this.stopUrlTracking = stopUrlTracker;

const ebtContextManager = new DiscoverEBTContextManager();
ebtContextManager.initialize({ core });

core.application.register({
id: PLUGIN_ID,
title: 'Discover',
Expand All @@ -173,6 +177,8 @@ export class DiscoverPlugin
window.dispatchEvent(new HashChangeEvent('hashchange'));
});

ebtContextManager.enable();

const services = buildServices({
core: coreStart,
plugins: discoverStartPlugins,
Expand All @@ -183,7 +189,11 @@ export class DiscoverPlugin
history: this.historyService.getHistory(),
scopedHistory: this.scopedHistory,
urlTracker: this.urlTracker!,
profilesManager: await this.createProfilesManager({ plugins: discoverStartPlugins }),
profilesManager: await this.createProfilesManager({
plugins: discoverStartPlugins,
ebtContextManager,
}),
ebtContextManager,
setHeaderActionMenu: params.setHeaderActionMenu,
});

Expand Down Expand Up @@ -216,6 +226,7 @@ export class DiscoverPlugin
});

return () => {
ebtContextManager.disableAndReset();
unlistenParentHistory();
unmount();
appUnMounted();
Expand Down Expand Up @@ -285,7 +296,12 @@ export class DiscoverPlugin
}

const getDiscoverServicesInternal = () => {
return this.getDiscoverServices(core, plugins, this.createEmptyProfilesManager());
return this.getDiscoverServices(
core,
plugins,
this.createEmptyProfilesManager(),
new DiscoverEBTContextManager() // it's not enabled outside of Discover
);
};

return {
Expand Down Expand Up @@ -320,29 +336,38 @@ export class DiscoverPlugin
return { rootProfileService, dataSourceProfileService, documentProfileService };
});

private async createProfilesManager({ plugins }: { plugins: DiscoverStartPlugins }) {
private async createProfilesManager({
plugins,
ebtContextManager,
}: {
plugins: DiscoverStartPlugins;
ebtContextManager: DiscoverEBTContextManager;
}) {
const { rootProfileService, dataSourceProfileService, documentProfileService } =
await this.createProfileServices({ plugins });

return new ProfilesManager(
rootProfileService,
dataSourceProfileService,
documentProfileService
documentProfileService,
ebtContextManager
);
}

private createEmptyProfilesManager() {
return new ProfilesManager(
new RootProfileService(),
new DataSourceProfileService(),
new DocumentProfileService()
new DocumentProfileService(),
new DiscoverEBTContextManager() // it's not enabled outside of Discover
);
}

private getDiscoverServices = (
core: CoreStart,
plugins: DiscoverStartPlugins,
profilesManager: ProfilesManager
profilesManager: ProfilesManager,
ebtContextManager: DiscoverEBTContextManager
) => {
return buildServices({
core,
Expand All @@ -354,6 +379,7 @@ export class DiscoverPlugin
history: this.historyService.getHistory(),
urlTracker: this.urlTracker!,
profilesManager,
ebtContextManager,
});
};

Expand All @@ -368,8 +394,12 @@ export class DiscoverPlugin

const getDiscoverServicesInternal = async () => {
const [coreStart, deps] = await core.getStartServices();
const profilesManager = await this.createProfilesManager({ plugins: deps });
return this.getDiscoverServices(coreStart, deps, profilesManager);
const ebtContextManager = new DiscoverEBTContextManager(); // it's not enabled outside of Discover
const profilesManager = await this.createProfilesManager({
plugins: deps,
ebtContextManager,
});
return this.getDiscoverServices(coreStart, deps, profilesManager, ebtContextManager);
};

plugins.embeddable.registerReactEmbeddableSavedObject<SavedSearchAttributes>({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { BehaviorSubject } from 'rxjs';
import { coreMock } from '@kbn/core/public/mocks';
import { DiscoverEBTContextManager } from './discover_ebt_context_manager';

const coreSetupMock = coreMock.createSetup();

describe('DiscoverEBTContextManager', () => {
let discoverEBTContextManager: DiscoverEBTContextManager;

beforeEach(() => {
discoverEBTContextManager = new DiscoverEBTContextManager();
});

describe('register', () => {
it('should register the context provider', () => {
discoverEBTContextManager.initialize({ core: coreSetupMock });

expect(coreSetupMock.analytics.registerContextProvider).toHaveBeenCalledWith({
name: 'discover_context',
context$: expect.any(BehaviorSubject),
schema: {
discoverProfiles: {
type: 'array',
items: {
type: 'keyword',
_meta: {
description: 'List of active Discover context awareness profiles',
},
},
},
},
});
});
});

describe('updateProfilesWith', () => {
it('should update the profiles with the provided props', () => {
const dscProfiles = ['profile1', 'profile2'];
const dscProfiles2 = ['profile21', 'profile22'];
discoverEBTContextManager.initialize({ core: coreSetupMock });
discoverEBTContextManager.enable();

discoverEBTContextManager.updateProfilesContextWith(dscProfiles);
expect(discoverEBTContextManager.getProfilesContext()).toBe(dscProfiles);

discoverEBTContextManager.updateProfilesContextWith(dscProfiles2);
expect(discoverEBTContextManager.getProfilesContext()).toBe(dscProfiles2);
});

it('should not update the profiles if profile list did not change', () => {
const dscProfiles = ['profile1', 'profile2'];
const dscProfiles2 = ['profile1', 'profile2'];
discoverEBTContextManager.initialize({ core: coreSetupMock });
discoverEBTContextManager.enable();

discoverEBTContextManager.updateProfilesContextWith(dscProfiles);
expect(discoverEBTContextManager.getProfilesContext()).toBe(dscProfiles);

discoverEBTContextManager.updateProfilesContextWith(dscProfiles2);
expect(discoverEBTContextManager.getProfilesContext()).toBe(dscProfiles);
});

it('should not update the profiles if not enabled yet', () => {
const dscProfiles = ['profile1', 'profile2'];
discoverEBTContextManager.initialize({ core: coreSetupMock });

discoverEBTContextManager.updateProfilesContextWith(dscProfiles);
expect(discoverEBTContextManager.getProfilesContext()).toEqual([]);
});

it('should not update the profiles after resetting unless enabled again', () => {
const dscProfiles = ['profile1', 'profile2'];
discoverEBTContextManager.initialize({ core: coreSetupMock });
discoverEBTContextManager.enable();
discoverEBTContextManager.updateProfilesContextWith(dscProfiles);
expect(discoverEBTContextManager.getProfilesContext()).toBe(dscProfiles);
discoverEBTContextManager.disableAndReset();
expect(discoverEBTContextManager.getProfilesContext()).toEqual([]);
discoverEBTContextManager.updateProfilesContextWith(dscProfiles);
expect(discoverEBTContextManager.getProfilesContext()).toEqual([]);
discoverEBTContextManager.enable();
discoverEBTContextManager.updateProfilesContextWith(dscProfiles);
expect(discoverEBTContextManager.getProfilesContext()).toBe(dscProfiles);
});
});
});
Loading

0 comments on commit 53f3e28

Please sign in to comment.