Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

APM execution context - app, page, entitiy id #124996

Merged
merged 79 commits into from
Mar 3, 2022
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
7deba6b
Client side execution app level context propagation
Feb 8, 2022
c44c4f2
context$ + apm rum integration
Feb 8, 2022
6b18395
invert the context parent \ child relationship (cc @mikhail)
Feb 8, 2022
a0044cd
Pass down context to apm on server
Feb 8, 2022
1115e93
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 8, 2022
ac41e1f
types
Feb 8, 2022
c2ed495
eslint
Feb 8, 2022
600607c
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 8, 2022
85408b1
parent <> child
Feb 8, 2022
efb7952
docs + eslint + jest
Feb 9, 2022
5da627a
execution context mock
Feb 9, 2022
1c1fce1
eslint
Feb 9, 2022
ed018ba
git pushMerge branch 'main' of https://github.com/elastic/kibana into…
Feb 9, 2022
5200052
jest
Feb 9, 2022
4c151dd
jest
Feb 9, 2022
b64e5e2
server jest
Feb 9, 2022
f1fc110
check
Feb 9, 2022
f413c38
jest
Feb 9, 2022
97350de
storybook
Feb 9, 2022
cf33228
jest
Feb 9, 2022
666441c
report the current space
Feb 10, 2022
d8c69c4
fix server side context container
Feb 10, 2022
6168b00
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 10, 2022
abcccdc
Remove spaces for now
Feb 10, 2022
824cb2a
docssss
Feb 10, 2022
89d7064
jest
Feb 10, 2022
e32f869
lint
Feb 10, 2022
a0c344e
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 10, 2022
756e7f5
test
Feb 11, 2022
aa6b774
Merge branch 'main' into client-side-execution-context
Feb 14, 2022
7cce534
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 14, 2022
ccb285d
Merge branch 'client-side-execution-context' of github.com:lizozom/ki…
Feb 14, 2022
8efe9ad
docs
Feb 15, 2022
7402fba
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 15, 2022
21e4438
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 15, 2022
c9fa18d
revert file
Feb 15, 2022
4b29f04
doc
Feb 15, 2022
b600cc0
all context params are optional
Feb 15, 2022
7cfd118
clear on page change
Feb 15, 2022
400731a
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 15, 2022
2a1a373
lint
Feb 15, 2022
2b45d23
ts
Feb 15, 2022
1cedb99
skipped test again
Feb 16, 2022
bb58dd2
testing fixes
Feb 16, 2022
2f2be65
oops
Feb 16, 2022
349ef2c
code review #1
Feb 17, 2022
47447e1
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 17, 2022
46807d3
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 21, 2022
efd5ea4
code review #2
Feb 21, 2022
80b1b68
getAsLabels
Feb 21, 2022
3c8a87d
maps inherit dashboard context
Feb 21, 2022
86b8cba
docs
Feb 21, 2022
66d6d3c
ts
Feb 21, 2022
7c500c7
Give common context to all vis editors
Feb 21, 2022
358515d
fix test
Feb 21, 2022
cb939d1
ts \ es \ tests
Feb 22, 2022
1dce721
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 22, 2022
84129cb
labels
Feb 22, 2022
984abf3
missing types
Feb 22, 2022
77decb1
docsy docs
Feb 22, 2022
2c0efd2
cr #3
Feb 22, 2022
19140f7
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 23, 2022
8a420c5
improve jest
Feb 23, 2022
5dad2dc
Use editor name
Feb 23, 2022
4facdf1
Update src/plugins/visualizations/public/visualize_app/components/vis…
lizozom Feb 23, 2022
585d690
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 24, 2022
f094803
fix maps context
Feb 24, 2022
59960cf
Merge branch 'client-side-execution-context' of github.com:lizozom/ki…
Feb 24, 2022
2a15a64
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 24, 2022
b0e216d
jest tests for maps
Feb 24, 2022
c10afe8
cr
Feb 24, 2022
d7c01e4
Merge branch 'main' of https://github.com/elastic/kibana into client-…
Feb 24, 2022
4a96dac
docs
Feb 24, 2022
a78a4b8
Update execution_context.test.ts
lizozom Feb 24, 2022
4dbc83e
docs
Feb 24, 2022
4c83f61
Merge branch 'client-side-execution-context' of github.com:lizozom/ki…
Feb 24, 2022
a57bed7
lint
Feb 27, 2022
7d8b955
Merge branch 'main' into client-side-execution-context
Feb 28, 2022
373eb31
Merge branch 'main' into client-side-execution-context
Mar 1, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [CoreSetup](./kibana-plugin-core-public.coresetup.md) &gt; [executionContext](./kibana-plugin-core-public.coresetup.executioncontext.md)

## CoreSetup.executionContext property


<b>Signature:</b>

```typescript
executionContext: ExecutionContextSetup;
```
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export interface CoreSetup<TPluginsStart extends object = object, TStart = unkno
| Property | Type | Description |
| --- | --- | --- |
| [application](./kibana-plugin-core-public.coresetup.application.md) | ApplicationSetup | [ApplicationSetup](./kibana-plugin-core-public.applicationsetup.md) |
| [executionContext](./kibana-plugin-core-public.coresetup.executioncontext.md) | ExecutionContextSetup | |
| [fatalErrors](./kibana-plugin-core-public.coresetup.fatalerrors.md) | FatalErrorsSetup | [FatalErrorsSetup](./kibana-plugin-core-public.fatalerrorssetup.md) |
| [getStartServices](./kibana-plugin-core-public.coresetup.getstartservices.md) | StartServicesAccessor&lt;TPluginsStart, TStart&gt; | [StartServicesAccessor](./kibana-plugin-core-public.startservicesaccessor.md) |
| [http](./kibana-plugin-core-public.coresetup.http.md) | HttpSetup | [HttpSetup](./kibana-plugin-core-public.httpsetup.md) |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->

[Home](./index.md) &gt; [kibana-plugin-core-public](./kibana-plugin-core-public.md) &gt; [CoreStart](./kibana-plugin-core-public.corestart.md) &gt; [executionContext](./kibana-plugin-core-public.corestart.executioncontext.md)

## CoreStart.executionContext property


<b>Signature:</b>

```typescript
executionContext: ExecutionContextStart;
```
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export interface CoreStart
| [chrome](./kibana-plugin-core-public.corestart.chrome.md) | ChromeStart | [ChromeStart](./kibana-plugin-core-public.chromestart.md) |
| [deprecations](./kibana-plugin-core-public.corestart.deprecations.md) | DeprecationsServiceStart | [DeprecationsServiceStart](./kibana-plugin-core-public.deprecationsservicestart.md) |
| [docLinks](./kibana-plugin-core-public.corestart.doclinks.md) | DocLinksStart | [DocLinksStart](./kibana-plugin-core-public.doclinksstart.md) |
| [executionContext](./kibana-plugin-core-public.corestart.executioncontext.md) | ExecutionContextStart | |
| [fatalErrors](./kibana-plugin-core-public.corestart.fatalerrors.md) | FatalErrorsStart | [FatalErrorsStart](./kibana-plugin-core-public.fatalerrorsstart.md) |
| [http](./kibana-plugin-core-public.corestart.http.md) | HttpStart | [HttpStart](./kibana-plugin-core-public.httpstart.md) |
| [i18n](./kibana-plugin-core-public.corestart.i18n.md) | I18nStart | [I18nStart](./kibana-plugin-core-public.i18nstart.md) |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ Represents a meta-information about a Kibana entity initiating a search request.

