[8.14] [HTTP/OAS] HTTP API (#181144) (#181239)

# Backport

This will backport the following commits from `main` to `8.14`:
- [[HTTP/OAS] HTTP API
(#181144)](https://github.com/elastic/kibana/pull/181144)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Jean-Louis
Leysens","email":"jeanlouis.leysens@elastic.co"},"sourceCommit":{"committedDate":"2024-04-19T13:07:35Z","message":"[HTTP/OAS]
HTTP API (#181144)\n\n## Summary\r\n\r\nAdds a new core-owned
experimental HTTP API: `GET /api/oas` with the\r\nfollowing query params
for filtering:\r\n\r\n- `pluginId` - request a specific plugin's
OAS\r\n- `pathStartsWith` - request OAS for paths that start with the
given\r\nstring\r\n\r\nThe current rationale is that this will provide
sufficient scoping to\r\nallow developers and docs maintainers to
request only the OAS they need\r\n(since Kibana's surface area is
HUGE).\r\n\r\nAlso added a new experimental config: `server.oas.enabled:
<boolean>`.\r\nSet this to `true` to play around with this feature
locally.\r\n\r\n## How to test\r\n\r\n1. Add `server.oas.enabled: true`
to `kibana.dev.yml`\r\n2. Start Kibana with `yarn start
--no-base-path`\r\n3. `curl
-uelastic:changeme\r\nhttp://localhost:5601/api/oas\\?pathStartsWith\\=/api/status
| jq`\r\n4. You should have a JSON representation of an OAS doc
containing the\r\n`/api/status`
endpoint!\r\n\r\n<details>\r\n\r\n<summary>example</summary>\r\n\r\n```json\r\n{\r\n
\"openapi\": \"3.0.0\",\r\n \"info\": {\r\n \"title\": \"Kibana HTTP
APIs\",\r\n \"version\": \"0.0.0\"\r\n },\r\n \"servers\": [\r\n {\r\n
\"url\": \"http://localhost:5601\"\r\n }\r\n ],\r\n \"paths\": {\r\n
\"/api/status\": {\r\n \"get\": {\r\n \"responses\": {},\r\n
\"parameters\": [\r\n {\r\n \"in\": \"header\",\r\n \"name\":
\"elastic-api-version\",\r\n \"schema\": {\r\n \"type\": \"string\",\r\n
\"enum\": [\r\n \"2023-10-31\"\r\n ],\r\n \"default\":
\"2023-10-31\"\r\n }\r\n },\r\n {\r\n \"name\": \"v7format\",\r\n
\"in\": \"query\",\r\n \"required\": false,\r\n \"schema\": {\r\n
\"type\": \"boolean\"\r\n }\r\n },\r\n {\r\n \"name\": \"v8format\",\r\n
\"in\": \"query\",\r\n \"required\": false,\r\n \"schema\": {\r\n
\"type\": \"boolean\"\r\n }\r\n }\r\n ],\r\n \"operationId\":
\"/api/status#0\"\r\n }\r\n }\r\n },\r\n \"components\": {\r\n
\"schemas\": {},\r\n \"securitySchemes\": {\r\n \"basicAuth\": {\r\n
\"type\": \"http\",\r\n \"scheme\": \"basic\"\r\n },\r\n \"apiKeyAuth\":
{\r\n \"type\": \"apiKey\",\r\n \"in\": \"header\",\r\n \"name\":
\"Authorization\"\r\n }\r\n }\r\n },\r\n \"security\": [\r\n {\r\n
\"basicAuth\": []\r\n }\r\n
]\r\n}\r\n\r\n```\r\n\r\n</details>\r\n\r\n## Risks\r\n\r\n* The work to
generate OAS can be expensive (for both CPU and memory).\r\nTo mitigate,
the endpoint will only be registered provided a specific\r\nconfig and
we are using concurrency control (`Semaphore`) to allow only\r\none of
these operations to be handled at a time.\r\n\r\n## Future work\r\n\r\n*
We must start the long tail work of furnishing endpoints with
missing\r\nresponse schemas. I intend to do so for the `/api/status`
route next\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"654c0dc467784d0c71d7c20e409136c9e10a1e3c","branchLabelMapping":{"^v8.15.0$":"main","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Feature:http","Team:Core","release_note:skip","Team:obs-ux-infra_services","apm:review","v8.14.0","v8.15.0"],"title":"[HTTP/OAS]
HTTP API
`/api/oas`","number":181144,"url":"https://github.com/elastic/kibana/pull/181144","mergeCommit":{"message":"[HTTP/OAS]
HTTP API (#181144)\n\n## Summary\r\n\r\nAdds a new core-owned
experimental HTTP API: `GET /api/oas` with the\r\nfollowing query params
for filtering:\r\n\r\n- `pluginId` - request a specific plugin's
OAS\r\n- `pathStartsWith` - request OAS for paths that start with the
given\r\nstring\r\n\r\nThe current rationale is that this will provide
sufficient scoping to\r\nallow developers and docs maintainers to
request only the OAS they need\r\n(since Kibana's surface area is
HUGE).\r\n\r\nAlso added a new experimental config: `server.oas.enabled:
<boolean>`.\r\nSet this to `true` to play around with this feature
locally.\r\n\r\n## How to test\r\n\r\n1. Add `server.oas.enabled: true`
to `kibana.dev.yml`\r\n2. Start Kibana with `yarn start
--no-base-path`\r\n3. `curl
-uelastic:changeme\r\nhttp://localhost:5601/api/oas\\?pathStartsWith\\=/api/status
| jq`\r\n4. You should have a JSON representation of an OAS doc
containing the\r\n`/api/status`
endpoint!\r\n\r\n<details>\r\n\r\n<summary>example</summary>\r\n\r\n```json\r\n{\r\n
\"openapi\": \"3.0.0\",\r\n \"info\": {\r\n \"title\": \"Kibana HTTP
APIs\",\r\n \"version\": \"0.0.0\"\r\n },\r\n \"servers\": [\r\n {\r\n
\"url\": \"http://localhost:5601\"\r\n }\r\n ],\r\n \"paths\": {\r\n
\"/api/status\": {\r\n \"get\": {\r\n \"responses\": {},\r\n
\"parameters\": [\r\n {\r\n \"in\": \"header\",\r\n \"name\":
\"elastic-api-version\",\r\n \"schema\": {\r\n \"type\": \"string\",\r\n
\"enum\": [\r\n \"2023-10-31\"\r\n ],\r\n \"default\":
\"2023-10-31\"\r\n }\r\n },\r\n {\r\n \"name\": \"v7format\",\r\n
\"in\": \"query\",\r\n \"required\": false,\r\n \"schema\": {\r\n
\"type\": \"boolean\"\r\n }\r\n },\r\n {\r\n \"name\": \"v8format\",\r\n
\"in\": \"query\",\r\n \"required\": false,\r\n \"schema\": {\r\n
\"type\": \"boolean\"\r\n }\r\n }\r\n ],\r\n \"operationId\":
\"/api/status#0\"\r\n }\r\n }\r\n },\r\n \"components\": {\r\n
\"schemas\": {},\r\n \"securitySchemes\": {\r\n \"basicAuth\": {\r\n
\"type\": \"http\",\r\n \"scheme\": \"basic\"\r\n },\r\n \"apiKeyAuth\":
{\r\n \"type\": \"apiKey\",\r\n \"in\": \"header\",\r\n \"name\":
\"Authorization\"\r\n }\r\n }\r\n },\r\n \"security\": [\r\n {\r\n
\"basicAuth\": []\r\n }\r\n
]\r\n}\r\n\r\n```\r\n\r\n</details>\r\n\r\n## Risks\r\n\r\n* The work to
generate OAS can be expensive (for both CPU and memory).\r\nTo mitigate,
the endpoint will only be registered provided a specific\r\nconfig and
we are using concurrency control (`Semaphore`) to allow only\r\none of
these operations to be handled at a time.\r\n\r\n## Future work\r\n\r\n*
We must start the long tail work of furnishing endpoints with
missing\r\nresponse schemas. I intend to do so for the `/api/status`
route next\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"654c0dc467784d0c71d7c20e409136c9e10a1e3c"}},"sourceBranch":"main","suggestedTargetBranches":["8.14"],"targetPullRequestStates":[{"branch":"8.14","label":"v8.14.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"state":"NOT_CREATED"},{"branch":"main","label":"v8.15.0","branchLabelMappingKey":"^v8.15.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/181144","number":181144,"mergeCommit":{"message":"[HTTP/OAS]
HTTP API (#181144)\n\n## Summary\r\n\r\nAdds a new core-owned
experimental HTTP API: `GET /api/oas` with the\r\nfollowing query params
for filtering:\r\n\r\n- `pluginId` - request a specific plugin's
OAS\r\n- `pathStartsWith` - request OAS for paths that start with the
given\r\nstring\r\n\r\nThe current rationale is that this will provide
sufficient scoping to\r\nallow developers and docs maintainers to
request only the OAS they need\r\n(since Kibana's surface area is
HUGE).\r\n\r\nAlso added a new experimental config: `server.oas.enabled:
<boolean>`.\r\nSet this to `true` to play around with this feature
locally.\r\n\r\n## How to test\r\n\r\n1. Add `server.oas.enabled: true`
to `kibana.dev.yml`\r\n2. Start Kibana with `yarn start
--no-base-path`\r\n3. `curl
-uelastic:changeme\r\nhttp://localhost:5601/api/oas\\?pathStartsWith\\=/api/status
| jq`\r\n4. You should have a JSON representation of an OAS doc
containing the\r\n`/api/status`
endpoint!\r\n\r\n<details>\r\n\r\n<summary>example</summary>\r\n\r\n```json\r\n{\r\n
\"openapi\": \"3.0.0\",\r\n \"info\": {\r\n \"title\": \"Kibana HTTP
APIs\",\r\n \"version\": \"0.0.0\"\r\n },\r\n \"servers\": [\r\n {\r\n
\"url\": \"http://localhost:5601\"\r\n }\r\n ],\r\n \"paths\": {\r\n
\"/api/status\": {\r\n \"get\": {\r\n \"responses\": {},\r\n
\"parameters\": [\r\n {\r\n \"in\": \"header\",\r\n \"name\":
\"elastic-api-version\",\r\n \"schema\": {\r\n \"type\": \"string\",\r\n
\"enum\": [\r\n \"2023-10-31\"\r\n ],\r\n \"default\":
\"2023-10-31\"\r\n }\r\n },\r\n {\r\n \"name\": \"v7format\",\r\n
\"in\": \"query\",\r\n \"required\": false,\r\n \"schema\": {\r\n
\"type\": \"boolean\"\r\n }\r\n },\r\n {\r\n \"name\": \"v8format\",\r\n
\"in\": \"query\",\r\n \"required\": false,\r\n \"schema\": {\r\n
\"type\": \"boolean\"\r\n }\r\n }\r\n ],\r\n \"operationId\":
\"/api/status#0\"\r\n }\r\n }\r\n },\r\n \"components\": {\r\n
\"schemas\": {},\r\n \"securitySchemes\": {\r\n \"basicAuth\": {\r\n
\"type\": \"http\",\r\n \"scheme\": \"basic\"\r\n },\r\n \"apiKeyAuth\":
{\r\n \"type\": \"apiKey\",\r\n \"in\": \"header\",\r\n \"name\":
\"Authorization\"\r\n }\r\n }\r\n },\r\n \"security\": [\r\n {\r\n
\"basicAuth\": []\r\n }\r\n
]\r\n}\r\n\r\n```\r\n\r\n</details>\r\n\r\n## Risks\r\n\r\n* The work to
generate OAS can be expensive (for both CPU and memory).\r\nTo mitigate,
the endpoint will only be registered provided a specific\r\nconfig and
we are using concurrency control (`Semaphore`) to allow only\r\none of
these operations to be handled at a time.\r\n\r\n## Future work\r\n\r\n*
We must start the long tail work of furnishing endpoints with
missing\r\nresponse schemas. I intend to do so for the `/api/status`
route next\r\n\r\n---------\r\n\r\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"654c0dc467784d0c71d7c20e409136c9e10a1e3c"}}]}]
BACKPORT-->

Co-authored-by: Jean-Louis Leysens <jeanlouis.leysens@elastic.co>
This commit is contained in:
Kibana Machine 2024-04-19 10:28:27 -04:00 committed by GitHub
parent 963727c1c5
commit e65ee683e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 424 additions and 100 deletions

View file

@ -135,6 +135,12 @@ describe('Router', () => {
expect(lazyValidation).toHaveBeenCalledTimes(1);
});
it('registers pluginId if provided', () => {
const pluginId = Symbol('test');
const router = new Router('', logger, enhanceWithContext, { pluginId });
expect(router.pluginId).toBe(pluginId);
});
describe('Options', () => {
it('throws if validation for a route is not defined explicitly', () => {
const router = new Router('', logger, enhanceWithContext, routerOptions);

View file

@ -125,6 +125,9 @@ export interface RouterOptions {
/** Whether we are running in development */
isDev?: boolean;
/** Plugin for which this router was registered */
pluginId?: symbol;
versionedRouterOptions?: {
/** {@inheritdoc VersionedRouterArgs['defaultHandlerResolutionStrategy'] }*/
defaultHandlerResolutionStrategy?: 'newest' | 'oldest' | 'none';
@ -163,6 +166,7 @@ export class Router<Context extends RequestHandlerContextBase = RequestHandlerCo
implements IRouter<Context>
{
public routes: Array<Readonly<InternalRouterRoute>> = [];
public pluginId?: symbol;
public get: InternalRegistrar<'get', Context>;
public post: InternalRegistrar<'post', Context>;
public delete: InternalRegistrar<'delete', Context>;
@ -175,6 +179,7 @@ export class Router<Context extends RequestHandlerContextBase = RequestHandlerCo
private readonly enhanceWithContext: ContextEnhancer<any, any, any, any, any>,
private readonly options: RouterOptions
) {
this.pluginId = options.pluginId;
const buildMethod =
<Method extends RouteMethod>(method: Method) =>
<P, Q, B>(

View file

@ -24,6 +24,12 @@ describe('Versioned router', () => {
expect(versionedRouter.getRoutes()).toHaveLength(3);
});
it('registers pluginId if router has one', () => {
const pluginId = Symbol('test');
const versionedRouter = CoreVersionedRouter.from({ router: createRouter({ pluginId }) });
expect(versionedRouter.pluginId).toBe(pluginId);
});
it('provides the expected metadata', () => {
const versionedRouter = CoreVersionedRouter.from({ router });
versionedRouter.get({ path: '/test/{id}', access: 'internal' });

View file

@ -43,6 +43,7 @@ export interface VersionedRouterArgs {
export class CoreVersionedRouter implements VersionedRouter {
private readonly routes = new Set<CoreVersionedRoute>();
public readonly useVersionResolutionStrategyForInternalPaths: Map<string, boolean> = new Map();
public pluginId?: symbol;
public static from({
router,
defaultHandlerResolutionStrategy,
@ -62,6 +63,7 @@ export class CoreVersionedRouter implements VersionedRouter {
public readonly isDev: boolean = false,
useVersionResolutionStrategyForInternalPaths: string[] = []
) {
this.pluginId = this.router.pluginId;
for (const path of useVersionResolutionStrategyForInternalPaths) {
this.useVersionResolutionStrategyForInternalPaths.set(path, true);
}

View file

@ -8,7 +8,10 @@
import { Router } from '../router';
export function createRouter() {
interface CreateMockRouterOptions {
pluginId?: symbol;
}
export function createRouter(opts: CreateMockRouterOptions = {}) {
return {
delete: jest.fn(),
get: jest.fn(),
@ -19,5 +22,6 @@ export function createRouter() {
patch: jest.fn(),
routerPath: '',
versioned: {} as any,
pluginId: opts.pluginId,
} as unknown as jest.Mocked<Router>;
}

View file

@ -73,6 +73,9 @@ Object {
"valueInBytes": 1048576,
},
"name": "kibana-hostname",
"oas": Object {
"enabled": false,
},
"payloadTimeout": 20000,
"port": 5601,
"requestId": Object {

View file

@ -494,6 +494,11 @@ describe('cors', () => {
});
});
test('oas is disabled by default', () => {
const { oas } = config.schema.validate({});
expect(oas.enabled).toBe(false);
});
describe('versioned', () => {
it('defaults version resolution "oldest" not in dev', () => {
expect(config.schema.validate({}, { dev: undefined })).toMatchObject({

View file

@ -46,7 +46,7 @@ const validHostName = () => {
* We assume the URL does not contain anything after the pathname so that
* we can safely append values to the pathname at runtime.
*/
function validateCdnURL(urlString: string): undefined | string {
export const validateCdnURL = (urlString: string) => {
const cdnURL = new URL(urlString);
const errors: string[] = [];
if (cdnURL.hash.length) {
@ -58,7 +58,7 @@ function validateCdnURL(urlString: string): undefined | string {
if (errors.length) {
return `CDN URL "${cdnURL.href}" is invalid:${EOL}${errors.join(EOL)}`;
}
}
};
const configSchema = schema.object(
{
@ -82,6 +82,9 @@ const configSchema = schema.object(
cdn: schema.object({
url: schema.maybe(schema.uri({ scheme: ['http', 'https'], validate: validateCdnURL })),
}),
oas: schema.object({
enabled: schema.boolean({ defaultValue: false }),
}),
cors: schema.object(
{
enabled: schema.boolean({ defaultValue: false }),
@ -290,6 +293,9 @@ export class HttpConfig implements IHttpConfig {
allowCredentials: boolean;
allowOrigin: string[];
};
public oas: {
enabled: boolean;
};
public securityResponseHeaders: Record<string, string | string[]>;
public customResponseHeaders: Record<string, string | string[]>;
public maxPayload: ByteSizeValue;
@ -363,6 +369,7 @@ export class HttpConfig implements IHttpConfig {
this.restrictInternalApis = rawHttpConfig.restrictInternalApis ?? false;
this.eluMonitor = rawHttpConfig.eluMonitor;
this.versioned = rawHttpConfig.versioned;
this.oas = rawHttpConfig.oas;
}
}

View file

@ -19,14 +19,13 @@ import {
} from '@kbn/server-http-tools';
import type { Duration } from 'moment';
import { firstValueFrom, Observable, Subscription } from 'rxjs';
import { take, pairwise } from 'rxjs';
import { Observable, Subscription, firstValueFrom, pairwise, take } from 'rxjs';
import apm from 'elastic-apm-node';
// @ts-expect-error no type definition
import Brok from 'brok';
import type { Logger, LoggerFactory } from '@kbn/logging';
import type { InternalExecutionContextSetup } from '@kbn/core-execution-context-server-internal';
import { isSafeMethod } from '@kbn/core-http-router-server-internal';
import { CoreVersionedRouter, isSafeMethod, Router } from '@kbn/core-http-router-server-internal';
import type {
IRouter,
RouteConfigOptions,
@ -168,6 +167,11 @@ export interface HttpServerSetupOptions {
executionContext?: InternalExecutionContextSetup;
}
/** @internal */
export interface GetRoutersOptions {
pluginId?: string;
}
export class HttpServer {
private server?: Server;
private config?: HttpConfig;
@ -624,6 +628,38 @@ export class HttpServer {
});
}
public getRouters({ pluginId }: GetRoutersOptions = {}): {
routers: Router[];
versionedRouters: CoreVersionedRouter[];
} {
const routers: {
routers: Router[];
versionedRouters: CoreVersionedRouter[];
} = {
routers: [],
versionedRouters: [],
};
const pluginIdFilter = pluginId ? Symbol(pluginId).toString() : undefined;
for (const router of this.registeredRouters) {
const matchesIdFilter =
!pluginIdFilter || (router as Router).pluginId?.toString() === pluginIdFilter;
if (
matchesIdFilter &&
(router as Router).getRoutes({ excludeVersionedRoutes: true }).length > 0
) {
routers.routers.push(router as Router);
}
const versionedRouter = router.versioned as CoreVersionedRouter;
if (matchesIdFilter && versionedRouter.getRoutes().length > 0) {
routers.versionedRouters.push(versionedRouter);
}
}
return routers;
}
private registerStaticDir(path: string, dirPath: string) {
if (this.server === undefined) {
throw new Error('Http server is not setup up yet');

View file

@ -6,10 +6,11 @@
* Side Public License, v 1.
*/
import { Observable, Subscription, combineLatest, firstValueFrom } from 'rxjs';
import { Observable, Subscription, combineLatest, firstValueFrom, of, mergeMap } from 'rxjs';
import { map } from 'rxjs';
import { pick } from '@kbn/std';
import { pick, Semaphore } from '@kbn/std';
import { generateOpenApiDocument } from '@kbn/router-to-openapispec';
import { Logger } from '@kbn/logging';
import { Env } from '@kbn/config';
import type { CoreContext, CoreService } from '@kbn/core-base-server-internal';
@ -52,6 +53,7 @@ export interface SetupDeps {
export class HttpService
implements CoreService<InternalHttpServiceSetup, InternalHttpServiceStart>
{
private static readonly generateOasSemaphore = new Semaphore(1);
private readonly prebootServer: HttpServer;
private isPrebootServerStopped = false;
private readonly httpServer: HttpServer;
@ -182,6 +184,7 @@ export class HttpService
const router = new Router<Context>(path, this.log, enhanceHandler, {
isDev: this.env.mode.dev,
versionedRouterOptions: getVersionedRouterOptions(config),
pluginId,
});
registerRouter(router);
return router;
@ -222,12 +225,65 @@ export class HttpService
await this.httpsRedirectServer.start(config);
}
if (config.oas.enabled) {
this.log.info('Registering experimental OAS API');
this.registerOasApi(config);
}
await this.httpServer.start();
}
return this.getStartContract();
}
private registerOasApi(config: HttpConfig) {
const basePath = this.internalSetup?.basePath;
const server = this.internalSetup?.server;
if (!basePath || !server) {
throw new Error('Cannot register OAS API before server setup is complete');
}
const baseUrl =
basePath.publicBaseUrl ?? `http://localhost:${config.port}${basePath.serverBasePath}`;
server.route({
path: '/api/oas',
method: 'GET',
handler: async (req, h) => {
const pathStartsWith = req.query?.pathStartsWith;
const pluginId = req.query?.pluginId;
return await firstValueFrom(
of(1).pipe(
HttpService.generateOasSemaphore.acquire(),
mergeMap(async () => {
try {
// Potentially quite expensive
const result = generateOpenApiDocument(this.httpServer.getRouters({ pluginId }), {
baseUrl,
title: 'Kibana HTTP APIs',
version: '0.0.0', // TODO get a better version here
pathStartsWith,
});
return h.response(result);
} catch (e) {
this.log.error(e);
return h.response({ message: e.message }).code(500);
}
})
)
);
},
options: {
app: { access: 'public' },
auth: false,
cache: {
privacy: 'public',
otherwise: 'must-revalidate',
},
},
});
}
/**
* Indicates if http server is configured to start listening on a configured port.
* (if `server.autoListen` is not explicitly set to `false`.)

View file

@ -33,6 +33,7 @@
"@kbn/core-execution-context-server-mocks",
"@kbn/core-http-context-server-mocks",
"@kbn/logging-mocks",
"@kbn/router-to-openapispec",
"@kbn/core-base-server-mocks",
],
"exclude": [

View file

@ -17,4 +17,4 @@ export type {
InternalHttpServiceSetupMock,
InternalHttpServiceStartMock,
} from './src/http_service.mock';
export { createCoreContext, createHttpServer, createConfigService } from './src/test_utils';
export { createCoreContext, createHttpService, createConfigService } from './src/test_utils';

View file

@ -19,6 +19,7 @@ import {
type ExternalUrlConfigType,
type CspConfigType,
HttpService,
config,
} from '@kbn/core-http-server-internal';
const coreId = Symbol('core');
@ -38,38 +39,43 @@ export const createConfigService = ({
const configService = configServiceMock.create();
configService.atPath.mockImplementation((path) => {
if (path === 'server') {
return new BehaviorSubject({
name: 'kibana',
hosts: ['localhost'],
maxPayload: new ByteSizeValue(1024),
autoListen: true,
ssl: {
enabled: false,
},
cors: {
enabled: false,
},
compression: { enabled: true, brotli: { enabled: false } },
xsrf: {
disableProtection: true,
allowlist: [],
},
securityResponseHeaders: {},
customResponseHeaders: {},
requestId: {
allowFromAnyIp: true,
ipAllowlist: [],
},
shutdownTimeout: moment.duration(30, 'seconds'),
keepaliveTimeout: 120_000,
socketTimeout: 120_000,
restrictInternalApis: false,
versioned: {
versionResolution: 'oldest',
strictClientVersionCheck: true,
},
...server,
} as any);
return new BehaviorSubject(
Object.assign(
config.schema.validate({}),
{
name: 'kibana',
hosts: ['localhost'],
maxPayload: new ByteSizeValue(1024),
autoListen: true,
ssl: {
enabled: false,
},
cors: {
enabled: false,
},
compression: { enabled: true, brotli: { enabled: false } },
xsrf: {
disableProtection: true,
allowlist: [],
},
securityResponseHeaders: {},
customResponseHeaders: {},
requestId: {
allowFromAnyIp: true,
ipAllowlist: [],
},
shutdownTimeout: moment.duration(30, 'seconds'),
keepaliveTimeout: 120_000,
socketTimeout: 120_000,
restrictInternalApis: false,
versioned: {
versionResolution: 'oldest',
strictClientVersionCheck: true,
},
},
server
)
);
}
if (path === 'externalUrl') {
return new BehaviorSubject({
@ -105,9 +111,9 @@ export const createCoreContext = (overrides: Partial<CoreContext> = {}): CoreCon
});
/**
* Creates a concrete HttpServer with a mocked context.
* Creates a concrete HttpService with a mocked context.
*/
export const createHttpServer = ({
export const createHttpService = ({
buildNum,
...overrides
}: Partial<CoreContext & { buildNum: number }> = {}): HttpService => {

View file

@ -8,7 +8,7 @@
import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks';
import { ContextService } from '@kbn/core-http-context-server-internal';
import { createHttpServer, createCoreContext } from '@kbn/core-http-server-mocks';
import { createHttpService, createCoreContext } from '@kbn/core-http-server-mocks';
import { contextServiceMock } from '@kbn/core-http-context-server-mocks';
import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks';
import { typeRegistryMock } from '@kbn/core-saved-objects-base-server-mocks';
@ -43,7 +43,7 @@ export const setupServer = async (coreId: symbol = defaultCoreId) => {
const coreContext = createCoreContext({ coreId });
const contextService = new ContextService(coreContext);
const server = createHttpServer(coreContext);
const server = createHttpService(coreContext);
await server.preboot({ context: contextServiceMock.createPrebootContract() });
const httpSetup = await server.setup({
context: contextService.setup({ pluginDependencies: new Map() }),

View file

@ -9,6 +9,17 @@ Object {
"type": "string",
},
},
"securitySchemes": Object {
"apiKeyAuth": Object {
"in": "header",
"name": "Authorization",
"type": "apiKey",
},
"basicAuth": Object {
"scheme": "basic",
"type": "http",
},
},
},
"externalDocs": undefined,
"info": Object {
@ -87,9 +98,6 @@ Object {
Object {
"basicAuth": Array [],
},
Object {
"apiKeyAuth": Array [],
},
],
"servers": Array [
Object {
@ -104,6 +112,17 @@ exports[`generateOpenApiDocument @kbn/config-schema generates the expected OpenA
Object {
"components": Object {
"schemas": Object {},
"securitySchemes": Object {
"apiKeyAuth": Object {
"in": "header",
"name": "Authorization",
"type": "apiKey",
},
"basicAuth": Object {
"scheme": "basic",
"type": "http",
},
},
},
"externalDocs": undefined,
"info": Object {
@ -334,9 +353,6 @@ Object {
Object {
"basicAuth": Array [],
},
Object {
"apiKeyAuth": Array [],
},
],
"servers": Array [
Object {

View file

@ -68,15 +68,21 @@ export const generateOpenApiDocument = (
},
],
paths,
components: converter.getSchemaComponents(),
security: [
{
basicAuth: [],
components: {
...converter.getSchemaComponents(),
securitySchemes: {
basicAuth: {
type: 'http',
scheme: 'basic',
},
apiKeyAuth: {
type: 'apiKey',
in: 'header',
name: 'Authorization',
},
},
{
apiKeyAuth: [],
},
],
},
security: [{ basicAuth: [] }],
tags: opts.tags?.map((tag) => ({ name: tag })),
externalDocs: opts.docsUrl ? { url: opts.docsUrl } : undefined,
};

View file

@ -18,7 +18,7 @@ import {
InternalHttpServicePreboot,
InternalHttpServiceSetup,
} from '@kbn/core-http-server-internal';
import { createHttpServer } from '@kbn/core-http-server-mocks';
import { createHttpService } from '@kbn/core-http-server-mocks';
import type { CapabilitiesSetup } from '@kbn/core-capabilities-server';
import { CapabilitiesService } from '@kbn/core-capabilities-server-internal';
@ -35,7 +35,7 @@ describe('CapabilitiesService', () => {
let serviceSetup: CapabilitiesSetup;
beforeEach(async () => {
server = createHttpServer();
server = createHttpService();
httpPreboot = await server.preboot({ context: contextServiceMock.createPrebootContract() });
httpSetup = await server.setup({
context: contextServiceMock.createSetupContract(),

View file

@ -14,7 +14,7 @@ import { executionContextServiceMock } from '@kbn/core-execution-context-server-
import { contextServiceMock } from '@kbn/core-http-context-server-mocks';
import type { IRouter } from '@kbn/core-http-server';
import { HttpService } from '@kbn/core-http-server-internal';
import { createHttpServer } from '@kbn/core-http-server-mocks';
import { createHttpService } from '@kbn/core-http-server-mocks';
import { registerRouteForBundle, FileHashCache } from '@kbn/core-apps-server-internal';
const buildHash = 'buildHash';
@ -31,7 +31,7 @@ describe('bundle routes', () => {
logger = loggingSystemMock.create();
fileHashCache = new FileHashCache();
server = createHttpServer({ logger });
server = createHttpService({ logger });
await server.preboot({ context: contextServiceMock.createPrebootContract() });
});

View file

@ -14,7 +14,7 @@ import { executionContextServiceMock } from '@kbn/core-execution-context-server-
import { contextServiceMock } from '@kbn/core-http-context-server-mocks';
import { ensureRawRequest } from '@kbn/core-http-router-server-internal';
import { HttpService } from '@kbn/core-http-server-internal';
import { createHttpServer } from '@kbn/core-http-server-mocks';
import { createHttpService } from '@kbn/core-http-server-mocks';
let server: HttpService;
@ -29,7 +29,7 @@ const setupDeps = {
beforeEach(async () => {
logger = loggingSystemMock.create();
server = createHttpServer({ logger });
server = createHttpService({ logger });
await server.preboot({ context: contextServiceMock.createPrebootContract() });
});

View file

@ -10,7 +10,7 @@ import supertest from 'supertest';
import { kibanaPackageJson } from '@kbn/repo-info';
import type { IRouter, RouteRegistrar } from '@kbn/core-http-server';
import { contextServiceMock } from '@kbn/core-http-context-server-mocks';
import { createConfigService, createHttpServer } from '@kbn/core-http-server-mocks';
import { createConfigService, createHttpService } from '@kbn/core-http-server-mocks';
import { HttpService, HttpServerSetup } from '@kbn/core-http-server-internal';
import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks';
import { schema } from '@kbn/config-schema';
@ -61,7 +61,7 @@ describe('core lifecycle handlers', () => {
beforeEach(async () => {
const configService = createConfigService(testConfig);
logger = loggerMock.create();
server = createHttpServer({ configService, logger });
server = createHttpService({ configService, logger });
await server.preboot({ context: contextServiceMock.createPrebootContract() });
const serverSetup = await server.setup(setupDeps);
router = serverSetup.createRouter('/');
@ -243,7 +243,7 @@ describe('core lifecycle handlers', () => {
restrictInternalApis: true,
},
});
server = createHttpServer({ configService });
server = createHttpService({ configService });
await server.preboot({ context: contextServiceMock.createPrebootContract() });
const serverSetup = await server.setup(setupDeps);
router = serverSetup.createRouter('/');
@ -317,7 +317,7 @@ describe('core lifecycle handlers with restrict internal routes enforced', () =>
beforeEach(async () => {
const configService = createConfigService({ server: { restrictInternalApis: true } });
server = createHttpServer({ configService });
server = createHttpService({ configService });
await server.preboot({ context: contextServiceMock.createPrebootContract() });
const serverSetup = await server.setup(setupDeps);
@ -382,7 +382,7 @@ describe('core lifecycle handlers with no strict client version check', () => {
},
},
});
server = createHttpServer({ configService, logger, buildNum: 1234 });
server = createHttpService({ configService, logger, buildNum: 1234 });
await server.preboot({ context: contextServiceMock.createPrebootContract() });
const serverSetup = await server.setup(setupDeps);
router = serverSetup.createRouter('/');

View file

@ -0,0 +1,164 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/
import supertest from 'supertest';
import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks';
import { contextServiceMock } from '@kbn/core-http-context-server-mocks';
import { createConfigService, createHttpService } from '@kbn/core-http-server-mocks';
import type {
InternalContextPreboot,
InternalContextSetup,
} from '@kbn/core-http-context-server-internal';
import { InternalExecutionContextSetup } from '@kbn/core-execution-context-server-internal';
import { IRouter } from '@kbn/core-http-server';
let prebootDeps: {
context: jest.Mocked<InternalContextPreboot>;
};
let setupDeps: {
context: jest.Mocked<InternalContextSetup>;
executionContext: jest.Mocked<InternalExecutionContextSetup>;
};
beforeEach(async () => {
prebootDeps = {
context: contextServiceMock.createPrebootContract(),
};
const contextSetup = contextServiceMock.createSetupContract();
setupDeps = {
context: contextSetup,
executionContext: executionContextServiceMock.createInternalSetupContract(),
};
});
let httpService: ReturnType<typeof createHttpService>;
type ConfigServiceArgs = Parameters<typeof createConfigService>[0];
async function startService(
args: {
config?: ConfigServiceArgs;
createRoutes?: (getRouter: (pluginId?: symbol) => IRouter) => void;
} = {}
) {
httpService = createHttpService({
configService: createConfigService(args.config),
});
await httpService.preboot(prebootDeps);
const { server: innerServer, createRouter } = await httpService.setup(setupDeps);
if (args.createRoutes) {
args.createRoutes((pluginId) => createRouter('/', pluginId));
}
await httpService.start();
return {
listener: innerServer.listener,
};
}
async function stopService() {
await httpService?.stop();
}
afterEach(async () => {
await stopService();
});
it('is disabled by default', async () => {
const server = await startService();
supertest(server.listener).get('/api/oas').expect(404);
});
it('handles requests when enabled', async () => {
const server = await startService({ config: { server: { oas: { enabled: true } } } });
const result = await supertest(server.listener).get('/api/oas');
expect(result.status).toBe(200);
});
it.each([
{
queryParam: { pathStartsWith: '/api/include-test' },
includes: {
paths: {
'/api/include-test': {
get: {},
post: {},
},
'/api/include-test/{id}': {},
},
},
excludes: {
paths: {
'/my-other-plugin': {},
},
},
},
{
queryParam: { pluginId: 'myPlugin' },
includes: {
paths: {
'/api/include-test': {
get: {},
post: {},
},
'/api/include-test/{id}': {},
},
},
excludes: {
paths: {
'/my-other-plugin': {},
},
},
},
{
queryParam: { pluginId: 'nonExistant' },
includes: {},
excludes: {
paths: {
'/my-include-test': {},
'/my-other-plugin': {},
},
},
},
{
queryParam: { pluginId: 'myOtherPlugin', pathStartsWith: '/api/my-other-plugin' },
includes: {
paths: {
'/api/my-other-plugin': {
get: {},
post: {},
put: {},
},
},
},
excludes: {
paths: {
'/my-include-test': {},
},
},
},
])(
'can filter paths based on query params $queryParam',
async ({ queryParam, includes, excludes }) => {
const server = await startService({
config: { server: { oas: { enabled: true } } },
createRoutes: (getRouter) => {
const router1 = getRouter(Symbol('myPlugin'));
router1.get({ path: '/api/include-test', validate: false }, (_, __, res) => res.ok());
router1.post({ path: '/api/include-test', validate: false }, (_, __, res) => res.ok());
router1.get({ path: '/api/include-test/{id}', validate: false }, (_, __, res) => res.ok());
router1.get({ path: '/api/exclude-test', validate: false }, (_, __, res) => res.ok());
const router2 = getRouter(Symbol('myOtherPlugin'));
router2.get({ path: '/api/my-other-plugin', validate: false }, (_, __, res) => res.ok());
router2.post({ path: '/api/my-other-plugin', validate: false }, (_, __, res) => res.ok());
router2.put({ path: '/api/my-other-plugin', validate: false }, (_, __, res) => res.ok());
},
});
const result = await supertest(server.listener).get('/api/oas').query(queryParam);
expect(result.status).toBe(200);
expect(result.body).toMatchObject(includes);
expect(result.body).not.toMatchObject(excludes);
}
);

View file

@ -11,7 +11,7 @@ import supertest from 'supertest';
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks';
import { contextServiceMock } from '@kbn/core-http-context-server-mocks';
import { createHttpServer } from '@kbn/core-http-server-mocks';
import { createHttpService } from '@kbn/core-http-server-mocks';
import { HttpService } from '@kbn/core-http-server-internal';
let server: HttpService;
@ -24,7 +24,7 @@ const setupDeps = {
};
beforeEach(async () => {
server = createHttpServer({ logger: loggingSystemMock.create() });
server = createHttpService({ logger: loggingSystemMock.create() });
});
afterEach(async () => {

View file

@ -15,7 +15,7 @@ import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks';
import { contextServiceMock } from '@kbn/core-http-context-server-mocks';
import type { HttpService } from '@kbn/core-http-server-internal';
import { createHttpServer } from '@kbn/core-http-server-mocks';
import { createHttpService } from '@kbn/core-http-server-mocks';
import { schema } from '@kbn/config-schema';
let server: HttpService;
@ -31,7 +31,7 @@ const setupDeps = {
beforeEach(async () => {
logger = loggingSystemMock.create();
server = createHttpServer({ logger });
server = createHttpService({ logger });
await server.preboot({ context: contextServiceMock.createPrebootContract() });
});

View file

@ -16,7 +16,7 @@ import { executionContextServiceMock } from '@kbn/core-execution-context-server-
import { contextServiceMock } from '@kbn/core-http-context-server-mocks';
import type { HttpService } from '@kbn/core-http-server-internal';
import { ensureRawRequest } from '@kbn/core-http-router-server-internal';
import { createHttpServer } from '@kbn/core-http-server-mocks';
import { createHttpService } from '@kbn/core-http-server-mocks';
import { inspect } from 'util';
let server: HttpService;
@ -32,7 +32,7 @@ const setupDeps = {
beforeEach(async () => {
logger = loggingSystemMock.create();
server = createHttpServer({ logger });
server = createHttpService({ logger });
await server.preboot({ context: contextServiceMock.createPrebootContract() });
});

View file

@ -17,7 +17,7 @@ import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks';
import { contextServiceMock } from '@kbn/core-http-context-server-mocks';
import { Router } from '@kbn/core-http-router-server-internal';
import { createHttpServer } from '@kbn/core-http-server-mocks';
import { createHttpService } from '@kbn/core-http-server-mocks';
import type { HttpService } from '@kbn/core-http-server-internal';
import { loggerMock } from '@kbn/logging-mocks';
@ -32,7 +32,7 @@ const setupDeps = {
beforeEach(async () => {
logger = loggingSystemMock.create();
server = createHttpServer({ logger });
server = createHttpService({ logger });
await server.preboot({ context: contextServiceMock.createPrebootContract() });
});

View file

@ -14,7 +14,7 @@ import { schema } from '@kbn/config-schema';
import { loggingSystemMock } from '@kbn/core-logging-server-mocks';
import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks';
import { contextServiceMock } from '@kbn/core-http-context-server-mocks';
import { createHttpServer, createConfigService } from '@kbn/core-http-server-mocks';
import { createHttpService, createConfigService } from '@kbn/core-http-server-mocks';
import type { HttpConfigType, HttpService } from '@kbn/core-http-server-internal';
import type { IRouter } from '@kbn/core-http-server';
import type { CliArgs } from '@kbn/config';
@ -42,7 +42,7 @@ describe('Routing versioned requests', () => {
options.useVersionResolutionStrategyForInternalPaths ?? [],
},
};
server = createHttpServer({
server = createHttpService({
logger,
env: createTestEnv({ envOptions: getEnvOptions({ cliArgs }) }),
configService: createConfigService({

View file

@ -10,7 +10,7 @@ import { BehaviorSubject, Subject } from 'rxjs';
import { take, filter } from 'rxjs';
import supertest from 'supertest';
import { Server as HapiServer } from '@hapi/hapi';
import { createHttpServer } from '@kbn/core-http-server-mocks';
import { createHttpService } from '@kbn/core-http-server-mocks';
import type { IRouter } from '@kbn/core-http-server';
import { contextServiceMock } from '@kbn/core-http-context-server-mocks';
import type { HttpService } from '@kbn/core-http-server-internal';
@ -28,7 +28,7 @@ describe('ServerMetricsCollector', () => {
const sendGet = (path: string) => supertest(hapiServer.listener).get(path);
beforeEach(async () => {
server = createHttpServer();
server = createHttpService();
await server.preboot({ context: contextServiceMock.createPrebootContract() });
const contextSetup = contextServiceMock.createSetupContract();
const httpSetup = await server.setup({

View file

@ -9,7 +9,7 @@
import supertest from 'supertest';
import { ContextService } from '@kbn/core-http-context-server-internal';
import type { HttpService, InternalHttpServiceSetup } from '@kbn/core-http-server-internal';
import { createHttpServer, createCoreContext } from '@kbn/core-http-server-mocks';
import { createHttpService, createCoreContext } from '@kbn/core-http-server-mocks';
import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks';
import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks';
import type { ICoreUsageStatsClient } from '@kbn/core-usage-data-base-server-internal';
@ -42,7 +42,7 @@ describe('GET /api/saved_objects/{type}/{id} with allowApiAccess true', () => {
beforeEach(async () => {
const coreContext = createCoreContext({ coreId });
server = createHttpServer(coreContext);
server = createHttpService(coreContext);
await server.preboot({ context: contextServiceMock.createPrebootContract() });
const contextService = new ContextService(coreContext);

View file

@ -9,7 +9,7 @@
import supertest from 'supertest';
import { ContextService } from '@kbn/core-http-context-server-internal';
import type { HttpService, InternalHttpServiceSetup } from '@kbn/core-http-server-internal';
import { createHttpServer, createCoreContext } from '@kbn/core-http-server-mocks';
import { createHttpService, createCoreContext } from '@kbn/core-http-server-mocks';
import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks';
import type { ICoreUsageStatsClient } from '@kbn/core-usage-data-base-server-internal';
import {
@ -43,7 +43,7 @@ describe('GET /api/saved_objects/resolve/{type}/{id} with allowApiAccess true',
beforeEach(async () => {
const coreContext = createCoreContext({ coreId });
server = createHttpServer(coreContext);
server = createHttpService(coreContext);
await server.preboot({ context: contextServiceMock.createPrebootContract() });
const contextService = new ContextService(coreContext);

View file

@ -9,7 +9,7 @@
import supertest from 'supertest';
import { ContextService } from '@kbn/core-http-context-server-internal';
import type { HttpService, InternalHttpServiceSetup } from '@kbn/core-http-server-internal';
import { createHttpServer, createCoreContext } from '@kbn/core-http-server-mocks';
import { createHttpService, createCoreContext } from '@kbn/core-http-server-mocks';
import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks';
import { executionContextServiceMock } from '@kbn/core-execution-context-server-mocks';
import type { ICoreUsageStatsClient } from '@kbn/core-usage-data-base-server-internal';
@ -43,7 +43,7 @@ describe('GET /api/saved_objects/{type}/{id}', () => {
beforeEach(async () => {
const coreContext = createCoreContext({ coreId });
server = createHttpServer(coreContext);
server = createHttpService(coreContext);
await server.preboot({ context: contextServiceMock.createPrebootContract() });
const contextService = new ContextService(coreContext);

View file

@ -9,7 +9,7 @@
import supertest from 'supertest';
import { ContextService } from '@kbn/core-http-context-server-internal';
import type { HttpService, InternalHttpServiceSetup } from '@kbn/core-http-server-internal';
import { createHttpServer, createCoreContext } from '@kbn/core-http-server-mocks';
import { createHttpService, createCoreContext } from '@kbn/core-http-server-mocks';
import { savedObjectsClientMock } from '@kbn/core-saved-objects-api-server-mocks';
import type { ICoreUsageStatsClient } from '@kbn/core-usage-data-base-server-internal';
import {
@ -44,7 +44,7 @@ describe('GET /api/saved_objects/resolve/{type}/{id}', () => {
beforeEach(async () => {
const coreContext = createCoreContext({ coreId });
server = createHttpServer(coreContext);
server = createHttpService(coreContext);
await server.preboot({ context: contextServiceMock.createPrebootContract() });
const contextService = new ContextService(coreContext);

View file

@ -7,7 +7,7 @@
*/
import supertest from 'supertest';
import { createCoreContext, createHttpServer } from '@kbn/core-http-server-mocks';
import { createCoreContext, createHttpService } from '@kbn/core-http-server-mocks';
import type { HttpService, InternalHttpServicePreboot } from '@kbn/core-http-server-internal';
import { contextServiceMock } from '@kbn/core-http-context-server-mocks';
@ -22,7 +22,7 @@ describe('GET /api/status', () => {
const setupServer = async () => {
const coreContext = createCoreContext({ coreId });
server = createHttpServer(coreContext);
server = createHttpService(coreContext);
httpPreboot = await server.preboot({
context: contextServiceMock.createPrebootContract(),
});

View file

@ -11,7 +11,7 @@ import supertest from 'supertest';
import { omit } from 'lodash';
import { ContextService } from '@kbn/core-http-context-server-internal';
import { createCoreContext, createHttpServer } from '@kbn/core-http-server-mocks';
import { createCoreContext, createHttpService } from '@kbn/core-http-server-mocks';
import type { HttpService, InternalHttpServiceSetup } from '@kbn/core-http-server-internal';
import { metricsServiceMock } from '@kbn/core-metrics-server-mocks';
import type { MetricsServiceSetup } from '@kbn/core-metrics-server';
@ -48,7 +48,7 @@ describe('GET /api/status', () => {
const coreContext = createCoreContext({ coreId });
const contextService = new ContextService(coreContext);
server = createHttpServer(coreContext);
server = createHttpService(coreContext);
await server.preboot({ context: contextServiceMock.createPrebootContract() });
httpSetup = await server.setup({
context: contextService.setup({ pluginDependencies: new Map() }),

View file

@ -163,6 +163,7 @@
"@kbn/core-security-server-mocks",
"@kbn/core-security-browser",
"@kbn/core-security-browser-mocks",
"@kbn/core-execution-context-server-internal",
],
"exclude": [
"target/**/*",

View file

@ -20,12 +20,12 @@ import {
metricsServiceMock,
executionContextServiceMock,
} from '@kbn/core/server/mocks';
import { createHttpServer } from '@kbn/core-http-server-mocks';
import { createHttpService } from '@kbn/core-http-server-mocks';
import { registerStatsRoute } from '../stats';
import supertest from 'supertest';
import { CollectorSet } from '../../collector';
type HttpService = ReturnType<typeof createHttpServer>;
type HttpService = ReturnType<typeof createHttpService>;
type HttpSetup = Awaited<ReturnType<HttpService['setup']>>;
describe('/api/stats', () => {
@ -35,7 +35,7 @@ describe('/api/stats', () => {
let metrics: MetricsServiceSetup;
beforeEach(async () => {
server = createHttpServer();
server = createHttpService();
await server.preboot({ context: contextServiceMock.createPrebootContract() });
httpSetup = await server.setup({
context: contextServiceMock.createSetupContract(),

View file

@ -6,15 +6,15 @@
*/
import { setTimeout as setTimeoutPromise } from 'timers/promises';
import { contextServiceMock, executionContextServiceMock } from '@kbn/core/server/mocks';
import { createHttpServer } from '@kbn/core-http-server-mocks';
import { createHttpService } from '@kbn/core-http-server-mocks';
import supertest from 'supertest';
import { APMEventClient } from '.';
describe('APMEventClient', () => {
let server: ReturnType<typeof createHttpServer>;
let server: ReturnType<typeof createHttpService>;
beforeEach(() => {
server = createHttpServer();
server = createHttpService();
});
afterEach(async () => {