Change ContextContainer to lazily initialize providers (#129896)

* Change ContextContainer to lazily initialize providers

* Introduce CustomRequestHandlerContext, start adapting usages

* adapt IContextProvider's return type

* start fixing violations

* fixing violations - 2

* adapt home routes

* fix remaining core violation

* fix violations on core tests

* fixing more violations

* fixing more violations

* update generated doc...

* fix more violations

* adapt remaining RequestHandlerContext

* fix more violations

* fix non-async method

* more fixes

* fix another await in non async method

* add yet another missing async

* [CI] Auto-commit changed files from 'node scripts/eslint --no-cache --fix'

* add yet yet another missing async

* update fleet's endpoints

* fix telemetry endpoints

* fix event_log endpoints

* fix some security unit tests

* adapt canvas routes

* adapt alerting routes

* adapt more so_tagging routes

* fix data_enhanced routes

* fix license_management routes

* fix file_upload routes

* fix index_management routes

* fix lists routes

* fix snapshot_restore routes

* fix rule_registry routes

* fix ingest_pipelines routes

* fix remote_clusters routes

* fix index_lifecycle_management routes

* improve and fix the lazy implementation

* fix triggers_actions_ui endpoints

* start fixing unit tests

* fix cases routes

* fix transform routes

* fix upgrade_assistant routes

* fix uptime route wrapper

* fix uptime route wrapper bis

* update osquery routes

* update cross_cluster_replication routes

* fix some ML routes / wrappers

* adapt maps routes

* adapt rollup routes

* fix some canvas unit tests

* fix more canvas unit tests

* fix observability wrapper

* fix (?) infra type hell

* start fixing monitoring

* fix a few test plugins

* woups

* fix yet more violations

* fixing UA  tests

* fix logstash handlers

* fix fleet unit tests

* lint?

* one more batch

* update security_solution endpoints

* start fixing security_solution mocks

* start fixing security_solution tests

* fix more security_solution tests

* fix more security_solution tests

* just one more

* fix last (?) security_solution tests

* fix timelion javascript file

* fix more test plugins

* fix transforms context type

* fix ml context type

* fix context tests

* fix securitySolution withEndpointAuthz tests

* fix features unit tests

* fix actions unit tests

* fix imports

* fix duplicate import

* fix some merge problems

* fix new usage

* fix new test

* introduces context.resolve

* down the rabbit hole again

* start fixing test type failures

* more test type failures fixes

* move import comment back to correct place

* more test type failures fixes, bis

* use context.resolve for security solution rules routes

* fix new violations due to master merge

* remove comment

Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
Pierre Gayvallet 2022-04-22 13:15:58 +02:00 committed by GitHub
parent 706d9268ef
commit a02c00b8a3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
699 changed files with 4508 additions and 3170 deletions

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [CoreRequestHandlerContext](./kibana-plugin-core-server.corerequesthandlercontext.md) &gt; [deprecations](./kibana-plugin-core-server.corerequesthandlercontext.deprecations.md)
## CoreRequestHandlerContext.deprecations property
<b>Signature:</b>
```typescript
deprecations: {
client: DeprecationsClient;
};
```

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [CoreRequestHandlerContext](./kibana-plugin-core-server.corerequesthandlercontext.md) &gt; [elasticsearch](./kibana-plugin-core-server.corerequesthandlercontext.elasticsearch.md)
## CoreRequestHandlerContext.elasticsearch property
<b>Signature:</b>
```typescript
elasticsearch: {
client: IScopedClusterClient;
};
```

View file

@ -0,0 +1,25 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [CoreRequestHandlerContext](./kibana-plugin-core-server.corerequesthandlercontext.md)
## CoreRequestHandlerContext interface
The `core` context provided to route handler.
Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.client](./kibana-plugin-core-server.iscopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request
<b>Signature:</b>
```typescript
export interface CoreRequestHandlerContext
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [deprecations](./kibana-plugin-core-server.corerequesthandlercontext.deprecations.md) | { client: DeprecationsClient; } | |
| [elasticsearch](./kibana-plugin-core-server.corerequesthandlercontext.elasticsearch.md) | { client: IScopedClusterClient; } | |
| [savedObjects](./kibana-plugin-core-server.corerequesthandlercontext.savedobjects.md) | { client: SavedObjectsClientContract; typeRegistry: ISavedObjectTypeRegistry; getClient: (options?: SavedObjectsClientProviderOptions) =&gt; SavedObjectsClientContract; getExporter: (client: SavedObjectsClientContract) =&gt; ISavedObjectsExporter; getImporter: (client: SavedObjectsClientContract) =&gt; ISavedObjectsImporter; } | |
| [uiSettings](./kibana-plugin-core-server.corerequesthandlercontext.uisettings.md) | { client: IUiSettingsClient; } | |

View file

@ -0,0 +1,17 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [CoreRequestHandlerContext](./kibana-plugin-core-server.corerequesthandlercontext.md) &gt; [savedObjects](./kibana-plugin-core-server.corerequesthandlercontext.savedobjects.md)
## CoreRequestHandlerContext.savedObjects property
<b>Signature:</b>
```typescript
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract;
getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter;
getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter;
};
```

View file

@ -0,0 +1,13 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [CoreRequestHandlerContext](./kibana-plugin-core-server.corerequesthandlercontext.md) &gt; [uiSettings](./kibana-plugin-core-server.corerequesthandlercontext.uisettings.md)
## CoreRequestHandlerContext.uiSettings property
<b>Signature:</b>
```typescript
uiSettings: {
client: IUiSettingsClient;
};
```

View file

@ -0,0 +1,14 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [CustomRequestHandlerContext](./kibana-plugin-core-server.customrequesthandlercontext.md)
## CustomRequestHandlerContext type
<b>Signature:</b>
```typescript
export declare type CustomRequestHandlerContext<T> = RequestHandlerContext & {
[Key in keyof T]: T[Key] extends Promise<unknown> ? T[Key] : Promise<T[Key]>;
};
```

View file

@ -87,5 +87,5 @@ async (context, request, response) => {
| [registerOnPreAuth](./kibana-plugin-core-server.httpservicesetup.registeronpreauth.md) | (handler: OnPreAuthHandler) =&gt; void | To define custom logic to perform for incoming requests before the Auth interceptor performs a check that user has access to requested resources. |
| [registerOnPreResponse](./kibana-plugin-core-server.httpservicesetup.registeronpreresponse.md) | (handler: OnPreResponseHandler) =&gt; void | To define custom logic to perform for the server response. |
| [registerOnPreRouting](./kibana-plugin-core-server.httpservicesetup.registeronprerouting.md) | (handler: OnPreRoutingHandler) =&gt; void | To define custom logic to perform for incoming requests before server performs a route lookup. |
| [registerRouteHandlerContext](./kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md) | &lt;Context extends RequestHandlerContext, ContextName extends keyof Context&gt;(contextName: ContextName, provider: RequestHandlerContextProvider&lt;Context, ContextName&gt;) =&gt; RequestHandlerContextContainer | Register a context provider for a route handler. |
| [registerRouteHandlerContext](./kibana-plugin-core-server.httpservicesetup.registerroutehandlercontext.md) | &lt;Context extends RequestHandlerContext, ContextName extends keyof Omit&lt;Context, 'resolve'&gt;&gt;(contextName: ContextName, provider: RequestHandlerContextProvider&lt;Context, ContextName&gt;) =&gt; RequestHandlerContextContainer | Register a context provider for a route handler. |

View file

@ -9,7 +9,7 @@ Register a context provider for a route handler.
<b>Signature:</b>
```typescript
registerRouteHandlerContext: <Context extends RequestHandlerContext, ContextName extends keyof Context>(contextName: ContextName, provider: RequestHandlerContextProvider<Context, ContextName>) => RequestHandlerContextContainer;
registerRouteHandlerContext: <Context extends RequestHandlerContext, ContextName extends keyof Omit<Context, 'resolve'>>(contextName: ContextName, provider: RequestHandlerContextProvider<Context, ContextName>) => RequestHandlerContextContainer;
```
## Example

View file

@ -9,7 +9,7 @@ A function that returns a context value for a specific key of given context type
<b>Signature:</b>
```typescript
export declare type IContextProvider<Context extends RequestHandlerContext, ContextName extends keyof Context> = (context: Omit<Context, ContextName>, ...rest: HandlerParameters<RequestHandler>) => Promise<Context[ContextName]> | Context[ContextName];
export declare type IContextProvider<Context extends RequestHandlerContext, ContextName extends keyof Context> = (context: Omit<Context, ContextName>, ...rest: HandlerParameters<RequestHandler>) => MaybePromise<Awaited<Context[ContextName]>>;
```
## Remarks

View file

@ -60,6 +60,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [ConfigDeprecationDetails](./kibana-plugin-core-server.configdeprecationdetails.md) | |
| [ContextSetup](./kibana-plugin-core-server.contextsetup.md) | An object that handles registration of context providers and configuring handlers with context. |
| [CorePreboot](./kibana-plugin-core-server.corepreboot.md) | Context passed to the <code>setup</code> method of <code>preboot</code> plugins. |
| [CoreRequestHandlerContext](./kibana-plugin-core-server.corerequesthandlercontext.md) | The <code>core</code> context provided to route handler.<!-- -->Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.client](./kibana-plugin-core-server.iscopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request |
| [CoreSetup](./kibana-plugin-core-server.coresetup.md) | Context passed to the <code>setup</code> method of <code>standard</code> plugins. |
| [CoreStart](./kibana-plugin-core-server.corestart.md) | Context passed to the plugins <code>start</code> method. |
| [CoreStatus](./kibana-plugin-core-server.corestatus.md) | Status of core services. |
@ -133,7 +134,8 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [PrebootPlugin](./kibana-plugin-core-server.prebootplugin.md) | The interface that should be returned by a <code>PluginInitializer</code> for a <code>preboot</code> plugin. |
| [PrebootServicePreboot](./kibana-plugin-core-server.prebootservicepreboot.md) | Kibana Preboot Service allows to control the boot flow of Kibana. Preboot plugins can use it to hold the boot until certain condition is met. |
| [RegisterDeprecationsConfig](./kibana-plugin-core-server.registerdeprecationsconfig.md) | |
| [RequestHandlerContext](./kibana-plugin-core-server.requesthandlercontext.md) | Plugin specific context passed to a route handler.<!-- -->Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.client](./kibana-plugin-core-server.iscopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request |
| [RequestHandlerContext](./kibana-plugin-core-server.requesthandlercontext.md) | Base context passed to a route handler. |
| [RequestHandlerContextBase](./kibana-plugin-core-server.requesthandlercontextbase.md) | \* |
| [ResolveCapabilitiesOptions](./kibana-plugin-core-server.resolvecapabilitiesoptions.md) | Defines a set of additional options for the <code>resolveCapabilities</code> method of [CapabilitiesStart](./kibana-plugin-core-server.capabilitiesstart.md)<!-- -->. |
| [RouteConfig](./kibana-plugin-core-server.routeconfig.md) | Route specific configuration. |
| [RouteConfigOptions](./kibana-plugin-core-server.routeconfigoptions.md) | Additional route options. |
@ -262,6 +264,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
| [AuthResult](./kibana-plugin-core-server.authresult.md) | |
| [CapabilitiesProvider](./kibana-plugin-core-server.capabilitiesprovider.md) | See [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) |
| [CapabilitiesSwitcher](./kibana-plugin-core-server.capabilitiesswitcher.md) | See [CapabilitiesSetup](./kibana-plugin-core-server.capabilitiessetup.md) |
| [CustomRequestHandlerContext](./kibana-plugin-core-server.customrequesthandlercontext.md) | |
| [DeprecationsDetails](./kibana-plugin-core-server.deprecationsdetails.md) | |
| [DestructiveRouteMethod](./kibana-plugin-core-server.destructiveroutemethod.md) | Set of HTTP methods changing the state of the server. |
| [DocLinksServiceStart](./kibana-plugin-core-server.doclinksservicestart.md) | |

View file

@ -7,22 +7,5 @@
<b>Signature:</b>
```typescript
core: {
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract;
getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter;
getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter;
};
elasticsearch: {
client: IScopedClusterClient;
};
uiSettings: {
client: IUiSettingsClient;
};
deprecations: {
client: DeprecationsClient;
};
};
core: Promise<CoreRequestHandlerContext>;
```

View file

@ -4,19 +4,18 @@
## RequestHandlerContext interface
Plugin specific context passed to a route handler.
Provides the following clients and services: - [savedObjects.client](./kibana-plugin-core-server.savedobjectsclient.md) - Saved Objects client which uses the credentials of the incoming request - [savedObjects.typeRegistry](./kibana-plugin-core-server.isavedobjecttyperegistry.md) - Type registry containing all the registered types. - [elasticsearch.client](./kibana-plugin-core-server.iscopedclusterclient.md) - Elasticsearch data client which uses the credentials of the incoming request - [uiSettings.client](./kibana-plugin-core-server.iuisettingsclient.md) - uiSettings client which uses the credentials of the incoming request
Base context passed to a route handler.
<b>Signature:</b>
```typescript
export interface RequestHandlerContext
export interface RequestHandlerContext extends RequestHandlerContextBase
```
<b>Extends:</b> RequestHandlerContextBase
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [core](./kibana-plugin-core-server.requesthandlercontext.core.md) | { savedObjects: { client: SavedObjectsClientContract; typeRegistry: ISavedObjectTypeRegistry; getClient: (options?: SavedObjectsClientProviderOptions) =&gt; SavedObjectsClientContract; getExporter: (client: SavedObjectsClientContract) =&gt; ISavedObjectsExporter; getImporter: (client: SavedObjectsClientContract) =&gt; ISavedObjectsImporter; }; elasticsearch: { client: IScopedClusterClient; }; uiSettings: { client: IUiSettingsClient; }; deprecations: { client: DeprecationsClient; }; } | |
| [core](./kibana-plugin-core-server.requesthandlercontext.core.md) | Promise&lt;CoreRequestHandlerContext&gt; | |

View file

@ -0,0 +1,20 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [RequestHandlerContextBase](./kibana-plugin-core-server.requesthandlercontextbase.md)
## RequestHandlerContextBase interface
\*
<b>Signature:</b>
```typescript
export interface RequestHandlerContextBase
```
## Properties
| Property | Type | Description |
| --- | --- | --- |
| [resolve](./kibana-plugin-core-server.requesthandlercontextbase.resolve.md) | &lt;T extends keyof Omit&lt;this, 'resolve'&gt;&gt;(parts: T\[\]) =&gt; Promise&lt;AwaitedProperties&lt;Pick&lt;this, T&gt;&gt;&gt; | Await all the specified context parts and return them. |

View file

@ -0,0 +1,23 @@
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
[Home](./index.md) &gt; [kibana-plugin-core-server](./kibana-plugin-core-server.md) &gt; [RequestHandlerContextBase](./kibana-plugin-core-server.requesthandlercontextbase.md) &gt; [resolve](./kibana-plugin-core-server.requesthandlercontextbase.resolve.md)
## RequestHandlerContextBase.resolve property
Await all the specified context parts and return them.
<b>Signature:</b>
```typescript
resolve: <T extends keyof Omit<this, 'resolve'>>(parts: T[]) => Promise<AwaitedProperties<Pick<this, T>>>;
```
## Example
```ts
const resolved = await context.resolve(['core', 'pluginA']);
const esClient = resolved.core.elasticsearch.client;
const pluginAService = resolved.pluginA.someService;
```

View file

@ -14,7 +14,7 @@ export const registerRoutes = ({ router, log, screenshotMode }: RouteDependencie
{ path: `${BASE_API_ROUTE}/check_is_screenshot`, validate: false },
async (ctx, req, res) => {
log.info(`Reading screenshot mode from a request: ${screenshotMode.isScreenshotMode(req)}`);
log.info(`Reading is screenshot mode from ctx: ${ctx.screenshotMode.isScreenshot}`);
log.info(`Reading is screenshot mode from ctx: ${(await ctx.screenshotMode).isScreenshot}`);
return res.ok();
}
);

View file

@ -33,8 +33,9 @@ export function registerServerSearchRoute(router: IRouter<DataRequestHandlerCont
const abortSignal = getRequestAbortedSignal(request.events.aborted$);
try {
const res = await context
.search!.search(
const search = await context.search;
const res = await search
.search(
{
params: {
index,

View file

@ -26,6 +26,13 @@ export type MaybePromise<T> = T | Promise<T>;
*/
export type ShallowPromise<T> = T extends Promise<infer U> ? Promise<U> : Promise<T>;
/**
* Unwrap all promise attributes of the given type
*/
export type AwaitedProperties<T> = {
[K in keyof T]: Awaited<T[K]>;
};
/**
* Minimal interface for an object resembling an `Observable`.
*/

View file

@ -7,7 +7,7 @@
*/
import { ContextContainer } from './context';
import { PluginOpaqueId } from '../..';
import type { PluginOpaqueId, RequestHandlerContextBase } from '../..';
import { httpServerMock } from '../../http/http_server.mocks';
const pluginA = Symbol('pluginA');
@ -22,7 +22,7 @@ const plugins: ReadonlyMap<PluginOpaqueId, PluginOpaqueId[]> = new Map([
]);
const coreId = Symbol();
interface MyContext {
interface MyContext extends RequestHandlerContextBase {
core: any;
core1: string;
core2: number;
@ -32,31 +32,47 @@ interface MyContext {
ctxFromD: object;
}
describe('ContextContainer', () => {
it('does not allow the same context to be registered twice', () => {
const contextContainer = new ContextContainer(plugins, coreId);
contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>(
coreId,
'ctxFromA',
() => 'aString'
);
type TestContext<T> = T & RequestHandlerContextBase;
expect(() =>
contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>(
describe('ContextContainer', () => {
describe('registerContext', () => {
it('throws an error if the same context is registered twice', () => {
const contextContainer = new ContextContainer(plugins, coreId);
contextContainer.registerContext<TestContext<{ ctxFromA: string; core: any }>, 'ctxFromA'>(
coreId,
'ctxFromA',
() => 'aString'
)
).toThrowErrorMatchingInlineSnapshot(
`"Context provider for ctxFromA has already been registered."`
);
});
);
expect(() =>
contextContainer.registerContext<TestContext<{ ctxFromA: string; core: any }>, 'ctxFromA'>(
coreId,
'ctxFromA',
() => 'aString'
)
).toThrowErrorMatchingInlineSnapshot(
`"Context provider for ctxFromA has already been registered."`
);
});
it('throws an error if a `resolve` context is registered', () => {
const contextContainer = new ContextContainer(plugins, coreId);
expect(() =>
contextContainer.registerContext<TestContext<{ ctxFromA: string; core: any }>, 'ctxFromA'>(
coreId,
// @ts-expect-error protected with typing too
'resolve',
() => 'aString'
)
).toThrowErrorMatchingInlineSnapshot(
`"Cannot register a provider for resolve, it is a reserved keyword."`
);
});
describe('registerContext', () => {
it('throws error if called with an unknown symbol', async () => {
const contextContainer = new ContextContainer(plugins, coreId);
await expect(() =>
contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>(
contextContainer.registerContext<TestContext<{ ctxFromA: string; core: any }>, 'ctxFromA'>(
Symbol('unknown'),
'ctxFromA',
jest.fn()
@ -69,7 +85,7 @@ describe('ContextContainer', () => {
it('reports a TS error if returned contract does not satisfy the Context interface', async () => {
const contextContainer = new ContextContainer(plugins, coreId);
await expect(() =>
contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>(
contextContainer.registerContext<TestContext<{ ctxFromA: string; core: any }>, 'ctxFromA'>(
pluginA,
'ctxFromA',
// @ts-expect-error expected string, returned number
@ -82,7 +98,7 @@ describe('ContextContainer', () => {
const contextContainer = new ContextContainer(plugins, coreId);
await expect(() =>
// @ts-expect-error expects ctxFromB, but given ctxFromC
contextContainer.registerContext<{ ctxFromB: string; core: any }, 'ctxFromC'>(
contextContainer.registerContext<TestContext<{ ctxFromB: string; core: any }>, 'ctxFromC'>(
pluginB,
'ctxFromC',
async () => 1
@ -92,39 +108,216 @@ describe('ContextContainer', () => {
});
describe('context building', () => {
it('resolves dependencies', async () => {
const resolveAllContexts = async (ctx: Record<string, any>): Promise<unknown> => {
const resolved = {} as Record<string, any>;
for (const key of Object.getOwnPropertyNames(ctx)) {
if (key === 'resolve') {
continue;
}
resolved[key] = await ctx[key];
}
return resolved;
};
it('lazily loads the providers when accessed', async () => {
const contextContainer = new ContextContainer(plugins, coreId);
expect.assertions(8);
contextContainer.registerContext<{ core1: string; core: any }, 'core1'>(
const core1provider = jest.fn().mockReturnValue('core1');
const ctxFromAProvider = jest.fn().mockReturnValue('ctxFromA');
contextContainer.registerContext<TestContext<{ core1: string; core: any }>, 'core1'>(
coreId,
'core1',
(context) => {
expect(context).toEqual({});
core1provider
);
contextContainer.registerContext<TestContext<{ ctxFromA: string; core: any }>, 'ctxFromA'>(
pluginA,
'ctxFromA',
ctxFromAProvider
);
let context: any;
const rawHandler1 = jest.fn((ctx) => {
context = ctx;
return 'rawHandler1' as any;
});
const handler1 = contextContainer.createHandler(pluginC, rawHandler1);
const request = httpServerMock.createKibanaRequest();
const response = httpServerMock.createResponseFactory();
await handler1(request, response);
expect(core1provider).not.toHaveBeenCalled();
expect(ctxFromAProvider).not.toHaveBeenCalled();
await context!.core1;
expect(core1provider).toHaveBeenCalledTimes(1);
expect(ctxFromAProvider).not.toHaveBeenCalled();
await context!.ctxFromA;
expect(core1provider).toHaveBeenCalledTimes(1);
expect(ctxFromAProvider).toHaveBeenCalledTimes(1);
});
it(`does not eagerly loads a provider's dependencies`, async () => {
const contextContainer = new ContextContainer(plugins, coreId);
const core1provider = jest.fn().mockReturnValue('core1');
const ctxFromAProvider = jest.fn().mockReturnValue('ctxFromA');
contextContainer.registerContext<TestContext<{ core1: string; core: any }>, 'core1'>(
coreId,
'core1',
core1provider
);
contextContainer.registerContext<TestContext<{ ctxFromA: string; core: any }>, 'ctxFromA'>(
pluginA,
'ctxFromA',
ctxFromAProvider
);
let context: any;
const rawHandler1 = jest.fn((ctx) => {
context = ctx;
return 'rawHandler1' as any;
});
const handler1 = contextContainer.createHandler(pluginC, rawHandler1);
const request = httpServerMock.createKibanaRequest();
const response = httpServerMock.createResponseFactory();
await handler1(request, response);
expect(core1provider).not.toHaveBeenCalled();
expect(ctxFromAProvider).not.toHaveBeenCalled();
await context!.ctxFromA;
expect(core1provider).not.toHaveBeenCalled();
expect(ctxFromAProvider).toHaveBeenCalledTimes(1);
});
it(`allows to load a dependency from a provider`, async () => {
const contextContainer = new ContextContainer(plugins, coreId);
const core1provider = jest.fn().mockReturnValue('core1');
const ctxFromAProvider = jest.fn().mockImplementation(async (ctx: any) => {
const core1 = await ctx.core1;
return `${core1}-ctxFromA`;
});
contextContainer.registerContext<TestContext<{ core1: string; core: any }>, 'core1'>(
coreId,
'core1',
core1provider
);
contextContainer.registerContext<TestContext<{ ctxFromA: string; core: any }>, 'ctxFromA'>(
pluginA,
'ctxFromA',
ctxFromAProvider
);
let context: any;
const rawHandler1 = jest.fn((ctx) => {
context = ctx;
return 'rawHandler1' as any;
});
const handler1 = contextContainer.createHandler(pluginC, rawHandler1);
const request = httpServerMock.createKibanaRequest();
const response = httpServerMock.createResponseFactory();
await handler1(request, response);
expect(core1provider).not.toHaveBeenCalled();
expect(ctxFromAProvider).not.toHaveBeenCalled();
const contextValue = await context!.ctxFromA;
expect(contextValue).toEqual('core1-ctxFromA');
expect(core1provider).toHaveBeenCalledTimes(1);
expect(ctxFromAProvider).toHaveBeenCalledTimes(1);
});
it(`only calls a provider once and caches the returned value`, async () => {
const contextContainer = new ContextContainer(plugins, coreId);
const core1provider = jest.fn().mockReturnValue('core1');
const ctxFromAProvider = jest.fn().mockImplementation(async (ctx: any) => {
const core1 = await ctx.core1;
return `${core1}-ctxFromA`;
});
contextContainer.registerContext<TestContext<{ core1: string; core: any }>, 'core1'>(
coreId,
'core1',
core1provider
);
contextContainer.registerContext<TestContext<{ ctxFromA: string; core: any }>, 'ctxFromA'>(
pluginA,
'ctxFromA',
ctxFromAProvider
);
let context: any;
const rawHandler1 = jest.fn((ctx) => {
context = ctx;
return 'rawHandler1' as any;
});
const handler1 = contextContainer.createHandler(pluginC, rawHandler1);
const request = httpServerMock.createKibanaRequest();
const response = httpServerMock.createResponseFactory();
await handler1(request, response);
expect(core1provider).not.toHaveBeenCalled();
expect(ctxFromAProvider).not.toHaveBeenCalled();
await context!.core1;
await context!.ctxFromA;
await context!.core1;
expect(core1provider).toHaveBeenCalledTimes(1);
expect(ctxFromAProvider).toHaveBeenCalledTimes(1);
});
it('resolves dependencies', async () => {
const contextContainer = new ContextContainer(plugins, coreId);
expect.assertions(10);
contextContainer.registerContext<TestContext<{ core1: string; core: any }>, 'core1'>(
coreId,
'core1',
async (context) => {
expect(await resolveAllContexts(context)).toEqual({});
return 'core';
}
);
contextContainer.registerContext<{ ctxFromA: string; core: any }, 'ctxFromA'>(
contextContainer.registerContext<TestContext<{ ctxFromA: string; core: any }>, 'ctxFromA'>(
pluginA,
'ctxFromA',
(context) => {
expect(context).toEqual({ core1: 'core' });
async (context) => {
expect(await resolveAllContexts(context)).toEqual({ core1: 'core' });
return 'aString';
}
);
contextContainer.registerContext<{ ctxFromB: number; core: any }, 'ctxFromB'>(
contextContainer.registerContext<TestContext<{ ctxFromB: number; core: any }>, 'ctxFromB'>(
pluginB,
'ctxFromB',
(context) => {
expect(context).toEqual({ core1: 'core', ctxFromA: 'aString' });
async (context) => {
expect(await resolveAllContexts(context)).toEqual({ core1: 'core', ctxFromA: 'aString' });
return 299;
}
);
contextContainer.registerContext<{ ctxFromC: boolean; core: any }, 'ctxFromC'>(
contextContainer.registerContext<TestContext<{ ctxFromC: boolean; core: any }>, 'ctxFromC'>(
pluginC,
'ctxFromC',
(context) => {
expect(context).toEqual({
async (context) => {
expect(await resolveAllContexts(context)).toEqual({
core1: 'core',
ctxFromA: 'aString',
ctxFromB: 299,
@ -132,19 +325,34 @@ describe('ContextContainer', () => {
return false;
}
);
contextContainer.registerContext<{ ctxFromD: {}; core: any }, 'ctxFromD'>(
contextContainer.registerContext<TestContext<{ ctxFromD: {}; core: any }>, 'ctxFromD'>(
pluginD,
'ctxFromD',
(context) => {
expect(context).toEqual({ core1: 'core' });
async (context) => {
expect(await resolveAllContexts(context)).toEqual({ core1: 'core' });
return {};
}
);
const rawHandler1 = jest.fn(() => 'handler1' as any);
const rawHandler1 = jest.fn(async (context) => {
expect(await resolveAllContexts(context)).toEqual({
core1: 'core',
ctxFromA: 'aString',
ctxFromB: 299,
ctxFromC: false,
});
return 'handler1' as any;
});
const handler1 = contextContainer.createHandler(pluginC, rawHandler1);
const rawHandler2 = jest.fn(() => 'handler2' as any);
const rawHandler2 = jest.fn(async (context) => {
expect(await resolveAllContexts(context)).toEqual({
core1: 'core',
ctxFromD: {},
});
return 'handler2' as any;
});
const handler2 = contextContainer.createHandler(pluginD, rawHandler2);
const request = httpServerMock.createKibanaRequest();
@ -154,41 +362,25 @@ describe('ContextContainer', () => {
await handler2(request, response);
// Should have context from pluginC, its deps, and core
expect(rawHandler1).toHaveBeenCalledWith(
{
core1: 'core',
ctxFromA: 'aString',
ctxFromB: 299,
ctxFromC: false,
},
request,
response
);
expect(rawHandler1).toHaveBeenCalledWith(expect.any(Object), request, response);
// Should have context from pluginD, and core
expect(rawHandler2).toHaveBeenCalledWith(
{
core1: 'core',
ctxFromD: {},
},
request,
response
);
expect(rawHandler2).toHaveBeenCalledWith(expect.any(Object), request, response);
});
it('exposes all core context to all providers regardless of registration order', async () => {
expect.assertions(4);
expect.assertions(5);
const contextContainer = new ContextContainer(plugins, coreId);
contextContainer
.registerContext<MyContext, 'ctxFromA'>(pluginA, 'ctxFromA', (context) => {
expect(context).toEqual({ core1: 'core', core2: 101 });
return `aString ${context.core1} ${context.core2}`;
.registerContext<MyContext, 'ctxFromA'>(pluginA, 'ctxFromA', async (context) => {
expect(await resolveAllContexts(context)).toEqual({ core1: 'core', core2: 101 });
return `aString ${await context.core1} ${await context.core2}`;
})
.registerContext<MyContext, 'core1'>(coreId, 'core1', () => 'core')
.registerContext<MyContext, 'core2'>(coreId, 'core2', () => 101)
.registerContext<MyContext, 'ctxFromB'>(pluginB, 'ctxFromB', (context) => {
expect(context).toEqual({
.registerContext<MyContext, 'ctxFromB'>(pluginB, 'ctxFromB', async (context) => {
expect(await resolveAllContexts(context)).toEqual({
core1: 'core',
core2: 101,
ctxFromA: 'aString core 101',
@ -196,40 +388,45 @@ describe('ContextContainer', () => {
return 277;
});
const rawHandler1 = jest.fn(() => 'handler1' as any);
const rawHandler1 = jest.fn(async (context) => {
expect(await resolveAllContexts(context)).toEqual({
core1: 'core',
core2: 101,
ctxFromA: 'aString core 101',
ctxFromB: 277,
});
return 'handler1' as any;
});
const handler1 = contextContainer.createHandler(pluginB, rawHandler1);
const request = httpServerMock.createKibanaRequest();
const response = httpServerMock.createResponseFactory();
expect(await handler1(request, response)).toEqual('handler1');
expect(rawHandler1).toHaveBeenCalledWith(
{
core1: 'core',
core2: 101,
ctxFromA: 'aString core 101',
ctxFromB: 277,
},
request,
response
);
expect(rawHandler1).toHaveBeenCalledWith(expect.any(Object), request, response);
});
it('exposes all core context to core providers', async () => {
expect.assertions(4);
expect.assertions(5);
const contextContainer = new ContextContainer(plugins, coreId);
contextContainer
.registerContext<MyContext, 'core1'>(coreId, 'core1', (context) => {
expect(context).toEqual({});
.registerContext<MyContext, 'core1'>(coreId, 'core1', async (context) => {
expect(await resolveAllContexts(context)).toEqual({});
return 'core';
})
.registerContext<MyContext, 'core2'>(coreId, 'core2', (context) => {
expect(context).toEqual({ core1: 'core' });
.registerContext<MyContext, 'core2'>(coreId, 'core2', async (context) => {
expect(await resolveAllContexts(context)).toEqual({ core1: 'core' });
return 101;
});
const rawHandler1 = jest.fn(() => 'handler1' as any);
const rawHandler1 = jest.fn(async (context) => {
expect(await resolveAllContexts(context)).toEqual({
core1: 'core',
core2: 101,
});
return 'handler1' as any;
});
const handler1 = contextContainer.createHandler(pluginA, rawHandler1);
const request = httpServerMock.createKibanaRequest();
@ -237,14 +434,7 @@ describe('ContextContainer', () => {
expect(await handler1(request, response)).toEqual('handler1');
// If no context is registered for pluginA, only core contexts should be exposed
expect(rawHandler1).toHaveBeenCalledWith(
{
core1: 'core',
core2: 101,
},
request,
response
);
expect(rawHandler1).toHaveBeenCalledWith(expect.any(Object), request, response);
});
it('does not expose plugin contexts to core handler', async () => {
@ -254,24 +444,24 @@ describe('ContextContainer', () => {
.registerContext<MyContext, 'core1'>(coreId, 'core1', (context) => 'core')
.registerContext<MyContext, 'ctxFromA'>(pluginA, 'ctxFromA', (context) => 'aString');
const rawHandler1 = jest.fn(() => 'handler1' as any);
const rawHandler1 = jest.fn(async (context) => {
// pluginA context should not be present in a core handler
expect(await resolveAllContexts(context)).toEqual({
core1: 'core',
});
return 'handler1' as any;
});
const handler1 = contextContainer.createHandler(coreId, rawHandler1);
const request = httpServerMock.createKibanaRequest();
const response = httpServerMock.createResponseFactory();
expect(await handler1(request, response)).toEqual('handler1');
// pluginA context should not be present in a core handler
expect(rawHandler1).toHaveBeenCalledWith(
{
core1: 'core',
},
request,
response
);
expect(rawHandler1).toHaveBeenCalledWith(expect.any(Object), request, response);
});
it('passes additional arguments to providers', async () => {
expect.assertions(6);
expect.assertions(7);
const contextContainer = new ContextContainer(plugins, coreId);
const request = httpServerMock.createKibanaRequest();
@ -292,19 +482,102 @@ describe('ContextContainer', () => {
}
);
const rawHandler1 = jest.fn(() => 'handler1' as any);
const rawHandler1 = jest.fn(async (context) => {
expect(await resolveAllContexts(context)).toEqual({
core1: 'core',
ctxFromB: 77,
});
return 'handler1' as any;
});
const handler1 = contextContainer.createHandler(pluginD, rawHandler1);
expect(await handler1(request, response)).toEqual('handler1');
expect(rawHandler1).toHaveBeenCalledWith(
{
core1: 'core',
ctxFromB: 77,
},
request,
response
);
expect(rawHandler1).toHaveBeenCalledWith(expect.any(Object), request, response);
});
describe('#resolve', () => {
it('resolves dependencies', async () => {
const contextContainer = new ContextContainer(plugins, coreId);
expect.assertions(10);
contextContainer.registerContext<MyContext, 'core1'>(coreId, 'core1', async (context) => {
expect(await context.resolve([])).toEqual({});
return 'core';
});
contextContainer.registerContext<MyContext, 'ctxFromA'>(
pluginA,
'ctxFromA',
async (context) => {
expect(await context.resolve(['core1'])).toEqual({ core1: 'core' });
return 'aString';
}
);
contextContainer.registerContext<MyContext, 'ctxFromB'>(
pluginB,
'ctxFromB',
async (context) => {
expect(await context.resolve(['core1', 'ctxFromA'])).toEqual({
core1: 'core',
ctxFromA: 'aString',
});
return 299;
}
);
contextContainer.registerContext<MyContext, 'ctxFromC'>(
pluginC,
'ctxFromC',
async (context) => {
expect(await context.resolve(['core1', 'ctxFromA', 'ctxFromB'])).toEqual({
core1: 'core',
ctxFromA: 'aString',
ctxFromB: 299,
});
return false;
}
);
contextContainer.registerContext<MyContext, 'ctxFromD'>(
pluginD,
'ctxFromD',
async (context) => {
expect(await context.resolve(['core1'])).toEqual({ core1: 'core' });
return {};
}
);
const rawHandler1 = jest.fn(async (context) => {
expect(await context.resolve(['core1', 'ctxFromA', 'ctxFromB', 'ctxFromC'])).toEqual({
core1: 'core',
ctxFromA: 'aString',
ctxFromB: 299,
ctxFromC: false,
});
return 'handler1' as any;
});
const handler1 = contextContainer.createHandler(pluginC, rawHandler1);
const rawHandler2 = jest.fn(async (context) => {
expect(await context.resolve(['core1', 'ctxFromD'])).toEqual({
core1: 'core',
ctxFromD: {},
});
return 'handler2' as any;
});
const handler2 = contextContainer.createHandler(pluginD, rawHandler2);
const request = httpServerMock.createKibanaRequest();
const response = httpServerMock.createResponseFactory();
await handler1(request, response);
await handler2(request, response);
// Should have context from pluginC, its deps, and core
expect(rawHandler1).toHaveBeenCalledWith(expect.any(Object), request, response);
// Should have context from pluginD, and core
expect(rawHandler2).toHaveBeenCalledWith(expect.any(Object), request, response);
});
});
});
@ -337,7 +610,11 @@ describe('ContextContainer', () => {
const request = httpServerMock.createKibanaRequest();
const response = httpServerMock.createResponseFactory();
await handler1(request, response);
expect(rawHandler1).toHaveBeenCalledWith({}, request, response);
expect(rawHandler1).toHaveBeenCalledWith(
{ resolve: expect.any(Function) },
request,
response
);
});
});
});

View file

@ -7,8 +7,7 @@
*/
import { flatten } from 'lodash';
import { ShallowPromise } from '@kbn/utility-types';
import { pick } from 'lodash';
import { ShallowPromise, MaybePromise } from '@kbn/utility-types';
import type { CoreId, PluginOpaqueId, RequestHandler, RequestHandlerContext } from '../..';
/**
@ -31,7 +30,7 @@ export type IContextProvider<
// context.core will always be available, but plugin contexts are typed as optional
context: Omit<Context, ContextName>,
...rest: HandlerParameters<RequestHandler>
) => Promise<Context[ContextName]> | Context[ContextName];
) => MaybePromise<Awaited<Context[ContextName]>>;
/**
* A function that accepts a context object and an optional number of additional arguments. Used for the generic types
@ -202,6 +201,9 @@ export class ContextContainer implements IContextContainer {
provider: IContextProvider<Context, ContextName>
): this => {
const contextName = name as string;
if (contextName === 'resolve') {
throw new Error(`Cannot register a provider for ${contextName}, it is a reserved keyword.`);
}
if (this.contextProviders.has(contextName)) {
throw new Error(`Context provider for ${contextName} has already been registered.`);
}
@ -224,36 +226,51 @@ export class ContextContainer implements IContextContainer {
}
return (async (...args: HandlerParameters<RequestHandler>) => {
const context = await this.buildContext(source, ...args);
const context = this.buildContext(source, ...args);
return handler(context, ...args);
}) as (
...args: HandlerParameters<RequestHandler>
) => ShallowPromise<ReturnType<RequestHandler>>;
};
private async buildContext(
private buildContext(
source: symbol,
...contextArgs: HandlerParameters<RequestHandler>
): Promise<HandlerContextType<RequestHandler>> {
): HandlerContextType<RequestHandler> {
const contextsToBuild = new Set(this.getContextNamesForSource(source));
const builtContextPromises: Record<string, Promise<unknown>> = {};
const builtContext = {} as HandlerContextType<RequestHandler>;
(builtContext as unknown as RequestHandlerContext).resolve = async (keys) => {
const resolved = await Promise.all(
keys.map(async (key) => {
return [key, await builtContext[key]];
})
);
return Object.fromEntries(resolved);
};
return [...this.contextProviders]
.sort(sortByCoreFirst(this.coreId))
.filter(([contextName]) => contextsToBuild.has(contextName))
.reduce(async (contextPromise, [contextName, { provider, source: providerSource }]) => {
const resolvedContext = await contextPromise;
.reduce((contextAccessors, [contextName, { provider, source: providerSource }]) => {
const exposedContext = createExposedContext({
currentContextName: contextName,
exposedContextNames: [...this.getContextNamesForSource(providerSource)],
contextAccessors,
});
Object.defineProperty(contextAccessors, contextName, {
get: async () => {
const contextKey = contextName as keyof HandlerContextType<RequestHandler>;
if (!builtContextPromises[contextKey]) {
builtContextPromises[contextKey] = provider(exposedContext, ...contextArgs);
}
return await builtContextPromises[contextKey];
},
});
// For the next provider, only expose the context available based on the dependencies of the plugin that
// registered that provider.
const exposedContext = pick(resolvedContext, [
...this.getContextNamesForSource(providerSource),
]);
return {
...resolvedContext,
[contextName]: await provider(exposedContext, ...contextArgs),
};
}, Promise.resolve({}) as Promise<HandlerContextType<RequestHandler>>);
return contextAccessors;
}, builtContext);
}
private getContextNamesForSource(source: symbol): ReadonlySet<string> {
@ -299,3 +316,28 @@ const sortByCoreFirst =
return rightProvider.source === coreId ? 1 : 0;
}
};
const createExposedContext = ({
currentContextName,
exposedContextNames,
contextAccessors,
}: {
currentContextName: string;
exposedContextNames: string[];
contextAccessors: Partial<HandlerContextType<RequestHandler>>;
}) => {
const exposedContext: Partial<HandlerContextType<RequestHandler>> = {};
exposedContext.resolve = contextAccessors.resolve;
for (const contextName of exposedContextNames) {
if (contextName === currentContextName) {
continue;
}
const descriptor = Object.getOwnPropertyDescriptor(contextAccessors, contextName);
if (descriptor) {
Object.defineProperty(exposedContext, contextName, descriptor);
}
}
return exposedContext;
};

View file

@ -89,7 +89,8 @@ export class CoreApp {
const resources = coreSetup.httpResources.createRegistrar(router);
router.get({ path: '/', validate: false }, async (context, req, res) => {
const defaultRoute = await context.core.uiSettings.client.get<string>('defaultRoute');
const { uiSettings } = await context.core;
const defaultRoute = await uiSettings.client.get<string>('defaultRoute');
const basePath = httpSetup.basePath.get(req);
const url = `${basePath}${defaultRoute}`;

View file

@ -15,7 +15,7 @@ export const registerGetRoute = (router: IRouter) => {
validate: false,
},
async (context, req, res) => {
const deprecationsClient = context.core.deprecations.client;
const deprecationsClient = (await context.core).deprecations.client;
const body: DeprecationsGetResponse = {
deprecations: await deprecationsClient.getAllDeprecations(),

View file

@ -65,10 +65,8 @@ describe('trace', () => {
const router = createRouter('');
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
const { headers } = await context.core.elasticsearch.client.asInternalUser.ping(
{},
{ meta: true }
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asInternalUser.ping({}, { meta: true });
return res.ok({ body: headers || {} });
});
@ -90,10 +88,8 @@ describe('trace', () => {
const router = createRouter('');
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping(
{},
{ meta: true }
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asCurrentUser.ping({}, { meta: true });
return res.ok({ body: headers || {} });
});
@ -115,10 +111,8 @@ describe('trace', () => {
const router = createRouter('');
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
const { headers } = await context.core.elasticsearch.client.asInternalUser.ping(
{},
{ meta: true }
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asInternalUser.ping({}, { meta: true });
return res.ok({ body: headers || {} });
});
@ -136,10 +130,8 @@ describe('trace', () => {
const router = createRouter('');
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping(
{},
{ meta: true }
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asCurrentUser.ping({}, { meta: true });
return res.ok({ body: headers || {} });
});
@ -157,7 +149,8 @@ describe('trace', () => {
const router = createRouter('');
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
const { headers } = await context.core.elasticsearch.client.asInternalUser.ping(
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asInternalUser.ping(
{},
{
opaqueId: 'new-opaque-id',
@ -203,10 +196,8 @@ describe('trace', () => {
const router = createRouter('');
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping(
{},
{ meta: true }
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asCurrentUser.ping({}, { meta: true });
return res.ok({ body: headers || {} });
});
@ -229,10 +220,8 @@ describe('trace', () => {
const router = createRouter('');
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
executionContext.set(parentContext);
const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping(
{},
{ meta: true }
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asCurrentUser.ping({}, { meta: true });
return res.ok({
body: {
context: executionContext.get()?.toJSON(),
@ -345,10 +334,8 @@ describe('trace', () => {
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
executionContext.set(parentContext);
await delay(id-- * 100);
const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping(
{},
{ meta: true }
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asCurrentUser.ping({}, { meta: true });
return res.ok({ body: headers || {} });
});
@ -397,10 +384,8 @@ describe('trace', () => {
const router = createRouter('');
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping(
{},
{ meta: true }
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asCurrentUser.ping({}, { meta: true });
return res.ok({ body: headers || {} });
});
@ -487,10 +472,8 @@ describe('trace', () => {
const router = createRouter('');
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping(
{},
{ meta: true }
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asCurrentUser.ping({}, { meta: true });
return res.ok({ body: headers || {} });
});
@ -511,10 +494,8 @@ describe('trace', () => {
const router = createRouter('');
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
const { headers } = await context.core.elasticsearch.client.asInternalUser.ping(
{},
{ meta: true }
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asInternalUser.ping({}, { meta: true });
return res.ok({ body: headers || {} });
});
@ -535,10 +516,8 @@ describe('trace', () => {
const router = createRouter('');
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping(
{},
{ meta: true }
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asCurrentUser.ping({}, { meta: true });
return res.ok({ body: headers || {} });
});
@ -561,10 +540,8 @@ describe('trace', () => {
const router = createRouter('');
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
executionContext.set(parentContext);
const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping(
{},
{ meta: true }
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asCurrentUser.ping({}, { meta: true });
return res.ok({ body: headers || {} });
});
@ -593,10 +570,8 @@ describe('trace', () => {
};
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
executionContext.set(ctx);
const { headers } = await context.core.elasticsearch.client.asCurrentUser.ping(
{},
{ meta: true }
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await esClient.asCurrentUser.ping({}, { meta: true });
return res.ok({ body: headers || {} });
});
@ -663,9 +638,10 @@ describe('trace', () => {
description: 'new-description',
};
router.get({ path: '/execution-context', validate: false }, async (context, req, res) => {
const { headers } = await executionContext.withContext(newContext, () =>
context.core.elasticsearch.client.asCurrentUser.ping({}, { meta: true })
);
const esClient = (await context.core).elasticsearch.client;
const { headers } = await executionContext.withContext(newContext, () => {
return esClient.asCurrentUser.ping({}, { meta: true });
});
return res.ok({ body: headers || {} });
});

View file

@ -366,7 +366,7 @@ export interface HttpServiceSetup {
*/
registerRouteHandlerContext: <
Context extends RequestHandlerContext,
ContextName extends keyof Context
ContextName extends keyof Omit<Context, 'resolve'>
>(
contextName: ContextName,
provider: RequestHandlerContextProvider<Context, ContextName>
@ -393,7 +393,7 @@ export interface InternalHttpServiceSetup
authRequestHeaders: IAuthHeadersStorage;
registerRouteHandlerContext: <
Context extends RequestHandlerContext,
ContextName extends keyof Context
ContextName extends keyof Omit<Context, 'resolve'>
>(
pluginOpaqueId: PluginOpaqueId,
contextName: ContextName,

View file

@ -27,7 +27,7 @@ describe('HttpResources service', () => {
let setupDeps: SetupDeps;
let router: jest.Mocked<IRouter>;
const kibanaRequest = httpServerMock.createKibanaRequest();
const context = { core: coreMock.createRequestHandlerContext() };
const context = coreMock.createCustomRequestHandlerContext({});
const apmConfig = { mockApmConfig: true };
beforeEach(() => {
@ -71,7 +71,7 @@ describe('HttpResources service', () => {
await routeHandler(context, kibanaRequest, responseFactory);
expect(getDeps().rendering.render).toHaveBeenCalledWith(
kibanaRequest,
context.core.uiSettings.client,
(await context.core).uiSettings.client,
{
isAnonymousPage: false,
vars: {
@ -117,7 +117,7 @@ describe('HttpResources service', () => {
await routeHandler(context, kibanaRequest, responseFactory);
expect(getDeps().rendering.render).toHaveBeenCalledWith(
kibanaRequest,
context.core.uiSettings.client,
(await context.core).uiSettings.client,
{
isAnonymousPage: true,
vars: {

View file

@ -93,7 +93,8 @@ export class HttpResourcesService implements CoreService<InternalHttpResourcesSe
return {
async renderCoreApp(options: HttpResourcesRenderOptions = {}) {
const apmConfig = getApmConfig(request.url.pathname);
const body = await deps.rendering.render(request, context.core.uiSettings.client, {
const { uiSettings } = await context.core;
const body = await deps.rendering.render(request, uiSettings.client, {
isAnonymousPage: false,
vars: {
apmConfig,
@ -108,7 +109,8 @@ export class HttpResourcesService implements CoreService<InternalHttpResourcesSe
},
async renderAnonymousCoreApp(options: HttpResourcesRenderOptions = {}) {
const apmConfig = getApmConfig(request.url.pathname);
const body = await deps.rendering.render(request, context.core.uiSettings.client, {
const { uiSettings } = await context.core;
const body = await deps.rendering.render(request, uiSettings.client, {
isAnonymousPage: true,
vars: {
apmConfig,

View file

@ -28,6 +28,7 @@
* @packageDocumentation
*/
import { AwaitedProperties } from '@kbn/utility-types';
import { Type } from '@kbn/config-schema';
import {
ElasticsearchServiceSetup,
@ -465,8 +466,39 @@ export type {
} from './analytics';
export { TelemetryCounterType } from './analytics';
/** @public **/
export interface RequestHandlerContextBase {
/**
* Await all the specified context parts and return them.
*
* @example
* ```ts
* const resolved = await context.resolve(['core', 'pluginA']);
* const esClient = resolved.core.elasticsearch.client;
* const pluginAService = resolved.pluginA.someService;
* ```
*/
resolve: <T extends keyof Omit<this, 'resolve'>>(
parts: T[]
) => Promise<AwaitedProperties<Pick<this, T>>>;
}
/**
* Plugin specific context passed to a route handler.
* Base context passed to a route handler.
*
* @public
*/
export interface RequestHandlerContext extends RequestHandlerContextBase {
core: Promise<CoreRequestHandlerContext>;
}
/** @public */
export type CustomRequestHandlerContext<T> = RequestHandlerContext & {
[Key in keyof T]: T[Key] extends Promise<unknown> ? T[Key] : Promise<T[Key]>;
};
/**
* The `core` context provided to route handler.
*
* Provides the following clients and services:
* - {@link SavedObjectsClient | savedObjects.client} - Saved Objects client
@ -477,27 +509,24 @@ export { TelemetryCounterType } from './analytics';
* data client which uses the credentials of the incoming request
* - {@link IUiSettingsClient | uiSettings.client} - uiSettings client
* which uses the credentials of the incoming request
*
* @public
*/
export interface RequestHandlerContext {
core: {
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract;
getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter;
getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter;
};
elasticsearch: {
client: IScopedClusterClient;
};
uiSettings: {
client: IUiSettingsClient;
};
deprecations: {
client: DeprecationsClient;
};
export interface CoreRequestHandlerContext {
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract;
getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter;
getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter;
};
elasticsearch: {
client: IScopedClusterClient;
};
uiSettings: {
client: IUiSettingsClient;
};
deprecations: {
client: DeprecationsClient;
};
}

View file

@ -9,6 +9,7 @@
import { of } from 'rxjs';
import { duration } from 'moment';
import { ByteSizeValue } from '@kbn/config-schema';
import { isPromise } from '@kbn/std';
import type { MockedKeys } from '@kbn/utility-types/jest';
import type {
PluginInitializerContext,
@ -66,7 +67,11 @@ export { executionContextServiceMock } from './execution_context/execution_conte
export { docLinksServiceMock } from './doc_links/doc_links_service.mock';
export { analyticsServiceMock } from './analytics/analytics_service.mock';
export type { ElasticsearchClientMock } from './elasticsearch/client/mocks';
export type {
ElasticsearchClientMock,
ClusterClientMock,
ScopedClusterClientMock,
} from './elasticsearch/client/mocks';
type MockedPluginInitializerConfig<T> = jest.Mocked<PluginInitializerContext<T>['config']>;
@ -280,6 +285,40 @@ function createCoreRequestHandlerContextMock() {
};
}
export type CustomRequestHandlerMock<T> = {
core: Promise<ReturnType<typeof createCoreRequestHandlerContextMock>>;
resolve: jest.MockedFunction<any>;
} & {
[Key in keyof T]: T[Key] extends Promise<unknown> ? T[Key] : Promise<T[Key]>;
};
const createCustomRequestHandlerContextMock = <T>(contextParts: T): CustomRequestHandlerMock<T> => {
const mock = Object.entries(contextParts).reduce(
(context, [key, value]) => {
// @ts-expect-error type matching from inferred types is hard
context[key] = isPromise(value) ? value : Promise.resolve(value);
return context;
},
{
core: Promise.resolve(createCoreRequestHandlerContextMock()),
} as CustomRequestHandlerMock<T>
);
mock.resolve = jest.fn().mockImplementation(async () => {
const resolved = {};
for (const propName of Object.keys(mock)) {
if (propName === 'resolve') {
continue;
}
// @ts-expect-error type matching from inferred types is hard
resolved[propName] = await mock[propName];
}
return resolved;
});
return mock;
};
export const coreMock = {
createPreboot: createCorePrebootMock,
createSetup: createCoreSetupMock,
@ -289,4 +328,5 @@ export const coreMock = {
createInternalStart: createInternalCoreStartMock,
createPluginInitializerContext: pluginInitializerContextMock,
createRequestHandlerContext: createCoreRequestHandlerContextMock,
createCustomRequestHandlerContext: createCustomRequestHandlerContextMock,
};

View file

@ -185,7 +185,7 @@ export function createPluginSetupContext<TPlugin, TPluginDependencies>(
createCookieSessionStorageFactory: deps.http.createCookieSessionStorageFactory,
registerRouteHandlerContext: <
Context extends RequestHandlerContext,
ContextName extends keyof Context
ContextName extends keyof Omit<Context, 'resolve'>
>(
contextName: ContextName,
provider: RequestHandlerContextProvider<Context, ContextName>

View file

@ -25,7 +25,7 @@ export const registerBootstrapRoute = ({
validate: false,
},
async (ctx, req, res) => {
const uiSettingsClient = ctx.core.uiSettings.client;
const uiSettingsClient = (await ctx.core).uiSettings.client;
const { body, etag } = await renderer({ uiSettingsClient, request: req });
return res.ok({
@ -48,7 +48,7 @@ export const registerBootstrapRoute = ({
validate: false,
},
async (ctx, req, res) => {
const uiSettingsClient = ctx.core.uiSettings.client;
const uiSettingsClient = (await ctx.core).uiSettings.client;
const { body, etag } = await renderer({
uiSettingsClient,
request: req,

View file

@ -51,7 +51,8 @@ export const registerBulkCreateRoute = (router: IRouter, { coreUsageData }: Rout
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkCreate({ request: req }).catch(() => {});
const result = await context.core.savedObjects.client.bulkCreate(req.body, { overwrite });
const { savedObjects } = await context.core;
const result = await savedObjects.client.bulkCreate(req.body, { overwrite });
return res.ok({ body: result });
})
);

View file

@ -34,7 +34,8 @@ export const registerBulkGetRoute = (router: IRouter, { coreUsageData }: RouteDe
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkGet({ request: req }).catch(() => {});
const result = await context.core.savedObjects.client.bulkGet(req.body);
const { savedObjects } = await context.core;
const result = await savedObjects.client.bulkGet(req.body);
return res.ok({ body: result });
})
);

View file

@ -32,7 +32,8 @@ export const registerBulkResolveRoute = (router: IRouter, { coreUsageData }: Rou
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkResolve({ request: req }).catch(() => {});
const result = await context.core.savedObjects.client.bulkResolve(req.body);
const { savedObjects } = await context.core;
const result = await savedObjects.client.bulkResolve(req.body);
return res.ok({ body: result });
})
);

View file

@ -44,7 +44,8 @@ export const registerBulkUpdateRoute = (router: IRouter, { coreUsageData }: Rout
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsBulkUpdate({ request: req }).catch(() => {});
const savedObject = await context.core.savedObjects.client.bulkUpdate(req.body);
const { savedObjects } = await context.core;
const savedObject = await savedObjects.client.bulkUpdate(req.body);
return res.ok({ body: savedObject });
})
);

View file

@ -61,7 +61,8 @@ export const registerCreateRoute = (router: IRouter, { coreUsageData }: RouteDep
references,
initialNamespaces,
};
const result = await context.core.savedObjects.client.create(type, attributes, options);
const { savedObjects } = await context.core;
const result = await savedObjects.client.create(type, attributes, options);
return res.ok({ body: result });
})
);

View file

@ -32,7 +32,7 @@ export const registerDeleteRoute = (router: IRouter, { coreUsageData }: RouteDep
catchAndReturnBoomErrors(async (context, req, res) => {
const { type, id } = req.params;
const { force } = req.query;
const { getClient } = context.core.savedObjects;
const { getClient } = (await context.core).savedObjects;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsDelete({ request: req }).catch(() => {});

View file

@ -25,9 +25,10 @@ export const registerDeleteUnknownTypesRoute = (
validate: false,
},
catchAndReturnBoomErrors(async (context, req, res) => {
const { elasticsearch, savedObjects } = await context.core;
await deleteUnknownTypeObjects({
esClient: context.core.elasticsearch.client,
typeRegistry: context.core.savedObjects.typeRegistry,
esClient: elasticsearch.client,
typeRegistry: savedObjects.typeRegistry,
kibanaIndex,
kibanaVersion,
});

View file

@ -165,7 +165,7 @@ export const registerExportRoute = (
},
catchAndReturnBoomErrors(async (context, req, res) => {
const cleaned = cleanOptions(req.body);
const { typeRegistry, getExporter, getClient } = context.core.savedObjects;
const { typeRegistry, getExporter, getClient } = (await context.core).savedObjects;
const supportedTypes = typeRegistry.getImportableAndExportableTypes().map((t) => t.name);
let options: EitherExportOptions;

View file

@ -73,8 +73,8 @@ export const registerFindRoute = (router: IRouter, { coreUsageData }: RouteDepen
});
}
}
const result = await context.core.savedObjects.client.find({
const { savedObjects } = await context.core;
const result = await savedObjects.client.find({
perPage: query.per_page,
page: query.page,
type: Array.isArray(query.type) ? query.type : [query.type],

View file

@ -32,8 +32,9 @@ export const registerGetRoute = (router: IRouter, { coreUsageData }: RouteDepend
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsGet({ request: req }).catch(() => {});
const savedObject = await context.core.savedObjects.client.get(type, id);
return res.ok({ body: savedObject });
const { savedObjects } = await context.core;
const object = await savedObjects.client.get(type, id);
return res.ok({ body: object });
})
);
};

View file

@ -63,7 +63,7 @@ export const registerImportRoute = (
},
catchAndReturnBoomErrors(async (context, req, res) => {
const { overwrite, createNewCopies } = req.query;
const { getClient, getImporter, typeRegistry } = context.core.savedObjects;
const { getClient, getImporter, typeRegistry } = (await context.core).savedObjects;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient

View file

@ -38,7 +38,7 @@ export const registerLegacyExportRoute = (
);
const ids = Array.isArray(req.query.dashboard) ? req.query.dashboard : [req.query.dashboard];
const { client } = ctx.core.savedObjects;
const { client } = (await ctx.core).savedObjects;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementLegacyDashboardsExport({ request: req }).catch(() => {});

View file

@ -46,7 +46,7 @@ export const registerLegacyImportRoute = (
"The import dashboard API '/api/kibana/dashboards/import' is deprecated. Use the saved objects import objects API '/api/saved_objects/_import' instead."
);
const { client } = ctx.core.savedObjects;
const { client } = (await ctx.core).savedObjects;
const objects = req.body.objects as SavedObject[];
const { force, exclude } = req.query;

View file

@ -27,11 +27,12 @@ export const registerResolveRoute = (router: IRouter, { coreUsageData }: RouteDe
},
router.handleLegacyErrors(async (context, req, res) => {
const { type, id } = req.params;
const { savedObjects } = await context.core;
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsResolve({ request: req }).catch(() => {});
const result = await context.core.savedObjects.client.resolve(type, id);
const result = await savedObjects.client.resolve(type, id);
return res.ok({ body: result });
})
);

View file

@ -92,7 +92,7 @@ export const registerResolveImportErrorsRoute = (
});
}
const { getClient, getImporter, typeRegistry } = context.core.savedObjects;
const { getClient, getImporter, typeRegistry } = (await context.core).savedObjects;
const includedHiddenTypes = chain(req.body.retries)
.map('type')

View file

@ -49,7 +49,8 @@ export const registerUpdateRoute = (router: IRouter, { coreUsageData }: RouteDep
const usageStatsClient = coreUsageData.getClient();
usageStatsClient.incrementSavedObjectsUpdate({ request: req }).catch(() => {});
const result = await context.core.savedObjects.client.update(type, id, attributes, options);
const { savedObjects } = await context.core;
const result = await savedObjects.client.update(type, id, attributes, options);
return res.ok({ body: result });
})
);

View file

@ -9,6 +9,7 @@
import { AddConfigDeprecation } from '@kbn/config';
import { AnalyticsClient } from '@kbn/analytics-client';
import apm from 'elastic-apm-node';
import { AwaitedProperties } from '@kbn/utility-types';
import Boom from '@hapi/boom';
import { ByteSizeValue } from '@kbn/config-schema';
import { CliArgs } from '@kbn/config';
@ -446,6 +447,30 @@ export interface CorePreboot {
preboot: PrebootServicePreboot;
}
// @public
export interface CoreRequestHandlerContext {
// (undocumented)
deprecations: {
client: DeprecationsClient;
};
// (undocumented)
elasticsearch: {
client: IScopedClusterClient;
};
// (undocumented)
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract;
getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter;
getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter;
};
// (undocumented)
uiSettings: {
client: IUiSettingsClient;
};
}
// @internal
export interface CoreServicesUsageData {
// (undocumented)
@ -840,6 +865,11 @@ export interface CustomHttpResponseOptions<T extends HttpResponsePayload | Respo
statusCode: number;
}
// @public (undocumented)
export type CustomRequestHandlerContext<T> = RequestHandlerContext & {
[Key in keyof T]: T[Key] extends Promise<unknown> ? T[Key] : Promise<T[Key]>;
};
// @internal (undocumented)
export const DEFAULT_APP_CATEGORIES: Record<string, AppCategory>;
@ -1189,7 +1219,7 @@ export interface HttpServiceSetup {
registerOnPreAuth: (handler: OnPreAuthHandler) => void;
registerOnPreResponse: (handler: OnPreResponseHandler) => void;
registerOnPreRouting: (handler: OnPreRoutingHandler) => void;
registerRouteHandlerContext: <Context extends RequestHandlerContext, ContextName extends keyof Context>(contextName: ContextName, provider: RequestHandlerContextProvider<Context, ContextName>) => RequestHandlerContextContainer;
registerRouteHandlerContext: <Context extends RequestHandlerContext, ContextName extends keyof Omit<Context, 'resolve'>>(contextName: ContextName, provider: RequestHandlerContextProvider<Context, ContextName>) => RequestHandlerContextContainer;
}
// @public (undocumented)
@ -1221,7 +1251,7 @@ export interface IContextContainer {
}
// @public
export type IContextProvider<Context extends RequestHandlerContext, ContextName extends keyof Context> = (context: Omit<Context, ContextName>, ...rest: HandlerParameters<RequestHandler>) => Promise<Context[ContextName]> | Context[ContextName];
export type IContextProvider<Context extends RequestHandlerContext, ContextName extends keyof Context> = (context: Omit<Context, ContextName>, ...rest: HandlerParameters<RequestHandler>) => MaybePromise<Awaited<Context[ContextName]>>;
// @public
export interface ICspConfig {
@ -1840,26 +1870,14 @@ export interface RegisterDeprecationsConfig {
export type RequestHandler<P = unknown, Q = unknown, B = unknown, Context extends RequestHandlerContext = RequestHandlerContext, Method extends RouteMethod = any, ResponseFactory extends KibanaResponseFactory = KibanaResponseFactory> = (context: Context, request: KibanaRequest<P, Q, B, Method>, response: ResponseFactory) => IKibanaResponse<any> | Promise<IKibanaResponse<any>>;
// @public
export interface RequestHandlerContext {
export interface RequestHandlerContext extends RequestHandlerContextBase {
// (undocumented)
core: {
savedObjects: {
client: SavedObjectsClientContract;
typeRegistry: ISavedObjectTypeRegistry;
getClient: (options?: SavedObjectsClientProviderOptions) => SavedObjectsClientContract;
getExporter: (client: SavedObjectsClientContract) => ISavedObjectsExporter;
getImporter: (client: SavedObjectsClientContract) => ISavedObjectsImporter;
};
elasticsearch: {
client: IScopedClusterClient;
};
uiSettings: {
client: IUiSettingsClient;
};
deprecations: {
client: DeprecationsClient;
};
};
core: Promise<CoreRequestHandlerContext>;
}
// @public (undocumented)
export interface RequestHandlerContextBase {
resolve: <T extends keyof Omit<this, 'resolve'>>(parts: T[]) => Promise<AwaitedProperties<Pick<this, T>>>;
}
// @public

View file

@ -42,7 +42,6 @@ import { config as uiSettingsConfig } from './ui_settings';
import { config as statusConfig } from './status';
import { config as i18nConfig } from './i18n';
import { ContextService } from './context';
import { RequestHandlerContext } from '.';
import {
InternalCorePreboot,
InternalCoreSetup,
@ -372,13 +371,9 @@ export class Server {
}
private registerCoreContext(coreSetup: InternalCoreSetup) {
coreSetup.http.registerRouteHandlerContext(
coreId,
'core',
(context, req, res): RequestHandlerContext['core'] => {
return new CoreRouteHandlerContext(this.coreStart!, req);
}
);
coreSetup.http.registerRouteHandlerContext(coreId, 'core', async (context, req, res) => {
return new CoreRouteHandlerContext(this.coreStart!, req);
});
}
public setupCoreConfig() {

View file

@ -23,7 +23,7 @@ export function registerDeleteRoute(router: IRouter) {
{ path: '/api/kibana/settings/{key}', validate },
async (context, request, response) => {
try {
const uiSettingsClient = context.core.uiSettings.client;
const uiSettingsClient = (await context.core).uiSettings.client;
await uiSettingsClient.remove(request.params.key);

View file

@ -14,7 +14,7 @@ export function registerGetRoute(router: IRouter) {
{ path: '/api/kibana/settings', validate: false },
async (context, request, response) => {
try {
const uiSettingsClient = context.core.uiSettings.client;
const uiSettingsClient = (await context.core).uiSettings.client;
return response.ok({
body: {
settings: await uiSettingsClient.getUserProvided(),

View file

@ -26,7 +26,7 @@ export function registerSetRoute(router: IRouter) {
{ path: '/api/kibana/settings/{key}', validate },
async (context, request, response) => {
try {
const uiSettingsClient = context.core.uiSettings.client;
const uiSettingsClient = (await context.core).uiSettings.client;
const { key } = request.params;
const { value } = request.body;

View file

@ -21,7 +21,7 @@ const validate = {
export function registerSetManyRoute(router: IRouter) {
router.post({ path: '/api/kibana/settings', validate }, async (context, request, response) => {
try {
const uiSettingsClient = context.core.uiSettings.client;
const uiSettingsClient = (await context.core).uiSettings.client;
const { changes } = request.body;

View file

@ -53,7 +53,7 @@ export const setupOptionsListSuggestionsRoute = (
try {
const suggestionRequest: OptionsListRequestBody = request.body;
const { index } = request.params;
const esClient = context.core.elasticsearch.client.asCurrentUser;
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
const suggestionsResponse = await getOptionsListSuggestions({
abortedEvent$: request.events.aborted$,
request: suggestionRequest,

View file

@ -6,6 +6,7 @@
* Side Public License, v 1.
*/
import { coreMock } from '@kbn/core/server/mocks';
import {
createFieldFormatsSetupMock,
createFieldFormatsStartMock,
@ -17,7 +18,6 @@ import {
} from './search/mocks';
import { createIndexPatternsStartMock } from './data_views/mocks';
import { createDatatableUtilitiesMock } from './datatable_utilities/mock';
import { DataRequestHandlerContext } from './search';
function createSetupContract() {
return {
@ -43,8 +43,9 @@ function createStartContract() {
function createRequestHandlerContext() {
return {
core: coreMock.createRequestHandlerContext(),
search: createSearchRequestHandlerContext(),
} as unknown as jest.Mocked<DataRequestHandlerContext>;
};
}
export const dataPluginMock = {

View file

@ -20,7 +20,6 @@ const {
savedObjects: { client: mockSavedObjectsClient },
},
} = mockContext;
const context = registerSavedQueryRouteHandlerContext(mockContext);
const savedQueryAttributes: SavedQueryAttributes = {
title: 'foo',
@ -73,7 +72,15 @@ const savedQueryReferences = [
];
describe('saved query route handler context', () => {
beforeEach(() => {
let context: Awaited<ReturnType<typeof registerSavedQueryRouteHandlerContext>>;
beforeEach(async () => {
context = await registerSavedQueryRouteHandlerContext(
coreMock.createCustomRequestHandlerContext({
core: mockContext.core,
})
);
mockSavedObjectsClient.create.mockClear();
mockSavedObjectsClient.resolve.mockClear();
mockSavedObjectsClient.find.mockClear();

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import { RequestHandlerContext, SavedObject } from '@kbn/core/server';
import { CustomRequestHandlerContext, RequestHandlerContext, SavedObject } from '@kbn/core/server';
import { isFilters } from '@kbn/es-query';
import { isQuery, SavedQueryAttributes } from '../../common';
import { extract, inject } from '../../common/query/persistable_state';
@ -66,18 +66,16 @@ function verifySavedQuery({ title, query, filters = [] }: SavedQueryAttributes)
}
}
export function registerSavedQueryRouteHandlerContext(context: RequestHandlerContext) {
export async function registerSavedQueryRouteHandlerContext(context: RequestHandlerContext) {
const soClient = (await context.core).savedObjects.client;
const createSavedQuery = async (attrs: SavedQueryAttributes) => {
verifySavedQuery(attrs);
const { attributes, references } = extractReferences(attrs);
const savedObject = await context.core.savedObjects.client.create<SavedQueryAttributes>(
'query',
attributes,
{
references,
}
);
const savedObject = await soClient.create<SavedQueryAttributes>('query', attributes, {
references,
});
// TODO: Handle properly
if (savedObject.error) throw new Error(savedObject.error.message);
@ -89,14 +87,9 @@ export function registerSavedQueryRouteHandlerContext(context: RequestHandlerCon
verifySavedQuery(attrs);
const { attributes, references } = extractReferences(attrs);
const savedObject = await context.core.savedObjects.client.update<SavedQueryAttributes>(
'query',
id,
attributes,
{
references,
}
);
const savedObject = await soClient.update<SavedQueryAttributes>('query', id, attributes, {
references,
});
// TODO: Handle properly
if (savedObject.error) throw new Error(savedObject.error.message);
@ -105,8 +98,10 @@ export function registerSavedQueryRouteHandlerContext(context: RequestHandlerCon
};
const getSavedQuery = async (id: string) => {
const { saved_object: savedObject, outcome } =
await context.core.savedObjects.client.resolve<SavedQueryAttributes>('query', id);
const { saved_object: savedObject, outcome } = await soClient.resolve<SavedQueryAttributes>(
'query',
id
);
if (outcome === 'conflict') {
throw new Error(`Multiple saved queries found with ID: ${id} (legacy URL alias conflict)`);
} else if (savedObject.error) {
@ -116,20 +111,19 @@ export function registerSavedQueryRouteHandlerContext(context: RequestHandlerCon
};
const getSavedQueriesCount = async () => {
const { total } = await context.core.savedObjects.client.find<SavedQueryAttributes>({
const { total } = await soClient.find<SavedQueryAttributes>({
type: 'query',
});
return total;
};
const findSavedQueries = async ({ page = 1, perPage = 50, search = '' } = {}) => {
const { total, saved_objects: savedObjects } =
await context.core.savedObjects.client.find<SavedQueryAttributes>({
type: 'query',
page,
perPage,
search,
});
const { total, saved_objects: savedObjects } = await soClient.find<SavedQueryAttributes>({
type: 'query',
page,
perPage,
search,
});
const savedQueries = savedObjects.map(injectReferences);
@ -137,7 +131,7 @@ export function registerSavedQueryRouteHandlerContext(context: RequestHandlerCon
};
const getAllSavedQueries = async () => {
const finder = context.core.savedObjects.client.createPointInTimeFinder<SavedQueryAttributes>({
const finder = soClient.createPointInTimeFinder<SavedQueryAttributes>({
type: 'query',
perPage: 100,
});
@ -152,8 +146,8 @@ export function registerSavedQueryRouteHandlerContext(context: RequestHandlerCon
return { total: savedQueries.length, savedQueries };
};
const deleteSavedQuery = (id: string) => {
return context.core.savedObjects.client.delete('query', id);
const deleteSavedQuery = async (id: string) => {
return await soClient.delete('query', id);
};
return {
@ -167,6 +161,6 @@ export function registerSavedQueryRouteHandlerContext(context: RequestHandlerCon
};
}
export interface SavedQueryRouteHandlerContext extends RequestHandlerContext {
savedQuery: ReturnType<typeof registerSavedQueryRouteHandlerContext>;
}
export type SavedQueryRouteHandlerContext = CustomRequestHandlerContext<{
savedQuery: Promise<ReturnType<typeof registerSavedQueryRouteHandlerContext>>;
}>;

View file

@ -37,7 +37,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void {
},
async (context, request, response) => {
try {
const body = await context.savedQuery.create(request.body);
const savedQuery = await context.savedQuery;
const body = await savedQuery.create(request.body);
return response.ok({ body });
} catch (e) {
// TODO: Handle properly
@ -57,7 +58,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void {
async (context, request, response) => {
const { id } = request.params;
try {
const body = await context.savedQuery.update(id, request.body);
const savedQuery = await context.savedQuery;
const body = await savedQuery.update(id, request.body);
return response.ok({ body });
} catch (e) {
// TODO: Handle properly
@ -76,7 +78,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void {
async (context, request, response) => {
const { id } = request.params;
try {
const body = await context.savedQuery.get(id);
const savedQuery = await context.savedQuery;
const body = await savedQuery.get(id);
return response.ok({ body });
} catch (e) {
// TODO: Handle properly
@ -92,7 +95,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void {
},
async (context, request, response) => {
try {
const count = await context.savedQuery.count();
const savedQuery = await context.savedQuery;
const count = await savedQuery.count();
return response.ok({ body: `${count}` });
} catch (e) {
// TODO: Handle properly
@ -114,7 +118,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void {
},
async (context, request, response) => {
try {
const body = await context.savedQuery.find(request.body);
const savedQuery = await context.savedQuery;
const body = await savedQuery.find(request.body);
return response.ok({ body });
} catch (e) {
// TODO: Handle properly
@ -130,7 +135,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void {
},
async (context, request, response) => {
try {
const body = await context.savedQuery.getAll();
const savedQuery = await context.savedQuery;
const body = await savedQuery.getAll();
return response.ok({ body });
} catch (e) {
// TODO: Handle properly
@ -149,7 +155,8 @@ export function registerSavedQueryRoutes({ http }: CoreSetup): void {
async (context, request, response) => {
const { id } = request.params;
try {
const body = await context.savedQuery.delete(id);
const savedQuery = await context.savedQuery;
const body = await savedQuery.delete(id);
return response.ok({ body });
} catch (e) {
// TODO: Handle properly

View file

@ -47,8 +47,9 @@ export function registerSearchRoute(router: DataPluginRouter): void {
const abortSignal = getRequestAbortedSignal(request.events.aborted$);
try {
const response = await context
.search!.search(
const search = await context.search;
const response = await search
.search(
{ ...searchRequest, id },
{
abortSignal,
@ -85,7 +86,8 @@ export function registerSearchRoute(router: DataPluginRouter): void {
const { strategy, id } = request.params;
try {
await context.search!.cancel(id, { strategy });
const search = await context.search;
await search.cancel(id, { strategy });
return res.ok();
} catch (err) {
return reportServerError(res, err);

View file

@ -143,7 +143,7 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {
core.http.registerRouteHandlerContext<DataRequestHandlerContext, 'search'>(
'search',
async (context, request) => {
(context, request) => {
return this.asScoped(request);
}
);

View file

@ -13,7 +13,7 @@ import type {
IUiSettingsClient,
SavedObjectsClientContract,
KibanaRequest,
RequestHandlerContext,
CustomRequestHandlerContext,
} from '@kbn/core/server';
import {
ISearchOptions,
@ -126,8 +126,8 @@ export interface ISearchStart<
export type SearchRequestHandlerContext = IScopedSearchClient;
export interface DataRequestHandlerContext extends RequestHandlerContext {
export type DataRequestHandlerContext = CustomRequestHandlerContext<{
search: SearchRequestHandlerContext;
}
}>;
export type DataPluginRouter = IRouter<DataRequestHandlerContext>;

View file

@ -38,7 +38,7 @@ export const registerFieldPreviewRoute = ({ router }: RouteDependencies): void =
},
},
async (ctx, req, res) => {
const { client } = ctx.core.elasticsearch;
const { client } = (await ctx.core).elasticsearch;
const type = req.body.context.split('_field')[0] as estypes.MappingRuntimeFieldType;
const body = {

View file

@ -24,7 +24,7 @@ export function registerPreviewScriptedFieldRoute(router: IRouter): void {
},
},
async (context, request, res) => {
const client = context.core.elasticsearch.client.asCurrentUser;
const client = (await context.core).elasticsearch.client.asCurrentUser;
const { index, name, script, query, additionalFields } = request.body;
try {

View file

@ -31,7 +31,8 @@ export function registerResolveIndexRoute(router: IRouter): void {
},
},
async (context, req, res) => {
const body = await context.core.elasticsearch.client.asCurrentUser.indices.resolveIndex({
const esClient = (await context.core).elasticsearch.client;
const body = await esClient.asCurrentUser.indices.resolveIndex({
name: req.params.query,
expand_wildcards: req.query.expand_wildcards || 'open',
});

View file

@ -94,8 +94,9 @@ const registerCreateDataViewRouteFactory =
},
router.handleLegacyErrors(
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { dataViewsServiceFactory }] = await getStartServices();
const dataViewsService = await dataViewsServiceFactory(

View file

@ -64,8 +64,9 @@ const manageDefaultIndexPatternRoutesFactory =
validate: {},
},
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { dataViewsServiceFactory }] = await getStartServices();
const dataViewsService = await dataViewsServiceFactory(
savedObjectsClient,
@ -103,8 +104,9 @@ const manageDefaultIndexPatternRoutesFactory =
},
},
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { dataViewsServiceFactory }] = await getStartServices();
const dataViewsService = await dataViewsServiceFactory(
savedObjectsClient,

View file

@ -58,8 +58,9 @@ const deleteIndexPatternRouteFactory =
},
router.handleLegacyErrors(
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { dataViewsServiceFactory }] = await getStartServices();
const dataViewsService = await dataViewsServiceFactory(
savedObjectsClient,

View file

@ -134,8 +134,9 @@ const updateFieldsActionRouteFactory = (path: string, serviceKey: string) => {
},
router.handleLegacyErrors(
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { dataViewsServiceFactory }] = await getStartServices();
const dataViewsService = await dataViewsServiceFactory(
savedObjectsClient,

View file

@ -63,8 +63,9 @@ const getDataViewRouteFactory =
},
router.handleLegacyErrors(
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { dataViewsServiceFactory }] = await getStartServices();
const dataViewsService = await dataViewsServiceFactory(
savedObjectsClient,

View file

@ -45,8 +45,9 @@ const hasUserDataViewRouteFactory =
},
router.handleLegacyErrors(
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { dataViewsServiceFactory }] = await getStartServices();
const dataViewsService = await dataViewsServiceFactory(

View file

@ -92,8 +92,9 @@ const runtimeCreateFieldRouteFactory =
},
},
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { dataViewsServiceFactory }] = await getStartServices();
const dataViewsService = await dataViewsServiceFactory(
savedObjectsClient,

View file

@ -73,8 +73,9 @@ const deleteRuntimeFieldRouteFactory =
},
},
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { dataViewsServiceFactory }] = await getStartServices();
const dataViewsService = await dataViewsServiceFactory(
savedObjectsClient,

View file

@ -80,8 +80,9 @@ const getRuntimeFieldRouteFactory =
},
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { dataViewsServiceFactory }] = await getStartServices();
const dataViewsService = await dataViewsServiceFactory(
savedObjectsClient,

View file

@ -92,8 +92,9 @@ const putRuntimeFieldRouteFactory =
},
},
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { dataViewsServiceFactory }] = await getStartServices();
const dataViewsService = await dataViewsServiceFactory(
savedObjectsClient,

View file

@ -93,8 +93,9 @@ const updateRuntimeFieldRouteFactory =
},
},
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { dataViewsServiceFactory }] = await getStartServices();
const dataViewsService = await dataViewsServiceFactory(
savedObjectsClient,

View file

@ -42,8 +42,9 @@ export const registerCreateScriptedFieldRoute = (
},
router.handleLegacyErrors(
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { indexPatternsServiceFactory }] = await getStartServices();
const indexPatternsService = await indexPatternsServiceFactory(
savedObjectsClient,

View file

@ -43,8 +43,9 @@ export const registerDeleteScriptedFieldRoute = (
},
router.handleLegacyErrors(
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { indexPatternsServiceFactory }] = await getStartServices();
const indexPatternsService = await indexPatternsServiceFactory(
savedObjectsClient,

View file

@ -43,8 +43,9 @@ export const registerGetScriptedFieldRoute = (
},
router.handleLegacyErrors(
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { indexPatternsServiceFactory }] = await getStartServices();
const indexPatternsService = await indexPatternsServiceFactory(
savedObjectsClient,

View file

@ -42,8 +42,9 @@ export const registerPutScriptedFieldRoute = (
},
router.handleLegacyErrors(
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { indexPatternsServiceFactory }] = await getStartServices();
const indexPatternsService = await indexPatternsServiceFactory(
savedObjectsClient,

View file

@ -63,8 +63,9 @@ export const registerUpdateScriptedFieldRoute = (
},
router.handleLegacyErrors(
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { indexPatternsServiceFactory }] = await getStartServices();
const indexPatternsService = await indexPatternsServiceFactory(
savedObjectsClient,

View file

@ -161,8 +161,9 @@ const updateDataViewRouteFactory =
},
router.handleLegacyErrors(
handleErrors(async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const [, , { dataViewsServiceFactory }] = await getStartServices();
const dataViewsService = await dataViewsServiceFactory(

View file

@ -56,7 +56,7 @@ export function registerRoutes(
},
},
async (context, request, response) => {
const { asCurrentUser } = context.core.elasticsearch.client;
const { asCurrentUser } = (await context.core).elasticsearch.client;
const indexPatterns = new IndexPatternsFetcher(asCurrentUser);
const { pattern, interval, look_back: lookBack, meta_fields: metaFields } = request.query;

View file

@ -51,7 +51,7 @@ const validate: RouteValidatorFullConfig<{}, IQuery, IBody> = {
body: schema.maybe(schema.object({ index_filter: schema.any() })),
};
const handler: RequestHandler<{}, IQuery, IBody> = async (context, request, response) => {
const { asCurrentUser } = context.core.elasticsearch.client;
const { asCurrentUser } = (await context.core).elasticsearch.client;
const indexPatterns = new IndexPatternsFetcher(asCurrentUser);
const {
pattern,

View file

@ -16,8 +16,9 @@ export const registerHasDataViewsRoute = (router: IRouter): void => {
validate: {},
},
async (ctx, req, res) => {
const savedObjectsClient = ctx.core.savedObjects.client;
const elasticsearchClient = ctx.core.elasticsearch.client.asCurrentUser;
const core = await ctx.core;
const savedObjectsClient = core.savedObjects.client;
const elasticsearchClient = core.elasticsearch.client.asCurrentUser;
const dataViews = await getIndexPattern({
esClient: elasticsearchClient,
soClient: savedObjectsClient,

View file

@ -22,7 +22,7 @@ export const registerHitsStatusRoute = (router: IRouter) => {
},
router.handleLegacyErrors(async (context, req, res) => {
const { index, query } = req.body;
const client = context.core.elasticsearch.client;
const client = (await context.core).elasticsearch.client;
try {
const body = await client.asCurrentUser.search({

View file

@ -38,7 +38,7 @@ export function createInstallRoute(
// @ts-ignore Custom query validation used
const now = query.now ? new Date(query.now) : new Date();
const sampleDataInstaller = getSampleDataInstaller({
const sampleDataInstaller = await getSampleDataInstaller({
datasetId: sampleDataset.id,
sampleDatasets,
logger,

View file

@ -78,7 +78,7 @@ async function findExistingSampleObjects(
.map(({ savedObjects }) => savedObjects.map(({ type, id }) => ({ type, id })))
.flat();
const objectTypes = getUniqueObjectTypes(objects);
const client = getSavedObjectsClient(context, objectTypes);
const client = await getSavedObjectsClient(context, objectTypes);
const findSampleObjectsResult = await findSampleObjects({ client, logger, objects });
let objectCounter = 0;
@ -101,18 +101,20 @@ async function getSampleDatasetStatus(
return { status: NOT_INSTALLED };
}
const { elasticsearch } = await context.core;
for (let i = 0; i < sampleDataset.dataIndices.length; i++) {
const dataIndexConfig = sampleDataset.dataIndices[i];
const index = createIndexName(sampleDataset.id, dataIndexConfig.id);
try {
const indexExists = await context.core.elasticsearch.client.asCurrentUser.indices.exists({
const indexExists = await elasticsearch.client.asCurrentUser.indices.exists({
index,
});
if (!indexExists) {
return { status: NOT_INSTALLED };
}
const count = await context.core.elasticsearch.client.asCurrentUser.count({
const count = await elasticsearch.client.asCurrentUser.count({
index,
});
if (count.count === 0) {

View file

@ -32,7 +32,7 @@ export function createUninstallRoute(
return response.notFound();
}
const sampleDataInstaller = getSampleDataInstaller({
const sampleDataInstaller = await getSampleDataInstaller({
datasetId: sampleDataset.id,
sampleDatasets,
logger,

View file

@ -11,7 +11,7 @@ import type { SampleDatasetSchema } from '../lib/sample_dataset_registry_types';
import { SampleDataInstaller } from '../sample_data_installer';
import { getUniqueObjectTypes } from '../lib/utils';
export const getSampleDataInstaller = ({
export const getSampleDataInstaller = async ({
datasetId,
context,
sampleDatasets,
@ -22,14 +22,15 @@ export const getSampleDataInstaller = ({
sampleDatasets: SampleDatasetSchema[];
logger: Logger;
}) => {
const core = await context.core;
const sampleDataset = sampleDatasets.find(({ id }) => id === datasetId)!;
const { getImporter, client: soClient } = context.core.savedObjects;
const { getImporter, client: soClient } = core.savedObjects;
const objectTypes = getUniqueObjectTypes(sampleDataset.savedObjects);
const savedObjectsClient = getSavedObjectsClient(context, objectTypes);
const savedObjectsClient = await getSavedObjectsClient(context, objectTypes);
const soImporter = getImporter(savedObjectsClient);
return new SampleDataInstaller({
esClient: context.core.elasticsearch.client,
esClient: core.elasticsearch.client,
soImporter,
soClient,
logger,
@ -37,8 +38,11 @@ export const getSampleDataInstaller = ({
});
};
export const getSavedObjectsClient = (context: RequestHandlerContext, objectTypes: string[]) => {
const { getClient, typeRegistry } = context.core.savedObjects;
export const getSavedObjectsClient = async (
context: RequestHandlerContext,
objectTypes: string[]
) => {
const { getClient, typeRegistry } = (await context.core).savedObjects;
const includedHiddenTypes = objectTypes.filter((supportedType) =>
typeRegistry.isHidden(supportedType)
);

View file

@ -29,7 +29,7 @@ export const registerBulkGetRoute = (
},
router.handleLegacyErrors(async (context, req, res) => {
const managementService = await managementServicePromise;
const { getClient, typeRegistry } = context.core.savedObjects;
const { getClient, typeRegistry } = (await context.core).savedObjects;
const objects = req.body;
const uniqueTypes = objects.reduce((acc, { type }) => acc.add(type), new Set<string>());

View file

@ -47,7 +47,7 @@ export const registerFindRoute = (
router.handleLegacyErrors(async (context, req, res) => {
const { query } = req;
const managementService = await managementServicePromise;
const { getClient, typeRegistry } = context.core.savedObjects;
const { getClient, typeRegistry } = (await context.core).savedObjects;
const searchTypes = Array.isArray(query.type) ? query.type : [query.type];
const includedFields = Array.isArray(query.fields) ? query.fields : [query.fields];

View file

@ -25,7 +25,7 @@ export const registerGetAllowedTypesRoute = (router: IRouter) => {
validate: false,
},
async (context, req, res) => {
const allowedTypes = context.core.savedObjects.typeRegistry
const allowedTypes = (await context.core).savedObjects.typeRegistry
.getImportableAndExportableTypes()
.filter((type) => type.management!.visibleInManagement ?? true)
.map(convertType);

View file

@ -32,7 +32,7 @@ export const registerRelationshipsRoute = (
},
router.handleLegacyErrors(async (context, req, res) => {
const managementService = await managementServicePromise;
const { getClient, typeRegistry } = context.core.savedObjects;
const { getClient, typeRegistry } = (await context.core).savedObjects;
const { type, id } = req.params;
const { size, savedObjectTypes: maybeArraySavedObjectTypes } = req.query;
const savedObjectTypes = Array.isArray(maybeArraySavedObjectTypes)

View file

@ -31,7 +31,7 @@ export const registerScrollForCountRoute = (router: IRouter) => {
},
},
router.handleLegacyErrors(async (context, req, res) => {
const { getClient, typeRegistry } = context.core.savedObjects;
const { getClient, typeRegistry } = (await context.core).savedObjects;
const { typesToInclude, searchString, references } = req.body;
const includedHiddenTypes = chain(typesToInclude)

View file

@ -6,7 +6,7 @@
* Side Public License, v 1.
*/
import type { RequestHandlerContext, KibanaRequest } from '@kbn/core/server';
import type { CustomRequestHandlerContext, KibanaRequest } from '@kbn/core/server';
export interface ScreenshotModePluginStart {
/**
@ -32,8 +32,8 @@ export interface ScreenshotModePluginSetup extends ScreenshotModePluginStart {
setScreenshotModeEnabled(): void;
}
export interface ScreenshotModeRequestHandlerContext extends RequestHandlerContext {
export type ScreenshotModeRequestHandlerContext = CustomRequestHandlerContext<{
screenshotMode: {
isScreenshot: boolean;
};
}
}>;

View file

@ -34,7 +34,7 @@ export const registerCreateRoute = (router: IRouter, url: ServerUrlService) => {
},
},
router.handleLegacyErrors(async (ctx, req, res) => {
const savedObjects = ctx.core.savedObjects.client;
const savedObjects = (await ctx.core).savedObjects.client;
const shortUrls = url.shortUrls.get({ savedObjects });
const { locatorId, params, slug, humanReadableSlug } = req.body;
const locator = url.locators.get(locatorId);

View file

@ -25,7 +25,7 @@ export const registerDeleteRoute = (router: IRouter, url: ServerUrlService) => {
},
router.handleLegacyErrors(async (ctx, req, res) => {
const id = req.params.id;
const savedObjects = ctx.core.savedObjects.client;
const savedObjects = (await ctx.core).savedObjects.client;
const shortUrls = url.shortUrls.get({ savedObjects });
await shortUrls.delete(id);

View file

@ -25,7 +25,7 @@ export const registerGetRoute = (router: IRouter, url: ServerUrlService) => {
},
router.handleLegacyErrors(async (ctx, req, res) => {
const id = req.params.id;
const savedObjects = ctx.core.savedObjects.client;
const savedObjects = (await ctx.core).savedObjects.client;
const shortUrls = url.shortUrls.get({ savedObjects });
const shortUrl = await shortUrls.get(id);

View file

@ -26,7 +26,7 @@ export const registerResolveRoute = (router: IRouter, url: ServerUrlService) =>
},
router.handleLegacyErrors(async (ctx, req, res) => {
const slug = req.params.slug;
const savedObjects = ctx.core.savedObjects.client;
const savedObjects = (await ctx.core).savedObjects.client;
try {
const shortUrls = url.shortUrls.get({ savedObjects });

View file

@ -48,12 +48,13 @@ export function registerTelemetryOptInRoutes({
},
async (context, req, res) => {
const newOptInStatus = req.body.enabled;
const soClient = (await context.core).savedObjects.client;
const attributes: TelemetrySavedObjectAttributes = {
enabled: newOptInStatus,
lastVersionChecked: currentKibanaVersion,
};
const config = await firstValueFrom(config$);
const telemetrySavedObject = await getTelemetrySavedObject(context.core.savedObjects.client);
const telemetrySavedObject = await getTelemetrySavedObject(soClient);
if (telemetrySavedObject === false) {
// If we get false, we couldn't get the saved object due to lack of permissions
@ -96,7 +97,7 @@ export function registerTelemetryOptInRoutes({
}
try {
await updateTelemetrySavedObject(context.core.savedObjects.client, attributes);
await updateTelemetrySavedObject(soClient, attributes);
} catch (e) {
if (SavedObjectsErrorHelpers.isForbiddenError(e)) {
return res.forbidden();

View file

@ -21,7 +21,7 @@ export function registerTelemetryUserHasSeenNotice(router: IRouter) {
validate: false,
},
async (context, req, res) => {
const internalRepository = context.core.savedObjects.client;
const internalRepository = (await context.core).savedObjects.client;
const telemetrySavedObject: TelemetrySavedObject = await getTelemetrySavedObject(
internalRepository
);

View file

@ -45,13 +45,14 @@ export function registerValueSuggestionsRoute(router: IRouter, config$: Observab
const { field: fieldName, query, filters, fieldMeta, method } = request.body;
const { index } = request.params;
const abortSignal = getRequestAbortedSignal(request.events.aborted$);
const { savedObjects, elasticsearch } = await context.core;
try {
const fn = method === 'terms_agg' ? termsAggSuggestions : termsEnumSuggestions;
const body = await fn(
config,
context.core.savedObjects.client,
context.core.elasticsearch.client.asCurrentUser,
savedObjects.client,
elasticsearch.client.asCurrentUser,
index,
fieldName,
query,

Some files were not shown because too many files have changed in this diff Show more