```typescript
export declare type KibanaExecutionContext = {
readonly type: string;
readonly name: string;
readonly id: string;
readonly type?: string;
readonly name?: string;
readonly page?: string;
readonly id?: string;
readonly description?: string;
readonly url?: string;
child?: KibanaExecutionContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ Represents a meta-information about a Kibana entity initiating a search request.

```typescript
export declare type KibanaExecutionContext = {
readonly type: string;
readonly name: string;
readonly id: string;
readonly type?: string;
readonly name?: string;
readonly page?: string;
readonly id?: string;
readonly description?: string;
readonly url?: string;
child?: KibanaExecutionContext;
Expand Down
4 changes: 4 additions & 0 deletions src/core/public/apm_system.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type { Transaction } from '@elastic/apm-rum';
import { ApmSystem } from './apm_system';
import { Subject } from 'rxjs';
import { InternalApplicationStart } from './application/types';
import { executionContextServiceMock } from './execution_context/execution_context_service.mock';

const initMock = init as jest.Mocked<typeof init>;
const apmMock = apm as DeeplyMockedKeys<typeof apm>;
Expand Down Expand Up @@ -96,6 +97,7 @@ describe('ApmSystem', () => {
application: {
currentAppId$,
} as any as InternalApplicationStart,
executionContext: executionContextServiceMock.createInternalStartContract(),
});

expect(mark).toHaveBeenCalledWith('apm-start');
Expand All @@ -118,6 +120,7 @@ describe('ApmSystem', () => {
application: {
currentAppId$,
} as any as InternalApplicationStart,
executionContext: executionContextServiceMock.createInternalStartContract(),
});
currentAppId$.next('myapp');

Expand Down Expand Up @@ -145,6 +148,7 @@ describe('ApmSystem', () => {
application: {
currentAppId$,
} as any as InternalApplicationStart,
executionContext: executionContextServiceMock.createInternalStartContract(),
});
currentAppId$.next('myapp');

Expand Down
17 changes: 17 additions & 0 deletions src/core/public/apm_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type { ApmBase, AgentConfigOptions, Transaction } from '@elastic/apm-rum'
import { modifyUrl } from '@kbn/std';
import { CachedResourceObserver } from './apm_resource_counter';
import type { InternalApplicationStart } from './application';
import { ExecutionContextStart } from './execution_context';

/** "GET protocol://hostname:port/pathname" */
const HTTP_REQUEST_TRANSACTION_NAME_REGEX =
Expand All @@ -27,13 +28,15 @@ interface ApmConfig extends AgentConfigOptions {

interface StartDeps {
application: InternalApplicationStart;
executionContext: ExecutionContextStart;
}

export class ApmSystem {
private readonly enabled: boolean;
private pageLoadTransaction?: Transaction;
private resourceObserver: CachedResourceObserver;
private apm?: ApmBase;

/**
* `apmConfig` would be populated with relevant APM RUM agent
* configuration if server is started with elastic.apm.* config.
Expand Down Expand Up @@ -64,6 +67,20 @@ export class ApmSystem {

this.markPageLoadStart();

start.executionContext.context$.subscribe((c) => {
// We're using labels because we want the context to be indexed
// https://www.elastic.co/guide/en/apm/get-started/current/metadata.html
const apmContext = {
appId: c.name,
page: c.page,
id: c.id,
};

this.apm?.addLabels(apmContext);
});

// TODO: Start a new transaction every page change instead of every app change.

/**
* Register listeners for navigation changes and capture them as
* route-change transactions after Kibana app is bootstrapped
Expand Down
19 changes: 18 additions & 1 deletion src/core/public/core_system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import { DeprecationsService } from './deprecations';
import { ThemeService } from './theme';
import { CoreApp } from './core_app';
import type { InternalApplicationSetup, InternalApplicationStart } from './application/types';
import { ExecutionContextService } from './execution_context';

interface Params {
rootDomElement: HTMLElement;
Expand Down Expand Up @@ -87,6 +88,7 @@ export class CoreSystem {
private readonly theme: ThemeService;
private readonly rootDomElement: HTMLElement;
private readonly coreContext: CoreContext;
private readonly executionContext: ExecutionContextService;
private fatalErrorsSetup: FatalErrorsSetup | null = null;

constructor(params: Params) {
Expand Down Expand Up @@ -121,6 +123,7 @@ export class CoreSystem {
this.application = new ApplicationService();
this.integrations = new IntegrationsService();
this.deprecations = new DeprecationsService();
this.executionContext = new ExecutionContextService();

this.plugins = new PluginsService(this.coreContext, injectedMetadata.uiPlugins);
this.coreApp = new CoreApp(this.coreContext);
Expand All @@ -137,7 +140,13 @@ export class CoreSystem {
});
await this.integrations.setup();
this.docLinks.setup();
const http = this.http.setup({ injectedMetadata, fatalErrors: this.fatalErrorsSetup });

const executionContext = this.executionContext.setup();
const http = this.http.setup({
injectedMetadata,
fatalErrors: this.fatalErrorsSetup,
executionContext,
});
const uiSettings = this.uiSettings.setup({ http, injectedMetadata });
const notifications = this.notifications.setup({ uiSettings });
const theme = this.theme.setup({ injectedMetadata });
Expand All @@ -153,6 +162,7 @@ export class CoreSystem {
notifications,
theme,
uiSettings,
executionContext,
};

// Services that do not expose contracts at setup
Expand Down Expand Up @@ -201,6 +211,11 @@ export class CoreSystem {
targetDomElement: notificationsTargetDomElement,
});
const application = await this.application.start({ http, theme, overlays });

const executionContext = this.executionContext.start({
curApp$: application.currentAppId$,
});

const chrome = await this.chrome.start({
application,
docLinks,
Expand All @@ -216,6 +231,7 @@ export class CoreSystem {
application,
chrome,
docLinks,
executionContext,
http,
theme,
savedObjects,
Expand Down Expand Up @@ -248,6 +264,7 @@ export class CoreSystem {

return {
application,
executionContext,
};
} catch (error) {
if (this.fatalErrorsSetup) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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 { PublicMethodsOf } from '@kbn/utility-types';
import { BehaviorSubject } from 'rxjs';

import { ExecutionContextService, ExecutionContextSetup } from './execution_context_service';

const createContractMock = (): jest.Mocked<ExecutionContextSetup> => ({
context$: new BehaviorSubject({}),
clear: jest.fn(),
set: jest.fn(),
getAll: jest.fn(),
});

const createMock = (): jest.Mocked<PublicMethodsOf<ExecutionContextService>> => ({
setup: jest.fn().mockReturnValue(createContractMock()),
start: jest.fn().mockReturnValue(createContractMock()),
stop: jest.fn(),
});

export const executionContextServiceMock = {
create: createMock,
createSetupContract: createContractMock,
createStartContract: createContractMock,
createInternalSetupContract: createContractMock,
createInternalStartContract: createContractMock,
};
85 changes: 85 additions & 0 deletions src/core/public/execution_context/execution_context_service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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 { isEqual } from 'lodash';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { distinctUntilChanged } from 'rxjs/operators';
import { CoreService } from '../../types';

export type ExecutionContext = Record<string, any>;

/** @public */
export interface ExecutionContextSetup {
context$: Observable<ExecutionContext>;
set(c$: ExecutionContext): void;
getAll(): ExecutionContext;
lizozom marked this conversation as resolved.
Show resolved Hide resolved
clear(): void;
}

/**
* See {@link ExecutionContextSetup}.
* @public
*/
export type ExecutionContextStart = ExecutionContextSetup;

export interface StartDeps {
curApp$: Observable<string | undefined>;
}

/** @internal */
export class ExecutionContextService
implements CoreService<ExecutionContextSetup, ExecutionContextStart>
{
private context$: BehaviorSubject<ExecutionContext> = new BehaviorSubject({});
private appId?: string;
private subscription: Subscription = new Subscription();
private contract?: ExecutionContextSetup;

public setup() {
this.contract = {
context$: this.context$,
lizozom marked this conversation as resolved.
Show resolved Hide resolved
clear: () => {
this.context$.next({});
},
set: (c: ExecutionContext) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to enforce that set is called only once per app? As mentioned below (see https://github.com/elastic/kibana/pull/124996/files#r802797191), we have different logic on the server-side.
It's always hard to think about the final shape of an object after several mutations.

const newVal = {
url: window.location.pathname,
lizozom marked this conversation as resolved.
Show resolved Hide resolved
name: this.appId,
...this.context$.value,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Server-side doesn't merge the contexts but gets the final version. It allows customers to implement any custom logic.

...c,
};
if (!isEqual(newVal, this.context$.value)) {
this.context$.next(newVal);
}
},
getAll: () => {
return this.context$.value;
},
};

return this.contract;
}

public start({ curApp$ }: StartDeps) {
const start = this.contract!;

// Track app id changes and clear context on app change
this.subscription.add(
curApp$.pipe(distinctUntilChanged()).subscribe((appId) => {
lizozom marked this conversation as resolved.
Show resolved Hide resolved
start.clear();
this.appId = appId;
})
);

return start;
}

public stop() {
this.subscription.unsubscribe();
}
}
2 changes: 2 additions & 0 deletions src/core/public/execution_context/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@

export type { KibanaExecutionContext } from '../../types';
export { ExecutionContextContainer } from './execution_context_container';
export { ExecutionContextService } from './execution_context_service';
export type { ExecutionContextSetup, ExecutionContextStart } from './execution_context_service';
2 changes: 2 additions & 0 deletions src/core/public/http/fetch.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { first } from 'rxjs/operators';
import { Fetch } from './fetch';
import { BasePath } from './base_path';
import { HttpResponse, HttpFetchOptionsWithPath } from './types';
import { executionContextServiceMock } from '../execution_context/execution_context_service.mock';

function delay<T>(duration: number) {
return new Promise<T>((r) => setTimeout(r, duration));
Expand All @@ -26,6 +27,7 @@ describe('Fetch', () => {
const fetchInstance = new Fetch({
basePath: new BasePath(BASE_PATH),
kibanaVersion: 'VERSION',
executionContext: executionContextServiceMock.createSetupContract(),
});
afterEach(() => {
fetchMock.restore();
Expand Down
Loading