mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
* Client side execution app level context propagation
* context$ + apm rum integration
* invert the context parent \ child relationship (cc @mikhail)
move more things to top level context
* Pass down context to apm on server
* types
* eslint
* parent <> child
* docs + eslint + jest
* execution context mock
* eslint
* jest
* jest
* server jest
* check
* jest
* storybook
* jest
* report the current space
* fix server side context container
* Remove spaces for now
* docssss
* jest
* lint
* test
* docs
* revert file
* doc
* all context params are optional
* clear on page change
* lint
* ts
* skipped test again
* testing fixes
* oops
* code review #1
* code review #2
* getAsLabels
* maps inherit dashboard context
* docs
* ts
* Give common context to all vis editors
* fix test
* ts \ es \ tests
* labels
* missing types
* docsy docs
* cr #3
* improve jest
* Use editor name
* Update src/plugins/visualizations/public/visualize_app/components/visualize_editor.tsx
Co-authored-by: Marco Liberati <dej611@users.noreply.github.com>
* fix maps context
* jest tests for maps
* cr
* docs
* Update execution_context.test.ts
* docs
* lint
Co-authored-by: Marco Liberati <dej611@users.noreply.github.com>
(cherry picked from commit d5416ed4ae
)
# Conflicts:
# src/plugins/discover/public/application/context/context_app.tsx
# x-pack/plugins/lens/public/app_plugin/app.tsx
This commit is contained in:
parent
573f60eacd
commit
bb9ebc2265
84 changed files with 909 additions and 113 deletions
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [CoreSetup](./kibana-plugin-core-public.coresetup.md) > [executionContext](./kibana-plugin-core-public.coresetup.executioncontext.md)
|
||||
|
||||
## CoreSetup.executionContext property
|
||||
|
||||
[ExecutionContextSetup](./kibana-plugin-core-public.executioncontextsetup.md)
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
executionContext: ExecutionContextSetup;
|
||||
```
|
|
@ -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 | [ExecutionContextSetup](./kibana-plugin-core-public.executioncontextsetup.md) |
|
||||
| [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<TPluginsStart, TStart> | [StartServicesAccessor](./kibana-plugin-core-public.startservicesaccessor.md) |
|
||||
| [http](./kibana-plugin-core-public.coresetup.http.md) | HttpSetup | [HttpSetup](./kibana-plugin-core-public.httpsetup.md) |
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [CoreStart](./kibana-plugin-core-public.corestart.md) > [executionContext](./kibana-plugin-core-public.corestart.executioncontext.md)
|
||||
|
||||
## CoreStart.executionContext property
|
||||
|
||||
[ExecutionContextStart](./kibana-plugin-core-public.executioncontextstart.md)
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
executionContext: ExecutionContextStart;
|
||||
```
|
|
@ -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 | [ExecutionContextStart](./kibana-plugin-core-public.executioncontextstart.md) |
|
||||
| [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) |
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ExecutionContextSetup](./kibana-plugin-core-public.executioncontextsetup.md) > [clear](./kibana-plugin-core-public.executioncontextsetup.clear.md)
|
||||
|
||||
## ExecutionContextSetup.clear() method
|
||||
|
||||
clears the context
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
clear(): void;
|
||||
```
|
||||
<b>Returns:</b>
|
||||
|
||||
void
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ExecutionContextSetup](./kibana-plugin-core-public.executioncontextsetup.md) > [context$](./kibana-plugin-core-public.executioncontextsetup.context_.md)
|
||||
|
||||
## ExecutionContextSetup.context$ property
|
||||
|
||||
The current context observable
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
context$: Observable<KibanaExecutionContext>;
|
||||
```
|
|
@ -0,0 +1,17 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ExecutionContextSetup](./kibana-plugin-core-public.executioncontextsetup.md) > [get](./kibana-plugin-core-public.executioncontextsetup.get.md)
|
||||
|
||||
## ExecutionContextSetup.get() method
|
||||
|
||||
Get the current top level context
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
get(): KibanaExecutionContext;
|
||||
```
|
||||
<b>Returns:</b>
|
||||
|
||||
KibanaExecutionContext
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ExecutionContextSetup](./kibana-plugin-core-public.executioncontextsetup.md) > [getAsLabels](./kibana-plugin-core-public.executioncontextsetup.getaslabels.md)
|
||||
|
||||
## ExecutionContextSetup.getAsLabels() method
|
||||
|
||||
returns apm labels
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getAsLabels(): Labels;
|
||||
```
|
||||
<b>Returns:</b>
|
||||
|
||||
Labels
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ExecutionContextSetup](./kibana-plugin-core-public.executioncontextsetup.md)
|
||||
|
||||
## ExecutionContextSetup interface
|
||||
|
||||
Kibana execution context. Used to provide execution context to Elasticsearch, reporting, performance monitoring, etc.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface ExecutionContextSetup
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [context$](./kibana-plugin-core-public.executioncontextsetup.context_.md) | Observable<KibanaExecutionContext> | The current context observable |
|
||||
|
||||
## Methods
|
||||
|
||||
| Method | Description |
|
||||
| --- | --- |
|
||||
| [clear()](./kibana-plugin-core-public.executioncontextsetup.clear.md) | clears the context |
|
||||
| [get()](./kibana-plugin-core-public.executioncontextsetup.get.md) | Get the current top level context |
|
||||
| [getAsLabels()](./kibana-plugin-core-public.executioncontextsetup.getaslabels.md) | returns apm labels |
|
||||
| [set(c$)](./kibana-plugin-core-public.executioncontextsetup.set.md) | Set the current top level context |
|
||||
| [withGlobalContext(context)](./kibana-plugin-core-public.executioncontextsetup.withglobalcontext.md) | merges the current top level context with the specific event context |
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ExecutionContextSetup](./kibana-plugin-core-public.executioncontextsetup.md) > [set](./kibana-plugin-core-public.executioncontextsetup.set.md)
|
||||
|
||||
## ExecutionContextSetup.set() method
|
||||
|
||||
Set the current top level context
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
set(c$: KibanaExecutionContext): void;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| c$ | KibanaExecutionContext | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
void
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ExecutionContextSetup](./kibana-plugin-core-public.executioncontextsetup.md) > [withGlobalContext](./kibana-plugin-core-public.executioncontextsetup.withglobalcontext.md)
|
||||
|
||||
## ExecutionContextSetup.withGlobalContext() method
|
||||
|
||||
merges the current top level context with the specific event context
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
withGlobalContext(context?: KibanaExecutionContext): KibanaExecutionContext;
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| context | KibanaExecutionContext | |
|
||||
|
||||
<b>Returns:</b>
|
||||
|
||||
KibanaExecutionContext
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-public](./kibana-plugin-core-public.md) > [ExecutionContextStart](./kibana-plugin-core-public.executioncontextstart.md)
|
||||
|
||||
## ExecutionContextStart type
|
||||
|
||||
See [ExecutionContextSetup](./kibana-plugin-core-public.executioncontextsetup.md)<!-- -->.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare type ExecutionContextStart = ExecutionContextSetup;
|
||||
```
|
|
@ -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;
|
||||
|
|
|
@ -62,6 +62,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
|
|||
| [DeprecationsServiceStart](./kibana-plugin-core-public.deprecationsservicestart.md) | DeprecationsService provides methods to fetch domain deprecation details from the Kibana server. |
|
||||
| [DocLinksStart](./kibana-plugin-core-public.doclinksstart.md) | |
|
||||
| [ErrorToastOptions](./kibana-plugin-core-public.errortoastoptions.md) | Options available for [IToasts](./kibana-plugin-core-public.itoasts.md) error APIs. |
|
||||
| [ExecutionContextSetup](./kibana-plugin-core-public.executioncontextsetup.md) | Kibana execution context. Used to provide execution context to Elasticsearch, reporting, performance monitoring, etc. |
|
||||
| [FatalErrorInfo](./kibana-plugin-core-public.fatalerrorinfo.md) | Represents the <code>message</code> and <code>stack</code> of a fatal Error |
|
||||
| [FatalErrorsSetup](./kibana-plugin-core-public.fatalerrorssetup.md) | FatalErrors stop the Kibana Public Core and displays a fatal error screen with details about the Kibana build and the error. |
|
||||
| [HttpFetchOptions](./kibana-plugin-core-public.httpfetchoptions.md) | All options that may be used with a [HttpHandler](./kibana-plugin-core-public.httphandler.md)<!-- -->. |
|
||||
|
@ -160,6 +161,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
|
|||
| [ChromeBreadcrumb](./kibana-plugin-core-public.chromebreadcrumb.md) | |
|
||||
| [ChromeHelpExtensionLinkBase](./kibana-plugin-core-public.chromehelpextensionlinkbase.md) | |
|
||||
| [ChromeHelpExtensionMenuLink](./kibana-plugin-core-public.chromehelpextensionmenulink.md) | |
|
||||
| [ExecutionContextStart](./kibana-plugin-core-public.executioncontextstart.md) | See [ExecutionContextSetup](./kibana-plugin-core-public.executioncontextsetup.md)<!-- -->. |
|
||||
| [FatalErrorsStart](./kibana-plugin-core-public.fatalerrorsstart.md) | FatalErrors stop the Kibana Public Core and displays a fatal error screen with details about the Kibana build and the error. |
|
||||
| [HttpStart](./kibana-plugin-core-public.httpstart.md) | See [HttpSetup](./kibana-plugin-core-public.httpsetup.md) |
|
||||
| [IToasts](./kibana-plugin-core-public.itoasts.md) | Methods for adding and removing global toast messages. See [ToastsApi](./kibana-plugin-core-public.toastsapi.md)<!-- -->. |
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-core-server](./kibana-plugin-core-server.md) > [ExecutionContextSetup](./kibana-plugin-core-server.executioncontextsetup.md) > [getAsLabels](./kibana-plugin-core-server.executioncontextsetup.getaslabels.md)
|
||||
|
||||
## ExecutionContextSetup.getAsLabels() method
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
getAsLabels(): apm.Labels;
|
||||
```
|
||||
<b>Returns:</b>
|
||||
|
||||
apm.Labels
|
||||
|
|
@ -15,5 +15,6 @@ export interface ExecutionContextSetup
|
|||
|
||||
| Method | Description |
|
||||
| --- | --- |
|
||||
| [getAsLabels()](./kibana-plugin-core-server.executioncontextsetup.getaslabels.md) | |
|
||||
| [withContext(context, fn)](./kibana-plugin-core-server.executioncontextsetup.withcontext.md) | Keeps track of execution context while the passed function is executed. Data are carried over all async operations spawned by the passed function. The nested calls stack the registered context on top of each other. |
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>;
|
||||
|
@ -96,6 +97,7 @@ describe('ApmSystem', () => {
|
|||
application: {
|
||||
currentAppId$,
|
||||
} as any as InternalApplicationStart,
|
||||
executionContext: executionContextServiceMock.createInternalStartContract(),
|
||||
});
|
||||
|
||||
expect(mark).toHaveBeenCalledWith('apm-start');
|
||||
|
@ -118,6 +120,7 @@ describe('ApmSystem', () => {
|
|||
application: {
|
||||
currentAppId$,
|
||||
} as any as InternalApplicationStart,
|
||||
executionContext: executionContextServiceMock.createInternalStartContract(),
|
||||
});
|
||||
currentAppId$.next('myapp');
|
||||
|
||||
|
@ -145,6 +148,7 @@ describe('ApmSystem', () => {
|
|||
application: {
|
||||
currentAppId$,
|
||||
} as any as InternalApplicationStart,
|
||||
executionContext: executionContextServiceMock.createInternalStartContract(),
|
||||
});
|
||||
currentAppId$.next('myapp');
|
||||
|
||||
|
|
|
@ -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 =
|
||||
|
@ -27,6 +28,7 @@ interface ApmConfig extends AgentConfigOptions {
|
|||
|
||||
interface StartDeps {
|
||||
application: InternalApplicationStart;
|
||||
executionContext: ExecutionContextStart;
|
||||
}
|
||||
|
||||
export class ApmSystem {
|
||||
|
@ -34,6 +36,7 @@ export class ApmSystem {
|
|||
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.
|
||||
|
@ -64,6 +67,15 @@ 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 = start.executionContext.getAsLabels();
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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 });
|
||||
|
@ -153,6 +162,7 @@ export class CoreSystem {
|
|||
notifications,
|
||||
theme,
|
||||
uiSettings,
|
||||
executionContext,
|
||||
};
|
||||
|
||||
// Services that do not expose contracts at setup
|
||||
|
@ -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,
|
||||
|
@ -216,6 +231,7 @@ export class CoreSystem {
|
|||
application,
|
||||
chrome,
|
||||
docLinks,
|
||||
executionContext,
|
||||
http,
|
||||
theme,
|
||||
savedObjects,
|
||||
|
@ -248,6 +264,7 @@ export class CoreSystem {
|
|||
|
||||
return {
|
||||
application,
|
||||
executionContext,
|
||||
};
|
||||
} catch (error) {
|
||||
if (this.fatalErrorsSetup) {
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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(),
|
||||
get: jest.fn(),
|
||||
getAsLabels: jest.fn(),
|
||||
withGlobalContext: 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,
|
||||
};
|
134
src/core/public/execution_context/execution_context_service.ts
Normal file
134
src/core/public/execution_context/execution_context_service.ts
Normal file
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
* 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, isUndefined, omitBy } from 'lodash';
|
||||
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
|
||||
import { CoreService, KibanaExecutionContext } from '../../types';
|
||||
|
||||
// Should be exported from elastic/apm-rum
|
||||
export type LabelValue = string | number | boolean;
|
||||
|
||||
export interface Labels {
|
||||
[key: string]: LabelValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Kibana execution context.
|
||||
* Used to provide execution context to Elasticsearch, reporting, performance monitoring, etc.
|
||||
* @public
|
||||
**/
|
||||
export interface ExecutionContextSetup {
|
||||
/**
|
||||
* The current context observable
|
||||
**/
|
||||
context$: Observable<KibanaExecutionContext>;
|
||||
/**
|
||||
* Set the current top level context
|
||||
**/
|
||||
set(c$: KibanaExecutionContext): void;
|
||||
/**
|
||||
* Get the current top level context
|
||||
**/
|
||||
get(): KibanaExecutionContext;
|
||||
/**
|
||||
* clears the context
|
||||
**/
|
||||
clear(): void;
|
||||
/**
|
||||
* returns apm labels
|
||||
**/
|
||||
getAsLabels(): Labels;
|
||||
/**
|
||||
* merges the current top level context with the specific event context
|
||||
**/
|
||||
withGlobalContext(context?: KibanaExecutionContext): KibanaExecutionContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<KibanaExecutionContext> = new BehaviorSubject({});
|
||||
private appId?: string;
|
||||
private subscription: Subscription = new Subscription();
|
||||
private contract?: ExecutionContextSetup;
|
||||
|
||||
public setup() {
|
||||
this.contract = {
|
||||
context$: this.context$.asObservable(),
|
||||
clear: () => {
|
||||
this.context$.next({});
|
||||
},
|
||||
set: (c: KibanaExecutionContext) => {
|
||||
const newVal = {
|
||||
...this.context$.value,
|
||||
...c,
|
||||
};
|
||||
if (!isEqual(newVal, this.context$.value)) {
|
||||
this.context$.next(newVal);
|
||||
}
|
||||
},
|
||||
get: () => {
|
||||
return this.mergeContext();
|
||||
},
|
||||
getAsLabels: () => {
|
||||
return this.removeUndefined({
|
||||
name: this.appId,
|
||||
id: this.context$.value?.id,
|
||||
page: this.context$.value?.page,
|
||||
}) as Labels;
|
||||
},
|
||||
withGlobalContext: (context: KibanaExecutionContext) => {
|
||||
return this.mergeContext(context);
|
||||
},
|
||||
};
|
||||
|
||||
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$.subscribe((appId) => {
|
||||
this.appId = appId;
|
||||
start.clear();
|
||||
})
|
||||
);
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
public stop() {
|
||||
this.subscription.unsubscribe();
|
||||
}
|
||||
|
||||
private removeUndefined(context: KibanaExecutionContext = {}) {
|
||||
return omitBy(context, isUndefined);
|
||||
}
|
||||
|
||||
private mergeContext(context: KibanaExecutionContext = {}): KibanaExecutionContext {
|
||||
return {
|
||||
name: this.appId,
|
||||
url: window.location.pathname,
|
||||
...this.context$.value,
|
||||
...context,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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';
|
||||
|
|
|
@ -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));
|
||||
|
@ -23,9 +24,11 @@ function delay<T>(duration: number) {
|
|||
const BASE_PATH = 'http://localhost/myBase';
|
||||
|
||||
describe('Fetch', () => {
|
||||
const executionContextMock = executionContextServiceMock.createSetupContract();
|
||||
const fetchInstance = new Fetch({
|
||||
basePath: new BasePath(BASE_PATH),
|
||||
kibanaVersion: 'VERSION',
|
||||
executionContext: executionContextMock,
|
||||
});
|
||||
afterEach(() => {
|
||||
fetchMock.restore();
|
||||
|
@ -230,13 +233,15 @@ describe('Fetch', () => {
|
|||
it('should inject context headers if provided', async () => {
|
||||
fetchMock.get('*', {});
|
||||
|
||||
const context = {
|
||||
type: 'test-type',
|
||||
name: 'test-name',
|
||||
description: 'test-description',
|
||||
id: '42',
|
||||
};
|
||||
executionContextMock.withGlobalContext.mockReturnValue(context);
|
||||
await fetchInstance.fetch('/my/path', {
|
||||
context: {
|
||||
type: 'test-type',
|
||||
name: 'test-name',
|
||||
description: 'test-description',
|
||||
id: '42',
|
||||
},
|
||||
context,
|
||||
});
|
||||
|
||||
expect(fetchMock.lastOptions()!.headers).toMatchObject({
|
||||
|
@ -245,6 +250,29 @@ describe('Fetch', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should include top level context context headers if provided', async () => {
|
||||
fetchMock.get('*', {});
|
||||
|
||||
const context = {
|
||||
type: 'test-type',
|
||||
name: 'test-name',
|
||||
description: 'test-description',
|
||||
id: '42',
|
||||
};
|
||||
executionContextMock.withGlobalContext.mockReturnValue({
|
||||
...context,
|
||||
name: 'banana',
|
||||
});
|
||||
await fetchInstance.fetch('/my/path', {
|
||||
context,
|
||||
});
|
||||
|
||||
expect(fetchMock.lastOptions()!.headers).toMatchObject({
|
||||
'x-kbn-context':
|
||||
'%7B%22type%22%3A%22test-type%22%2C%22name%22%3A%22banana%22%2C%22description%22%3A%22test-description%22%2C%22id%22%3A%2242%22%7D',
|
||||
});
|
||||
});
|
||||
|
||||
it('should return response', async () => {
|
||||
fetchMock.get('*', { foo: 'bar' });
|
||||
const json = await fetchInstance.fetch('/my/path');
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { omitBy } from 'lodash';
|
||||
import { isEmpty, omitBy } from 'lodash';
|
||||
import { format } from 'url';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
|
@ -22,11 +22,12 @@ import { HttpFetchError } from './http_fetch_error';
|
|||
import { HttpInterceptController } from './http_intercept_controller';
|
||||
import { interceptRequest, interceptResponse } from './intercept';
|
||||
import { HttpInterceptHaltError } from './http_intercept_halt_error';
|
||||
import { ExecutionContextContainer } from '../execution_context';
|
||||
import { ExecutionContextContainer, ExecutionContextSetup } from '../execution_context';
|
||||
|
||||
interface Params {
|
||||
basePath: IBasePath;
|
||||
kibanaVersion: string;
|
||||
executionContext: ExecutionContextSetup;
|
||||
}
|
||||
|
||||
const JSON_CONTENT = /^(application\/(json|x-javascript)|text\/(x-)?javascript|x-json)(;.*)?$/;
|
||||
|
@ -107,6 +108,7 @@ export class Fetch {
|
|||
};
|
||||
|
||||
private createRequest(options: HttpFetchOptionsWithPath): Request {
|
||||
const context = this.params.executionContext.withGlobalContext(options.context);
|
||||
// Merge and destructure options out that are not applicable to the Fetch API.
|
||||
const {
|
||||
query,
|
||||
|
@ -125,7 +127,7 @@ export class Fetch {
|
|||
'Content-Type': 'application/json',
|
||||
...options.headers,
|
||||
'kbn-version': this.params.kibanaVersion,
|
||||
...(options.context ? new ExecutionContextContainer(options.context).toHeader() : {}),
|
||||
...(!isEmpty(context) ? new ExecutionContextContainer(context).toHeader() : {}),
|
||||
}),
|
||||
};
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import { fatalErrorsServiceMock } from '../fatal_errors/fatal_errors_service.moc
|
|||
import { injectedMetadataServiceMock } from '../injected_metadata/injected_metadata_service.mock';
|
||||
import { HttpService } from './http_service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { executionContextServiceMock } from '../execution_context/execution_context_service.mock';
|
||||
|
||||
describe('interceptors', () => {
|
||||
afterEach(() => fetchMock.restore());
|
||||
|
@ -22,9 +23,10 @@ describe('interceptors', () => {
|
|||
fetchMock.get('*', {});
|
||||
const injectedMetadata = injectedMetadataServiceMock.createSetupContract();
|
||||
const fatalErrors = fatalErrorsServiceMock.createSetupContract();
|
||||
const executionContext = executionContextServiceMock.createSetupContract();
|
||||
const httpService = new HttpService();
|
||||
|
||||
const setup = httpService.setup({ fatalErrors, injectedMetadata });
|
||||
const setup = httpService.setup({ fatalErrors, injectedMetadata, executionContext });
|
||||
const setupInterceptor = jest.fn();
|
||||
setup.intercept({ request: setupInterceptor });
|
||||
|
||||
|
@ -47,7 +49,8 @@ describe('#setup()', () => {
|
|||
const injectedMetadata = injectedMetadataServiceMock.createSetupContract();
|
||||
const fatalErrors = fatalErrorsServiceMock.createSetupContract();
|
||||
const httpService = new HttpService();
|
||||
httpService.setup({ fatalErrors, injectedMetadata });
|
||||
const executionContext = executionContextServiceMock.createSetupContract();
|
||||
httpService.setup({ fatalErrors, injectedMetadata, executionContext });
|
||||
const loadingServiceSetup = loadingServiceMock.setup.mock.results[0].value;
|
||||
// We don't verify that this Observable comes from Fetch#getLoadingCount$() to avoid complex mocking
|
||||
expect(loadingServiceSetup.addLoadingCountSource).toHaveBeenCalledWith(expect.any(Observable));
|
||||
|
@ -59,7 +62,8 @@ describe('#stop()', () => {
|
|||
const injectedMetadata = injectedMetadataServiceMock.createSetupContract();
|
||||
const fatalErrors = fatalErrorsServiceMock.createSetupContract();
|
||||
const httpService = new HttpService();
|
||||
httpService.setup({ fatalErrors, injectedMetadata });
|
||||
const executionContext = executionContextServiceMock.createSetupContract();
|
||||
httpService.setup({ fatalErrors, injectedMetadata, executionContext });
|
||||
httpService.start();
|
||||
httpService.stop();
|
||||
expect(loadingServiceMock.stop).toHaveBeenCalled();
|
||||
|
|
|
@ -15,10 +15,12 @@ import { LoadingCountService } from './loading_count_service';
|
|||
import { Fetch } from './fetch';
|
||||
import { CoreService } from '../../types';
|
||||
import { ExternalUrlService } from './external_url_service';
|
||||
import { ExecutionContextSetup } from '../execution_context';
|
||||
|
||||
interface HttpDeps {
|
||||
injectedMetadata: InjectedMetadataSetup;
|
||||
fatalErrors: FatalErrorsSetup;
|
||||
executionContext: ExecutionContextSetup;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
|
@ -27,14 +29,15 @@ export class HttpService implements CoreService<HttpSetup, HttpStart> {
|
|||
private readonly loadingCount = new LoadingCountService();
|
||||
private service?: HttpSetup;
|
||||
|
||||
public setup({ injectedMetadata, fatalErrors }: HttpDeps): HttpSetup {
|
||||
public setup({ injectedMetadata, fatalErrors, executionContext }: HttpDeps): HttpSetup {
|
||||
const kibanaVersion = injectedMetadata.getKibanaVersion();
|
||||
const basePath = new BasePath(
|
||||
injectedMetadata.getBasePath(),
|
||||
injectedMetadata.getServerBasePath(),
|
||||
injectedMetadata.getPublicBaseUrl()
|
||||
);
|
||||
const fetchService = new Fetch({ basePath, kibanaVersion });
|
||||
|
||||
const fetchService = new Fetch({ basePath, kibanaVersion, executionContext });
|
||||
const loadingCount = this.loadingCount.setup({ fatalErrors });
|
||||
loadingCount.addLoadingCountSource(fetchService.getRequestCount$());
|
||||
|
||||
|
|
|
@ -65,6 +65,7 @@ import { DocLinksStart } from './doc_links';
|
|||
import { SavedObjectsStart } from './saved_objects';
|
||||
import { DeprecationsServiceStart } from './deprecations';
|
||||
import type { ThemeServiceSetup, ThemeServiceStart } from './theme';
|
||||
import { ExecutionContextSetup, ExecutionContextStart } from './execution_context';
|
||||
|
||||
export type {
|
||||
PackageInfo,
|
||||
|
@ -194,7 +195,11 @@ export type { MountPoint, UnmountCallback, PublicUiSettingsParams } from './type
|
|||
|
||||
export { URL_MAX_LENGTH } from './core_app';
|
||||
|
||||
export type { KibanaExecutionContext } from './execution_context';
|
||||
export type {
|
||||
KibanaExecutionContext,
|
||||
ExecutionContextSetup,
|
||||
ExecutionContextStart,
|
||||
} from './execution_context';
|
||||
|
||||
/**
|
||||
* Core services exposed to the `Plugin` setup lifecycle
|
||||
|
@ -221,6 +226,8 @@ export interface CoreSetup<TPluginsStart extends object = object, TStart = unkno
|
|||
notifications: NotificationsSetup;
|
||||
/** {@link IUiSettingsClient} */
|
||||
uiSettings: IUiSettingsClient;
|
||||
/** {@link ExecutionContextSetup} */
|
||||
executionContext: ExecutionContextSetup;
|
||||
/**
|
||||
* exposed temporarily until https://github.com/elastic/kibana/issues/41990 done
|
||||
* use *only* to retrieve config values. There is no way to set injected values
|
||||
|
@ -264,6 +271,8 @@ export interface CoreStart {
|
|||
chrome: ChromeStart;
|
||||
/** {@link DocLinksStart} */
|
||||
docLinks: DocLinksStart;
|
||||
/** {@link ExecutionContextStart} */
|
||||
executionContext: ExecutionContextStart;
|
||||
/** {@link HttpStart} */
|
||||
http: HttpStart;
|
||||
/** {@link SavedObjectsStart} */
|
||||
|
|
|
@ -29,6 +29,7 @@ import { themeServiceMock } from './theme/theme_service.mock';
|
|||
|
||||
export { chromeServiceMock } from './chrome/chrome_service.mock';
|
||||
export { docLinksServiceMock } from './doc_links/doc_links_service.mock';
|
||||
import { executionContextServiceMock } from './execution_context/execution_context_service.mock';
|
||||
export { fatalErrorsServiceMock } from './fatal_errors/fatal_errors_service.mock';
|
||||
export { httpServiceMock } from './http/http_service.mock';
|
||||
export { i18nServiceMock } from './i18n/i18n_service.mock';
|
||||
|
@ -54,6 +55,7 @@ function createCoreSetupMock({
|
|||
const mock = {
|
||||
application: applicationServiceMock.createSetupContract(),
|
||||
docLinks: docLinksServiceMock.createSetupContract(),
|
||||
executionContext: executionContextServiceMock.createSetupContract(),
|
||||
fatalErrors: fatalErrorsServiceMock.createSetupContract(),
|
||||
getStartServices: jest.fn<Promise<[ReturnType<typeof createCoreStartMock>, any, any]>, []>(() =>
|
||||
Promise.resolve([createCoreStartMock({ basePath }), pluginStartDeps, pluginStartContract])
|
||||
|
@ -76,6 +78,7 @@ function createCoreStartMock({ basePath = '' } = {}) {
|
|||
application: applicationServiceMock.createStartContract(),
|
||||
chrome: chromeServiceMock.createStartContract(),
|
||||
docLinks: docLinksServiceMock.createStartContract(),
|
||||
executionContext: executionContextServiceMock.createStartContract(),
|
||||
http: httpServiceMock.createStartContract({ basePath }),
|
||||
i18n: i18nServiceMock.createStartContract(),
|
||||
notifications: notificationServiceMock.createStartContract(),
|
||||
|
|
|
@ -88,6 +88,7 @@ export function createPluginSetupContext<
|
|||
registerAppUpdater: (statusUpdater$) => deps.application.registerAppUpdater(statusUpdater$),
|
||||
},
|
||||
fatalErrors: deps.fatalErrors,
|
||||
executionContext: deps.executionContext,
|
||||
http: deps.http,
|
||||
notifications: deps.notifications,
|
||||
uiSettings: deps.uiSettings,
|
||||
|
@ -129,6 +130,7 @@ export function createPluginStartContext<
|
|||
getUrlForApp: deps.application.getUrlForApp,
|
||||
},
|
||||
docLinks: deps.docLinks,
|
||||
executionContext: deps.executionContext,
|
||||
http: deps.http,
|
||||
chrome: omit(deps.chrome, 'getComponent'),
|
||||
i18n: deps.i18n,
|
||||
|
|
|
@ -36,6 +36,7 @@ import { docLinksServiceMock } from '../doc_links/doc_links_service.mock';
|
|||
import { savedObjectsServiceMock } from '../saved_objects/saved_objects_service.mock';
|
||||
import { deprecationsServiceMock } from '../deprecations/deprecations_service.mock';
|
||||
import { themeServiceMock } from '../theme/theme_service.mock';
|
||||
import { executionContextServiceMock } from '../execution_context/execution_context_service.mock';
|
||||
|
||||
export let mockPluginInitializers: Map<PluginName, MockedPluginInitializer>;
|
||||
|
||||
|
@ -85,6 +86,7 @@ describe('PluginsService', () => {
|
|||
mockSetupDeps = {
|
||||
application: applicationServiceMock.createInternalSetupContract(),
|
||||
fatalErrors: fatalErrorsServiceMock.createSetupContract(),
|
||||
executionContext: executionContextServiceMock.createSetupContract(),
|
||||
http: httpServiceMock.createSetupContract(),
|
||||
injectedMetadata: injectedMetadataServiceMock.createStartContract(),
|
||||
notifications: notificationServiceMock.createSetupContract(),
|
||||
|
@ -100,6 +102,7 @@ describe('PluginsService', () => {
|
|||
mockStartDeps = {
|
||||
application: applicationServiceMock.createInternalStartContract(),
|
||||
docLinks: docLinksServiceMock.createStartContract(),
|
||||
executionContext: executionContextServiceMock.createStartContract(),
|
||||
http: httpServiceMock.createStartContract(),
|
||||
chrome: chromeServiceMock.createStartContract(),
|
||||
i18n: i18nServiceMock.createStartContract(),
|
||||
|
|
|
@ -399,6 +399,8 @@ export interface CoreSetup<TPluginsStart extends object = object, TStart = unkno
|
|||
// (undocumented)
|
||||
application: ApplicationSetup;
|
||||
// (undocumented)
|
||||
executionContext: ExecutionContextSetup;
|
||||
// (undocumented)
|
||||
fatalErrors: FatalErrorsSetup;
|
||||
// (undocumented)
|
||||
getStartServices: StartServicesAccessor<TPluginsStart, TStart>;
|
||||
|
@ -427,6 +429,8 @@ export interface CoreStart {
|
|||
// (undocumented)
|
||||
docLinks: DocLinksStart;
|
||||
// (undocumented)
|
||||
executionContext: ExecutionContextStart;
|
||||
// (undocumented)
|
||||
fatalErrors: FatalErrorsStart;
|
||||
// (undocumented)
|
||||
http: HttpStart;
|
||||
|
@ -459,6 +463,7 @@ export class CoreSystem {
|
|||
// (undocumented)
|
||||
start(): Promise<{
|
||||
application: InternalApplicationStart;
|
||||
executionContext: ExecutionContextSetup;
|
||||
} | undefined>;
|
||||
// (undocumented)
|
||||
stop(): void;
|
||||
|
@ -509,6 +514,20 @@ export interface ErrorToastOptions extends ToastOptions {
|
|||
toastMessage?: string;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface ExecutionContextSetup {
|
||||
clear(): void;
|
||||
context$: Observable<KibanaExecutionContext>;
|
||||
get(): KibanaExecutionContext;
|
||||
// Warning: (ae-forgotten-export) The symbol "Labels" needs to be exported by the entry point index.d.ts
|
||||
getAsLabels(): Labels_2;
|
||||
set(c$: KibanaExecutionContext): void;
|
||||
withGlobalContext(context?: KibanaExecutionContext): KibanaExecutionContext;
|
||||
}
|
||||
|
||||
// @public
|
||||
export type ExecutionContextStart = ExecutionContextSetup;
|
||||
|
||||
// @public
|
||||
export interface FatalErrorInfo {
|
||||
// (undocumented)
|
||||
|
@ -749,9 +768,10 @@ export interface IUiSettingsClient {
|
|||
|
||||
// @public
|
||||
export 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;
|
||||
|
@ -1520,6 +1540,6 @@ export interface UserProvidedValues<T = any> {
|
|||
|
||||
// Warnings were encountered during analysis:
|
||||
//
|
||||
// src/core/public/core_system.ts:173:21 - (ae-forgotten-export) The symbol "InternalApplicationStart" needs to be exported by the entry point index.d.ts
|
||||
// src/core/public/core_system.ts:183:21 - (ae-forgotten-export) The symbol "InternalApplicationStart" needs to be exported by the entry point index.d.ts
|
||||
|
||||
```
|
||||
|
|
|
@ -50,9 +50,10 @@ export interface IExecutionContextContainer {
|
|||
}
|
||||
|
||||
function stringify(ctx: KibanaExecutionContext): string {
|
||||
const stringifiedCtx = `${encodeURIComponent(ctx.type)}:${encodeURIComponent(
|
||||
const encodeURIComponentIfNotEmpty = (val?: string) => encodeURIComponent(val || '');
|
||||
const stringifiedCtx = `${encodeURIComponentIfNotEmpty(ctx.type)}:${encodeURIComponentIfNotEmpty(
|
||||
ctx.name
|
||||
)}:${encodeURIComponent(ctx.id!)}`;
|
||||
)}:${encodeURIComponentIfNotEmpty(ctx.id)}`;
|
||||
return ctx.child ? `${stringifiedCtx};${stringify(ctx.child)}` : stringifiedCtx;
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ const createExecutionContextMock = () => {
|
|||
get: jest.fn(),
|
||||
getParentContextFrom: jest.fn(),
|
||||
getAsHeader: jest.fn(),
|
||||
getAsLabels: jest.fn(),
|
||||
};
|
||||
mock.withContext.mockImplementation(withContextMock);
|
||||
return mock;
|
||||
|
@ -38,6 +39,7 @@ const createInternalSetupContractMock = () => {
|
|||
const createSetupContractMock = () => {
|
||||
const mock: jest.Mocked<ExecutionContextSetup> = {
|
||||
withContext: jest.fn(),
|
||||
getAsLabels: jest.fn(),
|
||||
};
|
||||
mock.withContext.mockImplementation(withContextMock);
|
||||
return mock;
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
import { AsyncLocalStorage } from 'async_hooks';
|
||||
import apm from 'elastic-apm-node';
|
||||
import { isUndefined, omitBy } from 'lodash';
|
||||
import type { Subscription } from 'rxjs';
|
||||
|
||||
import type { CoreService, KibanaExecutionContext } from '../../types';
|
||||
|
@ -39,6 +41,10 @@ export interface IExecutionContext {
|
|||
* returns serialized representation to send as a header
|
||||
**/
|
||||
getAsHeader(): string | undefined;
|
||||
/**
|
||||
* returns apm labels
|
||||
**/
|
||||
getAsLabels(): apm.Labels;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -61,6 +67,7 @@ export interface ExecutionContextSetup {
|
|||
* The nested calls stack the registered context on top of each other.
|
||||
**/
|
||||
withContext<R>(context: KibanaExecutionContext | undefined, fn: (...args: any[]) => R): R;
|
||||
getAsLabels(): apm.Labels;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -97,6 +104,7 @@ export class ExecutionContextService
|
|||
setRequestId: this.setRequestId.bind(this),
|
||||
get: this.get.bind(this),
|
||||
getAsHeader: this.getAsHeader.bind(this),
|
||||
getAsLabels: this.getAsLabels.bind(this),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -108,6 +116,7 @@ export class ExecutionContextService
|
|||
withContext: this.withContext.bind(this),
|
||||
get: this.get.bind(this),
|
||||
getAsHeader: this.getAsHeader.bind(this),
|
||||
getAsLabels: this.getAsLabels.bind(this),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -161,4 +170,18 @@ export class ExecutionContextService
|
|||
|
||||
return `${requestId}${executionContextStr}`;
|
||||
}
|
||||
|
||||
private getAsLabels() {
|
||||
if (!this.enabled) return {};
|
||||
const executionContext = this.contextStore.getStore()?.toJSON();
|
||||
|
||||
return omitBy(
|
||||
{
|
||||
name: executionContext?.name,
|
||||
id: executionContext?.id,
|
||||
page: executionContext?.page,
|
||||
},
|
||||
isUndefined
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import agent from 'elastic-apm-node';
|
|||
import type { Duration } from 'moment';
|
||||
import { Observable } from 'rxjs';
|
||||
import { take } from 'rxjs/operators';
|
||||
import apm from 'elastic-apm-node';
|
||||
import { Logger, LoggerFactory } from '../logging';
|
||||
import { HttpConfig } from './http_config';
|
||||
import type { InternalExecutionContextSetup } from '../execution_context';
|
||||
|
@ -338,7 +339,11 @@ export class HttpServer {
|
|||
const requestId = getRequestId(request, config.requestId);
|
||||
|
||||
const parentContext = executionContext?.getParentContextFrom(request.headers);
|
||||
if (parentContext) executionContext?.set(parentContext);
|
||||
|
||||
if (executionContext && parentContext) {
|
||||
executionContext.set(parentContext);
|
||||
apm.addLabels(executionContext.getAsLabels());
|
||||
}
|
||||
|
||||
executionContext?.setRequestId(requestId);
|
||||
|
||||
|
|
|
@ -161,6 +161,7 @@ export function createPluginSetupContext<TPlugin, TPluginDependencies>(
|
|||
},
|
||||
executionContext: {
|
||||
withContext: deps.executionContext.withContext,
|
||||
getAsLabels: deps.executionContext.getAsLabels,
|
||||
},
|
||||
http: {
|
||||
createCookieSessionStorageFactory: deps.http.createCookieSessionStorageFactory,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
/// <reference types="node" />
|
||||
|
||||
import { AddConfigDeprecation } from '@kbn/config';
|
||||
import apm from 'elastic-apm-node';
|
||||
import Boom from '@hapi/boom';
|
||||
import { ByteSizeValue } from '@kbn/config-schema';
|
||||
import { CliArgs } from '@kbn/config';
|
||||
|
@ -1001,6 +1002,8 @@ export class EventLoopDelaysMonitor {
|
|||
|
||||
// @public (undocumented)
|
||||
export interface ExecutionContextSetup {
|
||||
// (undocumented)
|
||||
getAsLabels(): apm.Labels;
|
||||
withContext<R>(context: KibanaExecutionContext | undefined, fn: (...args: any[]) => R): R;
|
||||
}
|
||||
|
||||
|
@ -1326,9 +1329,10 @@ export interface IUiSettingsClient {
|
|||
|
||||
// @public
|
||||
export 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;
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import { HttpService } from '../public/http';
|
||||
import { fatalErrorsServiceMock } from '../public/fatal_errors/fatal_errors_service.mock';
|
||||
import { injectedMetadataServiceMock } from '../public/injected_metadata/injected_metadata_service.mock';
|
||||
import { executionContextServiceMock } from '../public/execution_context/execution_context_service.mock';
|
||||
|
||||
export type SetupTap = (
|
||||
injectedMetadata: ReturnType<typeof injectedMetadataServiceMock.createSetupContract>,
|
||||
|
@ -28,7 +29,8 @@ export function setup(tap: SetupTap = defaultTap) {
|
|||
tap(injectedMetadata, fatalErrors);
|
||||
|
||||
const httpService = new HttpService();
|
||||
const http = httpService.setup({ fatalErrors, injectedMetadata });
|
||||
const executionContext = executionContextServiceMock.createSetupContract();
|
||||
const http = httpService.setup({ fatalErrors, injectedMetadata, executionContext });
|
||||
|
||||
return { httpService, injectedMetadata, fatalErrors, http };
|
||||
}
|
||||
|
|
|
@ -16,11 +16,13 @@ export type KibanaExecutionContext = {
|
|||
/**
|
||||
* Kibana application initated an operation.
|
||||
* */
|
||||
readonly type: string; // 'visualization' | 'actions' | 'server' | ..;
|
||||
/** public name of a user-facing feature */
|
||||
readonly name: string; // 'TSVB' | 'Lens' | 'action_execution' | ..;
|
||||
readonly type?: string; // 'visualization' | 'actions' | 'server' | ..;
|
||||
/** public name of an application or a user-facing feature */
|
||||
readonly name?: string; // 'TSVB' | 'Lens' | 'action_execution' | ..;
|
||||
/** a stand alone, logical unit such as an application page or tab */
|
||||
readonly page?: string;
|
||||
/** unique value to identify the source */
|
||||
readonly id: string;
|
||||
readonly id?: string;
|
||||
/** human readable description. For example, a vis title, action name */
|
||||
readonly description?: string;
|
||||
/** in browser - url to navigate to a current page, on server - endpoint path, for task: task SO url */
|
||||
|
|
|
@ -11,7 +11,7 @@ import React, { useEffect, useMemo } from 'react';
|
|||
|
||||
import { useDashboardSelector } from './state';
|
||||
import { useDashboardAppState } from './hooks';
|
||||
import { useKibana } from '../../../kibana_react/public';
|
||||
import { useKibana, useExecutionContext } from '../../../kibana_react/public';
|
||||
import {
|
||||
getDashboardBreadcrumb,
|
||||
getDashboardTitle,
|
||||
|
@ -48,6 +48,12 @@ export function DashboardApp({
|
|||
[core.notifications.toasts, history, uiSettings]
|
||||
);
|
||||
|
||||
useExecutionContext(core.executionContext, {
|
||||
type: 'application',
|
||||
page: 'app',
|
||||
id: savedDashboardId || 'new',
|
||||
});
|
||||
|
||||
const dashboardState = useDashboardSelector((state) => state.dashboardStateReducer);
|
||||
const dashboardAppState = useDashboardAppState({
|
||||
history,
|
||||
|
|
|
@ -220,11 +220,7 @@ export const useDashboardAppState = ({
|
|||
savedDashboard,
|
||||
data,
|
||||
executionContext: {
|
||||
type: 'application',
|
||||
name: 'dashboard',
|
||||
id: savedDashboard.id ?? 'unsaved_dashboard',
|
||||
description: savedDashboard.title,
|
||||
url: history.location.pathname,
|
||||
},
|
||||
});
|
||||
if (canceled || !dashboardContainer) {
|
||||
|
|
|
@ -35,6 +35,7 @@ import { DashboardUnsavedListing } from './dashboard_unsaved_listing';
|
|||
import { confirmCreateWithUnsaved, confirmDiscardUnsavedChanges } from './confirm_overlays';
|
||||
import { getDashboardListItemLink } from './get_dashboard_list_item_link';
|
||||
import { DASHBOARD_PANELS_UNSAVED_ID } from '../lib/dashboard_session_storage';
|
||||
import { useExecutionContext } from '../../../../kibana_react/public';
|
||||
|
||||
export interface DashboardListingProps {
|
||||
kbnUrlStateStorage: IKbnUrlStateStorage;
|
||||
|
@ -67,6 +68,11 @@ export const DashboardListing = ({
|
|||
dashboardSessionStorage.getDashboardIdsWithUnsavedChanges()
|
||||
);
|
||||
|
||||
useExecutionContext(core.executionContext, {
|
||||
type: 'application',
|
||||
page: 'list',
|
||||
});
|
||||
|
||||
// Set breadcrumbs useEffect
|
||||
useEffect(() => {
|
||||
setBreadcrumbs([
|
||||
|
|
|
@ -119,6 +119,7 @@ describe('SearchInterceptor', () => {
|
|||
}),
|
||||
uiSettings: mockCoreSetup.uiSettings,
|
||||
http: mockCoreSetup.http,
|
||||
executionContext: mockCoreSetup.executionContext,
|
||||
session: sessionService,
|
||||
theme: themeServiceMock.createSetupContract(),
|
||||
});
|
||||
|
@ -543,7 +544,12 @@ describe('SearchInterceptor', () => {
|
|||
.catch(() => {});
|
||||
expect(fetchMock.mock.calls[0][0]).toEqual(
|
||||
expect.objectContaining({
|
||||
options: { sessionId, isStored: true, isRestore: true, strategy: 'ese' },
|
||||
options: {
|
||||
sessionId,
|
||||
isStored: true,
|
||||
isRestore: true,
|
||||
strategy: 'ese',
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ import { SearchAbortController } from './search_abort_controller';
|
|||
export interface SearchInterceptorDeps {
|
||||
bfetch: BfetchPublicSetup;
|
||||
http: CoreSetup['http'];
|
||||
executionContext: CoreSetup['executionContext'];
|
||||
uiSettings: CoreSetup['uiSettings'];
|
||||
startServices: Promise<[CoreStart, any, unknown]>;
|
||||
toasts: ToastsSetup;
|
||||
|
@ -297,10 +298,14 @@ export class SearchInterceptor {
|
|||
}
|
||||
}) as Promise<IKibanaSearchResponse>;
|
||||
} else {
|
||||
const { executionContext, ...rest } = options || {};
|
||||
return this.batchedFetch(
|
||||
{
|
||||
request,
|
||||
options: this.getSerializableOptions(options),
|
||||
options: this.getSerializableOptions({
|
||||
...rest,
|
||||
executionContext: this.deps.executionContext.withGlobalContext(executionContext),
|
||||
}),
|
||||
},
|
||||
abortSignal
|
||||
);
|
||||
|
|
|
@ -89,7 +89,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
constructor(private initializerContext: PluginInitializerContext<ConfigSchema>) {}
|
||||
|
||||
public setup(
|
||||
{ http, getStartServices, notifications, uiSettings, theme }: CoreSetup,
|
||||
{ http, getStartServices, notifications, uiSettings, executionContext, theme }: CoreSetup,
|
||||
{ bfetch, expressions, usageCollection, nowProvider }: SearchServiceSetupDependencies
|
||||
): ISearchSetup {
|
||||
this.usageCollector = createUsageCollector(getStartServices, usageCollection);
|
||||
|
@ -108,6 +108,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
|
|||
this.searchInterceptor = new SearchInterceptor({
|
||||
bfetch,
|
||||
toasts: notifications.toasts,
|
||||
executionContext,
|
||||
http,
|
||||
uiSettings,
|
||||
startServices: getStartServices(),
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
import { catchError, first } from 'rxjs/operators';
|
||||
import { BfetchServerSetup } from 'src/plugins/bfetch/server';
|
||||
import type { ExecutionContextSetup } from 'src/core/server';
|
||||
import apm from 'elastic-apm-node';
|
||||
import {
|
||||
IKibanaSearchRequest,
|
||||
IKibanaSearchResponse,
|
||||
|
@ -33,9 +34,10 @@ export function registerBsearchRoute(
|
|||
*/
|
||||
onBatchItem: async ({ request: requestData, options }) => {
|
||||
const { executionContext, ...restOptions } = options || {};
|
||||
return executionContextService.withContext(executionContext, () => {
|
||||
apm.addLabels(executionContextService.getAsLabels());
|
||||
|
||||
return executionContextService.withContext(executionContext, () =>
|
||||
search
|
||||
return search
|
||||
.search(requestData, restOptions)
|
||||
.pipe(
|
||||
first(),
|
||||
|
@ -49,8 +51,8 @@ export function registerBsearchRoute(
|
|||
};
|
||||
})
|
||||
)
|
||||
.toPromise()
|
||||
);
|
||||
.toPromise();
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
||||
|
|
|
@ -15,8 +15,14 @@ import { I18nProvider } from '@kbn/i18n-react';
|
|||
import { i18n } from '@kbn/i18n';
|
||||
import { euiThemeVars } from '@kbn/ui-theme';
|
||||
|
||||
import { ApplicationStart, ChromeStart, ScopedHistory, CoreTheme } from 'src/core/public';
|
||||
import { KibanaThemeProvider } from '../../kibana_react/public';
|
||||
import type {
|
||||
ApplicationStart,
|
||||
ChromeStart,
|
||||
ScopedHistory,
|
||||
CoreTheme,
|
||||
ExecutionContextStart,
|
||||
} from 'src/core/public';
|
||||
import { KibanaThemeProvider, useExecutionContext } from '../../kibana_react/public';
|
||||
import type { DocTitleService, BreadcrumbService } from './services';
|
||||
|
||||
import { DevToolApp } from './dev_tool';
|
||||
|
@ -24,6 +30,7 @@ import { DevToolApp } from './dev_tool';
|
|||
export interface AppServices {
|
||||
docTitleService: DocTitleService;
|
||||
breadcrumbService: BreadcrumbService;
|
||||
executionContext: ExecutionContextStart;
|
||||
}
|
||||
|
||||
interface DevToolsWrapperProps {
|
||||
|
@ -64,6 +71,11 @@ function DevToolsWrapper({
|
|||
breadcrumbService.setBreadcrumbs(activeDevTool.title);
|
||||
}, [activeDevTool, docTitleService, breadcrumbService]);
|
||||
|
||||
useExecutionContext(appServices.executionContext, {
|
||||
type: 'application',
|
||||
page: activeDevTool.id,
|
||||
});
|
||||
|
||||
return (
|
||||
<main className="devApp">
|
||||
<EuiTabs style={{ paddingLeft: euiThemeVars.euiSizeS }} size="l">
|
||||
|
|
|
@ -61,7 +61,7 @@ export class DevToolsPlugin implements Plugin<DevToolsSetup, void> {
|
|||
element.classList.add('devAppWrapper');
|
||||
|
||||
const [core] = await getStartServices();
|
||||
const { application, chrome } = core;
|
||||
const { application, chrome, executionContext } = core;
|
||||
|
||||
this.docTitleService.setup(chrome.docTitle.change);
|
||||
this.breadcrumbService.setup(chrome.setBreadcrumbs);
|
||||
|
@ -69,6 +69,7 @@ export class DevToolsPlugin implements Plugin<DevToolsSetup, void> {
|
|||
const appServices = {
|
||||
breadcrumbService: this.breadcrumbService,
|
||||
docTitleService: this.docTitleService,
|
||||
executionContext,
|
||||
};
|
||||
|
||||
const { renderApp } = await import('./application');
|
||||
|
|
|
@ -46,6 +46,9 @@ describe('ContextApp test', () => {
|
|||
toastNotifications: { addDanger: () => {} },
|
||||
navigation: mockNavigationPlugin,
|
||||
core: {
|
||||
executionContext: {
|
||||
set: jest.fn(),
|
||||
},
|
||||
notifications: { toasts: [] },
|
||||
theme: { theme$: themeServiceMock.createStartContract().theme$ },
|
||||
},
|
||||
|
|
|
@ -26,6 +26,7 @@ import { ContextAppContent } from './context_app_content';
|
|||
import { SurrDocType } from './services/context';
|
||||
import { DocViewFilterFn } from '../../services/doc_views/doc_views_types';
|
||||
import { useDiscoverServices } from '../../utils/use_discover_services';
|
||||
import { useExecutionContext } from '../../../../kibana_react/public';
|
||||
|
||||
const ContextAppContentMemoized = memo(ContextAppContent);
|
||||
|
||||
|
@ -36,11 +37,17 @@ export interface ContextAppProps {
|
|||
|
||||
export const ContextApp = ({ indexPattern, anchorId }: ContextAppProps) => {
|
||||
const services = useDiscoverServices();
|
||||
const { uiSettings, capabilities, indexPatterns, navigation, filterManager } = services;
|
||||
const { uiSettings, capabilities, indexPatterns, navigation, filterManager, core } = services;
|
||||
|
||||
const isLegacy = useMemo(() => uiSettings.get(DOC_TABLE_LEGACY), [uiSettings]);
|
||||
const useNewFieldsApi = useMemo(() => !uiSettings.get(SEARCH_FIELDS_FROM_SOURCE), [uiSettings]);
|
||||
|
||||
useExecutionContext(core.executionContext, {
|
||||
type: 'application',
|
||||
page: 'context',
|
||||
id: indexPattern.id || '',
|
||||
});
|
||||
|
||||
/**
|
||||
* Context app state
|
||||
*/
|
||||
|
|
|
@ -16,6 +16,7 @@ import { withQueryParams } from '../../utils/with_query_params';
|
|||
import { useMainRouteBreadcrumb } from '../../utils/use_navigation_props';
|
||||
import { Doc } from './components/doc';
|
||||
import { useDiscoverServices } from '../../utils/use_discover_services';
|
||||
import { useExecutionContext } from '../../../../kibana_react/public';
|
||||
|
||||
export interface SingleDocRouteProps {
|
||||
/**
|
||||
|
@ -31,11 +32,17 @@ export interface DocUrlParams {
|
|||
|
||||
const SingleDoc = ({ id }: SingleDocRouteProps) => {
|
||||
const services = useDiscoverServices();
|
||||
const { chrome, timefilter } = services;
|
||||
const { chrome, timefilter, core } = services;
|
||||
|
||||
const { indexPatternId, index } = useParams<DocUrlParams>();
|
||||
const breadcrumb = useMainRouteBreadcrumb();
|
||||
|
||||
useExecutionContext(core.executionContext, {
|
||||
type: 'application',
|
||||
page: 'single-doc',
|
||||
id: indexPatternId,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
chrome.setBreadcrumbs([
|
||||
...getRootBreadcrumbs(breadcrumb),
|
||||
|
|
|
@ -24,6 +24,7 @@ import { LoadingIndicator } from '../../components/common/loading_indicator';
|
|||
import { DiscoverError } from '../../components/common/error_alert';
|
||||
import { useDiscoverServices } from '../../utils/use_discover_services';
|
||||
import { getUrlTracker } from '../../kibana_services';
|
||||
import { useExecutionContext } from '../../../../kibana_react/public';
|
||||
|
||||
const DiscoverMainAppMemoized = memo(DiscoverMainApp);
|
||||
|
||||
|
@ -50,6 +51,12 @@ export function DiscoverMainRoute() {
|
|||
>([]);
|
||||
const { id } = useParams<DiscoverLandingParams>();
|
||||
|
||||
useExecutionContext(core.executionContext, {
|
||||
type: 'application',
|
||||
page: 'app',
|
||||
id: id || 'new',
|
||||
});
|
||||
|
||||
const navigateToOverview = useCallback(() => {
|
||||
core.application.navigateToApp('kibanaOverview', { path: '#' });
|
||||
}, [core.application]);
|
||||
|
|
|
@ -117,7 +117,7 @@ describe('test fetchCharts', () => {
|
|||
});
|
||||
});
|
||||
|
||||
test('fetch$ is called with execution context containing savedSearch id', async () => {
|
||||
test('fetch$ is called with request specific execution context', async () => {
|
||||
const fetch$Mock = jest.fn().mockReturnValue(of(requestResult));
|
||||
|
||||
savedSearchMockWithTimeField.searchSource.fetch$ = fetch$Mock;
|
||||
|
@ -126,10 +126,6 @@ describe('test fetchCharts', () => {
|
|||
expect(fetch$Mock.mock.calls[0][0].executionContext).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"description": "fetch chart data and total hits",
|
||||
"id": "the-saved-search-id-with-timefield",
|
||||
"name": "discover",
|
||||
"type": "application",
|
||||
"url": "/",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
|
|
@ -40,11 +40,7 @@ export function fetchChart(
|
|||
const chartAggConfigs = updateSearchSource(searchSource, interval, data);
|
||||
|
||||
const executionContext = {
|
||||
type: 'application',
|
||||
name: 'discover',
|
||||
description: 'fetch chart data and total hits',
|
||||
url: window.location.pathname,
|
||||
id: savedSearch.id ?? '',
|
||||
};
|
||||
|
||||
const fetch$ = searchSource
|
||||
|
|
|
@ -57,10 +57,6 @@ describe('test fetchDocuments', () => {
|
|||
expect(fetch$Mock.mock.calls[0][0].executionContext).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"description": "fetch total hits",
|
||||
"id": "the-saved-search-id",
|
||||
"name": "discover",
|
||||
"type": "application",
|
||||
"url": "/",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
|
|
@ -32,11 +32,7 @@ export const fetchDocuments = (
|
|||
}
|
||||
|
||||
const executionContext = {
|
||||
type: 'application',
|
||||
name: 'discover',
|
||||
description: 'fetch documents',
|
||||
url: window.location.pathname,
|
||||
id: savedSearch.id ?? '',
|
||||
};
|
||||
|
||||
const fetch$ = searchSource
|
||||
|
|
|
@ -51,10 +51,6 @@ describe('test fetchTotalHits', () => {
|
|||
expect(fetch$Mock.mock.calls[0][0].executionContext).toMatchInlineSnapshot(`
|
||||
Object {
|
||||
"description": "fetch total hits",
|
||||
"id": "the-saved-search-id",
|
||||
"name": "discover",
|
||||
"type": "application",
|
||||
"url": "/",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
|
|
@ -30,11 +30,7 @@ export function fetchTotalHits(
|
|||
}
|
||||
|
||||
const executionContext = {
|
||||
type: 'application',
|
||||
name: 'discover',
|
||||
description: 'fetch total hits',
|
||||
url: window.location.pathname,
|
||||
id: savedSearch.id ?? '',
|
||||
};
|
||||
|
||||
const fetch$ = searchSource
|
||||
|
|
|
@ -39,6 +39,8 @@ export { createReactOverlays } from './overlays';
|
|||
|
||||
export { useUiSetting, useUiSetting$ } from './ui_settings';
|
||||
|
||||
export { useExecutionContext } from './use_execution_context';
|
||||
|
||||
export type { TableListViewProps, TableListViewState } from './table_list_view';
|
||||
export { TableListView } from './table_list_view';
|
||||
|
||||
|
|
|
@ -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 { useExecutionContext } from './use_execution_context';
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 type { KibanaExecutionContext, CoreStart } from 'kibana/public';
|
||||
import useDeepCompareEffect from 'react-use/lib/useDeepCompareEffect';
|
||||
|
||||
/**
|
||||
* Set and clean up application level execution context
|
||||
* @param executionContext
|
||||
* @param context
|
||||
*/
|
||||
export function useExecutionContext(
|
||||
executionContext: CoreStart['executionContext'],
|
||||
context: KibanaExecutionContext
|
||||
) {
|
||||
useDeepCompareEffect(() => {
|
||||
executionContext.set(context);
|
||||
|
||||
return () => {
|
||||
executionContext.clear();
|
||||
};
|
||||
}, [context]);
|
||||
}
|
|
@ -39,7 +39,7 @@ import {
|
|||
ExpressionAstExpression,
|
||||
} from '../../../../plugins/expressions/public';
|
||||
import { Vis, SerializedVis } from '../vis';
|
||||
import { getExpressions, getTheme, getUiActions } from '../services';
|
||||
import { getExecutionContext, getExpressions, getTheme, getUiActions } from '../services';
|
||||
import { VIS_EVENT_TO_TRIGGER } from './events';
|
||||
import { VisualizeEmbeddableFactoryDeps } from './visualize_embeddable_factory';
|
||||
import { getSavedVisualization } from '../utils/saved_visualize_utils';
|
||||
|
@ -391,20 +391,18 @@ export class VisualizeEmbeddable
|
|||
};
|
||||
|
||||
private async updateHandler() {
|
||||
const parentContext = this.parent?.getInput().executionContext;
|
||||
const parentContext = this.parent?.getInput().executionContext || getExecutionContext().get();
|
||||
const child: KibanaExecutionContext = {
|
||||
type: 'visualization',
|
||||
name: this.vis.type.name,
|
||||
id: this.vis.id ?? 'an_unsaved_vis',
|
||||
id: this.vis.id ?? 'new',
|
||||
description: this.vis.title || this.input.title || this.vis.type.name,
|
||||
url: this.output.editUrl,
|
||||
};
|
||||
const context = parentContext
|
||||
? {
|
||||
...parentContext,
|
||||
child,
|
||||
}
|
||||
: child;
|
||||
const context = {
|
||||
...parentContext,
|
||||
child,
|
||||
};
|
||||
|
||||
const expressionParams: IExpressionLoaderParams = {
|
||||
searchContext: {
|
||||
|
|
|
@ -36,6 +36,7 @@ import {
|
|||
setDocLinks,
|
||||
setSpaces,
|
||||
setTheme,
|
||||
setExecutionContext,
|
||||
} from './services';
|
||||
import {
|
||||
createVisEmbeddableFromObject,
|
||||
|
@ -361,6 +362,7 @@ export class VisualizationsPlugin
|
|||
setTimeFilter(data.query.timefilter.timefilter);
|
||||
setAggs(data.search.aggs);
|
||||
setOverlays(core.overlays);
|
||||
setExecutionContext(core.executionContext);
|
||||
setChrome(core.chrome);
|
||||
|
||||
if (spaces) {
|
||||
|
|
|
@ -16,6 +16,7 @@ import type {
|
|||
SavedObjectsStart,
|
||||
DocLinksStart,
|
||||
ThemeServiceStart,
|
||||
ExecutionContextSetup,
|
||||
} from '../../../core/public';
|
||||
import type { TypesStart } from './vis_types';
|
||||
import { createGetterSetter } from '../../../plugins/kibana_utils/public';
|
||||
|
@ -65,4 +66,7 @@ export const [getOverlays, setOverlays] = createGetterSetter<OverlayStart>('Over
|
|||
|
||||
export const [getChrome, setChrome] = createGetterSetter<ChromeStart>('Chrome');
|
||||
|
||||
export const [getExecutionContext, setExecutionContext] =
|
||||
createGetterSetter<ExecutionContextSetup>('ExecutionContext');
|
||||
|
||||
export const [getSpaces, setSpaces] = createGetterSetter<SpacesPluginStart>('Spaces', false);
|
||||
|
|
|
@ -11,7 +11,7 @@ import React, { useEffect, useState } from 'react';
|
|||
import { useParams } from 'react-router-dom';
|
||||
import { EventEmitter } from 'events';
|
||||
|
||||
import { useKibana } from '../../../../kibana_react/public';
|
||||
import { useExecutionContext, useKibana } from '../../../../kibana_react/public';
|
||||
import {
|
||||
useChromeVisibility,
|
||||
useSavedVisInstance,
|
||||
|
@ -41,6 +41,14 @@ export const VisualizeEditor = ({ onAppLeave }: VisualizeAppProps) => {
|
|||
originatingApp,
|
||||
visualizationIdFromUrl
|
||||
);
|
||||
|
||||
const editorName = savedVisInstance?.vis.type.title.toLowerCase().replace(' ', '_') || '';
|
||||
useExecutionContext(services.executionContext, {
|
||||
type: 'application',
|
||||
page: `editor${editorName ? `:${editorName}` : ''}`,
|
||||
id: visualizationIdFromUrl || 'new',
|
||||
});
|
||||
|
||||
const { appState, hasUnappliedChanges } = useVisualizeAppState(
|
||||
services,
|
||||
eventEmitter,
|
||||
|
|
|
@ -21,7 +21,7 @@ import { findListItems } from '../../utils/saved_visualize_utils';
|
|||
import { showNewVisModal } from '../../wizard';
|
||||
import { getTypes } from '../../services';
|
||||
import { SavedObjectsFindOptionsReference } from '../../../../../core/public';
|
||||
import { useKibana, TableListView } from '../../../../kibana_react/public';
|
||||
import { useKibana, TableListView, useExecutionContext } from '../../../../kibana_react/public';
|
||||
import { VISUALIZE_ENABLE_LABS_SETTING } from '../../../../visualizations/public';
|
||||
import { VisualizeServices } from '../types';
|
||||
import { VisualizeConstants } from '../../../common/constants';
|
||||
|
@ -31,6 +31,7 @@ export const VisualizeListing = () => {
|
|||
const {
|
||||
services: {
|
||||
application,
|
||||
executionContext,
|
||||
chrome,
|
||||
history,
|
||||
toastNotifications,
|
||||
|
@ -49,6 +50,11 @@ export const VisualizeListing = () => {
|
|||
const closeNewVisModal = useRef(() => {});
|
||||
const listingLimit = savedObjectsPublic.settings.getListingLimit();
|
||||
|
||||
useExecutionContext(executionContext, {
|
||||
type: 'application',
|
||||
page: 'list',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (pathname === '/new') {
|
||||
// In case the user navigated to the page via the /visualize/new URL we start the dialog immediately
|
||||
|
|
27
x-pack/plugins/fleet/.storybook/context/execution_context.ts
Normal file
27
x-pack/plugins/fleet/.storybook/context/execution_context.ts
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import type { ExecutionContextSetup } from 'kibana/public';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
export const getExecutionContext = () => {
|
||||
const exec: ExecutionContextSetup = {
|
||||
context$: of({}),
|
||||
get: () => {
|
||||
return {};
|
||||
},
|
||||
clear: () => {},
|
||||
set: (context: Record<string, any>) => {},
|
||||
getAsLabels: () => {
|
||||
return {};
|
||||
},
|
||||
withGlobalContext: () => {
|
||||
return {};
|
||||
},
|
||||
};
|
||||
|
||||
return exec;
|
||||
};
|
|
@ -31,6 +31,7 @@ import { stubbedStartServices } from './stubs';
|
|||
import { getDocLinks } from './doc_links';
|
||||
import { getCloud } from './cloud';
|
||||
import { getShare } from './share';
|
||||
import { getExecutionContext } from './execution_context';
|
||||
|
||||
// TODO: clintandrewhall - this is not ideal, or complete. The root context of Fleet applications
|
||||
// requires full start contracts of its dependencies. As a result, we have to mock all of those contracts
|
||||
|
@ -52,6 +53,7 @@ export const StorybookContext: React.FC<{ storyContext?: StoryContext }> = ({
|
|||
() => ({
|
||||
...stubbedStartServices,
|
||||
application: getApplication(),
|
||||
executionContext: getExecutionContext(),
|
||||
chrome: getChrome(),
|
||||
cloud: {
|
||||
...getCloud({ isCloudEnabled }),
|
||||
|
|
|
@ -25,11 +25,13 @@ import { init as initHttp } from '../public/application/services/http';
|
|||
import { init as initUiMetric } from '../public/application/services/ui_metric';
|
||||
import { KibanaContextProvider } from '../public/shared_imports';
|
||||
import { PolicyListContextProvider } from '../public/application/sections/policy_list/policy_list_context';
|
||||
import { executionContextServiceMock } from 'src/core/public/execution_context/execution_context_service.mock';
|
||||
|
||||
initHttp(
|
||||
new HttpService().setup({
|
||||
injectedMetadata: injectedMetadataServiceMock.createSetupContract(),
|
||||
fatalErrors: fatalErrorsServiceMock.createSetupContract(),
|
||||
executionContext: executionContextServiceMock.createSetupContract(),
|
||||
})
|
||||
);
|
||||
initUiMetric(usageCollectionPluginMock.createSetupContract());
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
createKbnUrlStateStorage,
|
||||
withNotifyOnErrors,
|
||||
} from '../../../../../src/plugins/kibana_utils/public';
|
||||
import { useKibana } from '../../../../../src/plugins/kibana_react/public';
|
||||
import { useExecutionContext, useKibana } from '../../../../../src/plugins/kibana_react/public';
|
||||
import { OnSaveProps } from '../../../../../src/plugins/saved_objects/public';
|
||||
import { syncQueryStateWithUrl } from '../../../../../src/plugins/data/public';
|
||||
import { LensAppProps, LensAppServices } from './types';
|
||||
|
@ -69,6 +69,7 @@ export function App({
|
|||
getOriginatingAppName,
|
||||
spaces,
|
||||
http,
|
||||
executionContext,
|
||||
// Temporarily required until the 'by value' paradigm is default.
|
||||
dashboardFeatureFlag,
|
||||
} = lensAppServices;
|
||||
|
@ -105,6 +106,7 @@ export function App({
|
|||
const [indicateNoData, setIndicateNoData] = useState(false);
|
||||
const [isSaveModalVisible, setIsSaveModalVisible] = useState(false);
|
||||
const [lastKnownDoc, setLastKnownDoc] = useState<Document | undefined>(undefined);
|
||||
const savedObjectId = (initialInput as LensByReferenceInput)?.savedObjectId;
|
||||
|
||||
useEffect(() => {
|
||||
if (currentDoc) {
|
||||
|
@ -116,6 +118,12 @@ export function App({
|
|||
setIndicateNoData(true);
|
||||
}, [setIndicateNoData]);
|
||||
|
||||
useExecutionContext(executionContext, {
|
||||
type: 'application',
|
||||
id: savedObjectId || 'new',
|
||||
page: 'editor',
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (indicateNoData) {
|
||||
setIndicateNoData(false);
|
||||
|
@ -126,11 +134,9 @@ export function App({
|
|||
() =>
|
||||
Boolean(
|
||||
// Temporarily required until the 'by value' paradigm is default.
|
||||
dashboardFeatureFlag.allowByValueEmbeddables &&
|
||||
isLinkedToOriginatingApp &&
|
||||
!(initialInput as LensByReferenceInput)?.savedObjectId
|
||||
dashboardFeatureFlag.allowByValueEmbeddables && isLinkedToOriginatingApp && !savedObjectId
|
||||
),
|
||||
[dashboardFeatureFlag.allowByValueEmbeddables, isLinkedToOriginatingApp, initialInput]
|
||||
[dashboardFeatureFlag.allowByValueEmbeddables, isLinkedToOriginatingApp, savedObjectId]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
@ -76,6 +76,7 @@ export async function getLensServices(
|
|||
usageCollection,
|
||||
savedObjectsTagging,
|
||||
attributeService,
|
||||
executionContext: coreStart.executionContext,
|
||||
http: coreStart.http,
|
||||
chrome: coreStart.chrome,
|
||||
overlays: coreStart.overlays,
|
||||
|
|
|
@ -12,6 +12,7 @@ import type {
|
|||
ApplicationStart,
|
||||
AppMountParameters,
|
||||
ChromeStart,
|
||||
ExecutionContextStart,
|
||||
HttpStart,
|
||||
IUiSettingsClient,
|
||||
NotificationsStart,
|
||||
|
@ -98,6 +99,7 @@ export interface HistoryLocationState {
|
|||
|
||||
export interface LensAppServices {
|
||||
http: HttpStart;
|
||||
executionContext: ExecutionContextStart;
|
||||
chrome: ChromeStart;
|
||||
overlays: OverlayStart;
|
||||
storage: IStorageWrapper;
|
||||
|
|
|
@ -112,6 +112,7 @@ export function makeDefaultServices(
|
|||
chrome: core.chrome,
|
||||
overlays: core.overlays,
|
||||
uiSettings: core.uiSettings,
|
||||
executionContext: core.executionContext,
|
||||
navigation: navigationStartMock,
|
||||
notifications: core.notifications,
|
||||
attributeService: makeAttributeService(),
|
||||
|
|
36
x-pack/plugins/maps/common/execution_context.test.ts
Normal file
36
x-pack/plugins/maps/common/execution_context.test.ts
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 { makeExecutionContext } from './execution_context';
|
||||
|
||||
describe('makeExecutionContext', () => {
|
||||
test('returns basic fields if nothing is provided', () => {
|
||||
const context = makeExecutionContext({});
|
||||
expect(context).toStrictEqual({
|
||||
name: 'maps',
|
||||
type: 'application',
|
||||
});
|
||||
});
|
||||
|
||||
test('merges in context', () => {
|
||||
const context = makeExecutionContext({ id: '123' });
|
||||
expect(context).toStrictEqual({
|
||||
name: 'maps',
|
||||
type: 'application',
|
||||
id: '123',
|
||||
});
|
||||
});
|
||||
|
||||
test('omits undefined values', () => {
|
||||
const context = makeExecutionContext({ id: '123', description: undefined });
|
||||
expect(context).toStrictEqual({
|
||||
name: 'maps',
|
||||
type: 'application',
|
||||
id: '123',
|
||||
});
|
||||
});
|
||||
});
|
|
@ -5,14 +5,16 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { isUndefined, omitBy } from 'lodash';
|
||||
import { APP_ID } from './constants';
|
||||
|
||||
export function makeExecutionContext(id: string, url: string, description?: string) {
|
||||
return {
|
||||
name: APP_ID,
|
||||
type: 'application',
|
||||
id,
|
||||
description: description || '',
|
||||
url,
|
||||
};
|
||||
export function makeExecutionContext(context: { id?: string; url?: string; description?: string }) {
|
||||
return omitBy(
|
||||
{
|
||||
name: APP_ID,
|
||||
type: 'application',
|
||||
...context,
|
||||
},
|
||||
isUndefined
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,8 +5,14 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { coreMock } from '../../../../../../../src/core/public/mocks';
|
||||
import { MapExtent, VectorSourceRequestMeta } from '../../../../common/descriptor_types';
|
||||
import { getHttp, getIndexPatternService, getSearchService } from '../../../kibana_services';
|
||||
import {
|
||||
getExecutionContext,
|
||||
getHttp,
|
||||
getIndexPatternService,
|
||||
getSearchService,
|
||||
} from '../../../kibana_services';
|
||||
import { ESGeoGridSource } from './es_geo_grid_source';
|
||||
import {
|
||||
ES_GEO_FIELD_TYPE,
|
||||
|
@ -129,6 +135,13 @@ describe('ESGeoGridSource', () => {
|
|||
},
|
||||
},
|
||||
});
|
||||
|
||||
const coreStartMock = coreMock.createStart();
|
||||
coreStartMock.executionContext.get.mockReturnValue({
|
||||
name: 'some-app',
|
||||
});
|
||||
// @ts-expect-error
|
||||
getExecutionContext.mockReturnValue(coreStartMock.executionContext);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
|
|
@ -33,6 +33,7 @@ export const getUiSettings = () => coreStart.uiSettings;
|
|||
export const getIsDarkMode = () => getUiSettings().get('theme:darkMode', false);
|
||||
export const getIndexPatternSelectComponent = () => pluginsStart.data.ui.IndexPatternSelect;
|
||||
export const getHttp = () => coreStart.http;
|
||||
export const getExecutionContext = () => coreStart.executionContext;
|
||||
export const getTimeFilter = () => pluginsStart.data.query.timefilter.timefilter;
|
||||
export const getToasts = () => coreStart.notifications.toasts;
|
||||
export const getSavedObjectsClient = () => coreStart.savedObjects.client;
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
getMapsCapabilities,
|
||||
getToasts,
|
||||
getCoreChrome,
|
||||
getExecutionContext,
|
||||
getNavigateToApp,
|
||||
getSavedObjectsClient,
|
||||
getSavedObjectsTagging,
|
||||
|
@ -121,6 +122,12 @@ async function deleteMaps(items: object[]) {
|
|||
}
|
||||
|
||||
export function MapsListView() {
|
||||
getExecutionContext().set({
|
||||
type: 'application',
|
||||
page: 'list',
|
||||
id: '',
|
||||
});
|
||||
|
||||
const isReadOnly = !getMapsCapabilities().save;
|
||||
|
||||
getCoreChrome().docTitle.change(getAppTitle());
|
||||
|
|
|
@ -16,6 +16,7 @@ import { type Filter, FilterStateStore } from '@kbn/es-query';
|
|||
import type { Query, TimeRange, IndexPattern } from 'src/plugins/data/common';
|
||||
import {
|
||||
getData,
|
||||
getExecutionContext,
|
||||
getCoreChrome,
|
||||
getMapsCapabilities,
|
||||
getNavigation,
|
||||
|
@ -115,6 +116,12 @@ export class MapApp extends React.Component<Props, State> {
|
|||
componentDidMount() {
|
||||
this._isMounted = true;
|
||||
|
||||
getExecutionContext().set({
|
||||
type: 'application',
|
||||
page: 'editor',
|
||||
id: this.props.savedMap.getSavedObjectId() || 'new',
|
||||
});
|
||||
|
||||
this._autoRefreshSubscription = getTimeFilter()
|
||||
.getAutoRefreshFetch$()
|
||||
.pipe(
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { getGlyphUrl } from './util';
|
||||
import { getGlyphUrl, makePublicExecutionContext } from './util';
|
||||
|
||||
const MOCK_EMS_SETTINGS = {
|
||||
isEMSEnabled: () => true,
|
||||
|
@ -62,3 +62,55 @@ describe('getGlyphUrl', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('makePublicExecutionContext', () => {
|
||||
let injectedContext = {};
|
||||
beforeAll(() => {
|
||||
require('./kibana_services').getExecutionContext = () => ({
|
||||
get: () => injectedContext,
|
||||
});
|
||||
});
|
||||
|
||||
test('creates basic context when no top level context is provided', () => {
|
||||
const context = makePublicExecutionContext('test');
|
||||
expect(context).toStrictEqual({
|
||||
description: 'test',
|
||||
name: 'maps',
|
||||
type: 'application',
|
||||
url: '/',
|
||||
});
|
||||
});
|
||||
|
||||
test('merges with top level context if its from the same app', () => {
|
||||
injectedContext = {
|
||||
name: 'maps',
|
||||
id: '1234',
|
||||
};
|
||||
const context = makePublicExecutionContext('test');
|
||||
expect(context).toStrictEqual({
|
||||
description: 'test',
|
||||
name: 'maps',
|
||||
type: 'application',
|
||||
url: '/',
|
||||
id: '1234',
|
||||
});
|
||||
});
|
||||
|
||||
test('nests inside top level context if its from a different app', () => {
|
||||
injectedContext = {
|
||||
name: 'other-app',
|
||||
id: '1234',
|
||||
};
|
||||
const context = makePublicExecutionContext('test');
|
||||
expect(context).toStrictEqual({
|
||||
name: 'other-app',
|
||||
id: '1234',
|
||||
child: {
|
||||
description: 'test',
|
||||
type: 'application',
|
||||
name: 'maps',
|
||||
url: '/',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -8,7 +8,13 @@
|
|||
import { EMSClient, FileLayer, TMSService } from '@elastic/ems-client';
|
||||
import type { KibanaExecutionContext } from 'kibana/public';
|
||||
import { FONTS_API_PATH } from '../common/constants';
|
||||
import { getHttp, getTilemap, getEMSSettings, getMapsEmsStart } from './kibana_services';
|
||||
import {
|
||||
getHttp,
|
||||
getTilemap,
|
||||
getEMSSettings,
|
||||
getMapsEmsStart,
|
||||
getExecutionContext,
|
||||
} from './kibana_services';
|
||||
import { getLicenseId } from './licensed_features';
|
||||
import { makeExecutionContext } from '../common/execution_context';
|
||||
|
||||
|
@ -67,9 +73,21 @@ export function isRetina(): boolean {
|
|||
return window.devicePixelRatio === 2;
|
||||
}
|
||||
|
||||
export function makePublicExecutionContext(
|
||||
id: string,
|
||||
description?: string
|
||||
): KibanaExecutionContext {
|
||||
return makeExecutionContext(id, window.location.pathname, description);
|
||||
export function makePublicExecutionContext(description: string): KibanaExecutionContext {
|
||||
const topLevelContext = getExecutionContext().get();
|
||||
const context = makeExecutionContext({
|
||||
url: window.location.pathname,
|
||||
description,
|
||||
});
|
||||
|
||||
// Distinguish between running in maps app vs. embedded
|
||||
return topLevelContext.name !== undefined && topLevelContext.name !== context.name
|
||||
? {
|
||||
...topLevelContext,
|
||||
child: context,
|
||||
}
|
||||
: {
|
||||
...topLevelContext,
|
||||
...context,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -56,7 +56,10 @@ export async function getEsGridTile({
|
|||
};
|
||||
|
||||
const tile = await core.executionContext.withContext(
|
||||
makeExecutionContext('mvt:get_grid_tile', url),
|
||||
makeExecutionContext({
|
||||
description: 'mvt:get_grid_tile',
|
||||
url,
|
||||
}),
|
||||
async () => {
|
||||
return await context.core.elasticsearch.client.asCurrentUser.transport.request(
|
||||
{
|
||||
|
|
|
@ -57,7 +57,10 @@ export async function getEsTile({
|
|||
};
|
||||
|
||||
const tile = await core.executionContext.withContext(
|
||||
makeExecutionContext('mvt:get_tile', url),
|
||||
makeExecutionContext({
|
||||
description: 'mvt:get_tile',
|
||||
url,
|
||||
}),
|
||||
async () => {
|
||||
return await context.core.elasticsearch.client.asCurrentUser.transport.request(
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue