From dc51e5b3b66293889a3c11457aea769a6ee73748 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Tue, 6 Aug 2019 14:22:50 +0200 Subject: [PATCH 1/8] expose createRouter, prepare route handler for context introduction. --- src/core/server/http/http_server.ts | 38 ++--- src/core/server/http/http_service.ts | 34 +++- src/core/server/http/index.ts | 2 +- src/core/server/http/router/index.ts | 2 +- src/core/server/http/router/router.ts | 189 +++++++++++++++------- src/core/server/index.ts | 7 +- src/core/server/plugins/plugin_context.ts | 3 + src/core/server/server.ts | 8 +- 8 files changed, 192 insertions(+), 91 deletions(-) diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index d90fb880a581c2..1d395c5c301c97 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -17,7 +17,7 @@ * under the License. */ -import { Request, Server, ResponseToolkit } from 'hapi'; +import { Request, Server } from 'hapi'; import { Logger, LoggerFactory } from '../logging'; import { HttpConfig } from './http_config'; @@ -25,7 +25,8 @@ import { createServer, getListenerOptions, getServerOptions } from './http_tools import { adoptToHapiAuthFormat, AuthenticationHandler } from './lifecycle/auth'; import { adoptToHapiOnPostAuthFormat, OnPostAuthHandler } from './lifecycle/on_post_auth'; import { adoptToHapiOnPreAuthFormat, OnPreAuthHandler } from './lifecycle/on_pre_auth'; -import { KibanaRequest, LegacyRequest, ResponseHeaders, Router } from './router'; + +import { KibanaRequest, LegacyRequest, ResponseHeaders, IRouter } from './router'; import { SessionStorageCookieOptions, createCookieSessionStorageFactory, @@ -95,7 +96,7 @@ import { BasePath } from './base_path_service'; * path: 'path/{id}', * validate * }, - * async (request, response) => { + * async (context, request, response) => { * const data = await findObject(request.params.id); * if (!data) return response.notFound(); * return response.ok(data, { @@ -111,9 +112,9 @@ export interface HttpServerSetup { server: Server; /** * Add all the routes registered with `router` to HTTP server request listeners. - * @param router {@link Router} - a router with registered route handlers. + * @param router {@link IRouter} - a router with registered route handlers. */ - registerRouter: (router: Router) => void; + registerRouter: (router: IRouter) => void; /** * Creates cookie based session storage factory {@link SessionStorageFactory} * @param cookieOptions {@link SessionStorageCookieOptions} - options to configure created cookie session storage. @@ -179,7 +180,7 @@ export interface HttpServerSetup { export class HttpServer { private server?: Server; private config?: HttpConfig; - private registeredRouters = new Set(); + private registeredRouters = new Set(); private authRegistered = false; private cookieSessionStorageCreated = false; @@ -199,12 +200,11 @@ export class HttpServer { return this.server !== undefined && this.server.listener.listening; } - private registerRouter(router: Router) { + private registerRouter(router: IRouter) { if (this.isListening()) { throw new Error('Routers can be registered only when HTTP server is stopped.'); } - this.log.debug(`registering route handler for [${router.path}]`); this.registeredRouters.add(router); } @@ -246,12 +246,12 @@ export class HttpServer { for (const router of this.registeredRouters) { for (const route of router.getRoutes()) { + this.log.debug(`registering route handler for [${route.path}]`); const { authRequired = true, tags } = route.options; this.server.route({ - handler: (req: Request, responseToolkit: ResponseToolkit) => - route.handler(req, responseToolkit, this.log), + handler: route.handler, method: route.method, - path: this.getRouteFullPath(router.path, route.path), + path: route.path, options: { auth: authRequired ? undefined : false, tags: tags ? Array.from(tags) : undefined, @@ -261,9 +261,12 @@ export class HttpServer { } await this.server.start(); - const serverPath = this.config!.rewriteBasePath || this.config!.basePath || ''; - this.log.info('http server running'); - this.log.debug(`http server listening on ${this.server.info.uri}${serverPath}`); + const serverPath = + this.config && this.config.rewriteBasePath && this.config.basePath !== undefined + ? this.config.basePath + : ''; + + this.log.info(`http server running at ${this.server.info.uri}${serverPath}`); } public async stop() { @@ -292,13 +295,6 @@ export class HttpServer { }); } - private getRouteFullPath(routerPath: string, routePath: string) { - // If router's path ends with slash and route's path starts with slash, - // we should omit one of them to have a valid concatenated path. - const routePathStartIndex = routerPath.endsWith('/') && routePath.startsWith('/') ? 1 : 0; - return `${routerPath}${routePath.slice(routePathStartIndex)}`; - } - private registerOnPostAuth(fn: OnPostAuthHandler) { if (this.server === undefined) { throw new Error('Server is not created yet'); diff --git a/src/core/server/http/http_service.ts b/src/core/server/http/http_service.ts index e69906d512bacc..4013db490cc49e 100644 --- a/src/core/server/http/http_service.ts +++ b/src/core/server/http/http_service.ts @@ -23,14 +23,33 @@ import { Server } from 'hapi'; import { LoggerFactory } from '../logging'; import { CoreService } from '../../types'; + import { Logger } from '../logging'; import { CoreContext } from '../core_context'; + +import { Router, IRouter } from './router'; import { HttpConfig, HttpConfigType } from './http_config'; import { HttpServer, HttpServerSetup } from './http_server'; import { HttpsRedirectServer } from './https_redirect_server'; /** @public */ -export type HttpServiceSetup = HttpServerSetup; +export type HttpServiceSetup = HttpServerSetup & { + /** + * Provides ability to declare a handler function for a particular path and HTTP request method. + * Each route can have only one handler functions, which is executed when the route is matched. + * All routes are prefixed with plugin name as a first segment of URL path. + * @example + * ```ts + * const router = createRouter(); + * // handler is called when '${my-plugin-id}/path' resource is requested with `GET` method + * router.get({ path: '/path', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); + * ``` + * + * @internal + * */ + createRouter: (path: string) => IRouter; +}; + /** @public */ export interface HttpServiceStart { /** Indicates if http server is listening on a given port */ @@ -78,7 +97,16 @@ export class HttpService implements CoreService { + return new Router(path, this.log); + }, + }; + + return contract; } public async start() { @@ -141,7 +169,7 @@ export class HttpService implements CoreService { - this.log.debug(`Kibana server is not ready yet ${req.method}:${req.url}.`); + this.log.debug(`Kibana server is not ready yet ${req.method}:${req.url.href}.`); // If server is not ready yet, because plugins or core can perform // long running tasks (build assets, saved objects migrations etc.) diff --git a/src/core/server/http/index.ts b/src/core/server/http/index.ts index 4b2bb2b6b86bfc..7c18c61c605bfb 100644 --- a/src/core/server/http/index.ts +++ b/src/core/server/http/index.ts @@ -38,7 +38,7 @@ export { kibanaResponseFactory, KibanaResponseFactory, RouteConfig, - Router, + IRouter, RouteMethod, RouteConfigOptions, } from './router'; diff --git a/src/core/server/http/router/index.ts b/src/core/server/http/router/index.ts index f9009949825ba3..96b2b57cccdb8b 100644 --- a/src/core/server/http/router/index.ts +++ b/src/core/server/http/router/index.ts @@ -18,7 +18,7 @@ */ export { Headers, filterHeaders, ResponseHeaders, KnownHeaders } from './headers'; -export { Router, RequestHandler } from './router'; +export { Router, RequestHandler, IRouter } from './router'; export { KibanaRequest, KibanaRequestRoute, diff --git a/src/core/server/http/router/router.ts b/src/core/server/http/router/router.ts index a4d4b62e40203b..35f8e726d29dac 100644 --- a/src/core/server/http/router/router.ts +++ b/src/core/server/http/router/router.ts @@ -30,54 +30,105 @@ interface RouterRoute { method: RouteMethod; path: string; options: RouteConfigOptions; - handler: (req: Request, responseToolkit: ResponseToolkit, log: Logger) => Promise; + handler: (req: Request, responseToolkit: ResponseToolkit) => Promise; } -/** - * Provides ability to declare a handler function for a particular path and HTTP request method. - * Each route can have only one handler functions, which is executed when the route is matched. - * - * @example - * ```ts - * const router = new Router('my-app'); - * // handler is called when 'my-app/path' resource is requested with `GET` method - * router.get({ path: '/path', validate: false }, (req, res) => res.ok({ content: 'ok' })); - * ``` - * - * @public - * */ -export class Router { - public routes: Array> = []; +export interface IRouter { /** - * @param path - a router path, set as the very first path segment for all registered routes. + * Resulted path */ - constructor(readonly path: string) {} + routerPath: string; /** * Register a route handler for `GET` request. * @param route {@link RouteConfig} - a route configuration. * @param handler {@link RequestHandler} - a function to call to respond to an incoming request */ + get:

( + route: RouteConfig, + handler: RequestHandler + ) => void; + /** + * Register a route handler for `POST` request. + * @param route {@link RouteConfig} - a route configuration. + * @param handler {@link RequestHandler} - a function to call to respond to an incoming request + */ + post:

( + route: RouteConfig, + handler: RequestHandler + ) => void; + /** + * Register a route handler for `PUT` request. + * @param route {@link RouteConfig} - a route configuration. + * @param handler {@link RequestHandler} - a function to call to respond to an incoming request + */ + put:

( + route: RouteConfig, + handler: RequestHandler + ) => void; + + /** + * Register a route handler for `DELETE` request. + * @param route {@link RouteConfig} - a route configuration. + * @param handler {@link RequestHandler} - a function to call to respond to an incoming request + */ + delete:

( + route: RouteConfig, + handler: RequestHandler + ) => void; + /** + * Returns all routes registered with the this router. + * @returns List of registered routes. + * @internal + */ + getRoutes: () => RouterRoute[]; +} + +export type ContextEnhancer

= ( + handler: RequestHandler +) => RequestHandlerEnhanced; + +function getRouteFullPath(routerPath: string, routePath: string) { + // If router's path ends with slash and route's path starts with slash, + // we should omit one of them to have a valid concatenated path. + const routePathStartIndex = routerPath.endsWith('/') && routePath.startsWith('/') ? 1 : 0; + return `${routerPath}${routePath.slice(routePathStartIndex)}`; +} + +/** + * @internal + */ +export class Router implements IRouter { + public routes: Array> = []; + + constructor( + readonly routerPath: string, + private readonly log: Logger, + private readonly enhanceWithContext: ContextEnhancer = fn => fn.bind(null, {}) + ) {} + public get

( route: RouteConfig, handler: RequestHandler ) { const { path, options = {} } = route; const routeSchemas = this.routeSchemasFromRouteConfig(route, 'get'); + this.routes.push({ - handler: async (req, responseToolkit, log) => - await this.handle(routeSchemas, req, responseToolkit, handler, log), + handler: async (req, responseToolkit) => { + return await this.handle({ + routeSchemas, + request: req, + responseToolkit, + handler: this.enhanceWithContext(handler), + }); + }, method: 'get', - path, + path: getRouteFullPath(this.routerPath, path), options, }); } - /** - * Register a route handler for `POST` request. - * @param route {@link RouteConfig} - a route configuration. - * @param handler {@link RequestHandler} - a function to call to respond to an incoming request - */ public post

( route: RouteConfig, handler: RequestHandler @@ -85,19 +136,20 @@ export class Router { const { path, options = {} } = route; const routeSchemas = this.routeSchemasFromRouteConfig(route, 'post'); this.routes.push({ - handler: async (req, responseToolkit, log) => - await this.handle(routeSchemas, req, responseToolkit, handler, log), + handler: async (req, responseToolkit) => { + return await this.handle({ + routeSchemas, + request: req, + responseToolkit, + handler: this.enhanceWithContext(handler), + }); + }, method: 'post', - path, + path: getRouteFullPath(this.routerPath, path), options, }); } - /** - * Register a route handler for `PUT` request. - * @param route {@link RouteConfig} - a route configuration. - * @param handler {@link RequestHandler} - a function to call to respond to an incoming request - */ public put

( route: RouteConfig, handler: RequestHandler @@ -105,19 +157,20 @@ export class Router { const { path, options = {} } = route; const routeSchemas = this.routeSchemasFromRouteConfig(route, 'put'); this.routes.push({ - handler: async (req, responseToolkit, log) => - await this.handle(routeSchemas, req, responseToolkit, handler, log), + handler: async (req, responseToolkit) => { + return await this.handle({ + routeSchemas, + request: req, + responseToolkit, + handler: this.enhanceWithContext(handler), + }); + }, method: 'put', - path, + path: getRouteFullPath(this.routerPath, path), options, }); } - /** - * Register a route handler for `DELETE` request. - * @param route {@link RouteConfig} - a route configuration. - * @param handler {@link RequestHandler} - a function to call to respond to an incoming request - */ public delete

( route: RouteConfig, handler: RequestHandler @@ -125,19 +178,20 @@ export class Router { const { path, options = {} } = route; const routeSchemas = this.routeSchemasFromRouteConfig(route, 'delete'); this.routes.push({ - handler: async (req, responseToolkit, log) => - await this.handle(routeSchemas, req, responseToolkit, handler, log), + handler: async (req, responseToolkit) => { + return await this.handle({ + routeSchemas, + request: req, + responseToolkit, + handler: this.enhanceWithContext(handler), + }); + }, method: 'delete', - path, + path: getRouteFullPath(this.routerPath, path), options, }); } - /** - * Returns all routes registered with the this router. - * @returns List of registered routes. - * @internal - */ public getRoutes() { return [...this.routes]; } @@ -174,13 +228,17 @@ export class Router { return route.validate ? route.validate : undefined; } - private async handle

( - routeSchemas: RouteSchemas | undefined, - request: Request, - responseToolkit: ResponseToolkit, - handler: RequestHandler, - log: Logger - ) { + private async handle

({ + routeSchemas, + request, + responseToolkit, + handler, + }: { + request: Request; + responseToolkit: ResponseToolkit; + handler: RequestHandlerEnhanced; + routeSchemas?: RouteSchemas; + }) { let kibanaRequest: KibanaRequest, TypeOf, TypeOf>; const hapiResponseAdapter = new HapiResponseAdapter(responseToolkit); try { @@ -193,12 +251,22 @@ export class Router { const kibanaResponse = await handler(kibanaRequest, kibanaResponseFactory); return hapiResponseAdapter.handle(kibanaResponse); } catch (e) { - log.error(e); + this.log.error(e); return hapiResponseAdapter.toInternalError(); } } } +type WithoutHeadArgument = T extends (first: any, ...rest: infer Params) => infer Return + ? (...rest: Params) => Return + : never; + +type RequestHandlerEnhanced< + P extends ObjectType, + Q extends ObjectType, + B extends ObjectType +> = WithoutHeadArgument>; + /** * A function executed when route path matched requested resource path. * Request handler is expected to return a result of one of {@link KibanaResponseFactory} functions. @@ -221,8 +289,8 @@ export class Router { * }, * }, * // function to execute to create a responses - * async (request, response) => { - * const data = await findObject(request.params.id); + * async (context, request, response) => { + * const data = await context.findObject(request.params.id); * // creates a command to respond with 'not found' error * if (!data) return response.notFound(); * // creates a command to send found data to the client @@ -233,6 +301,7 @@ export class Router { * @public */ export type RequestHandler

= ( + context: {}, request: KibanaRequest, TypeOf, TypeOf>, response: KibanaResponseFactory ) => KibanaResponse | Promise>; diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 72a183f2dd1db8..fc6a953fd2407c 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -41,7 +41,8 @@ import { ElasticsearchClientConfig, ElasticsearchServiceSetup, } from './elasticsearch'; -import { HttpServiceSetup, HttpServiceStart } from './http'; + +import { HttpServiceSetup, HttpServiceStart, IRouter } from './http'; import { PluginsServiceSetup, PluginsServiceStart } from './plugins'; export { bootstrap } from './bootstrap'; @@ -85,7 +86,7 @@ export { kibanaResponseFactory, KibanaResponseFactory, RouteConfig, - Router, + IRouter, RouteMethod, RouteConfigOptions, SessionStorage, @@ -161,6 +162,8 @@ export interface CoreSetup { registerOnPostAuth: HttpServiceSetup['registerOnPostAuth']; basePath: HttpServiceSetup['basePath']; isTlsEnabled: HttpServiceSetup['isTlsEnabled']; + createRouter: () => IRouter; + registerRouter: (router: IRouter) => void; }; } diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 8ebd3e6b3c8b2c..473fb4379ab5b0 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -23,6 +23,7 @@ import { CoreContext } from '../core_context'; import { LoggerFactory } from '../logging'; import { PluginWrapper, PluginManifest } from './plugin'; import { PluginsServiceSetupDeps, PluginsServiceStartDeps } from './plugins_service'; +import { IRouter } from '../http'; import { CoreSetup, CoreStart } from '..'; /** @@ -119,6 +120,8 @@ export function createPluginSetupContext( }, http: { createCookieSessionStorageFactory: deps.http.createCookieSessionStorageFactory, + createRouter: () => deps.http.createRouter(`/${plugin.name}`), + registerRouter: (router: IRouter) => deps.http.registerRouter(router), registerOnPreAuth: deps.http.registerOnPreAuth, registerAuth: deps.http.registerAuth, registerOnPostAuth: deps.http.registerOnPostAuth, diff --git a/src/core/server/server.ts b/src/core/server/server.ts index 01f2673c3f9e5c..debab247b2ed0a 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -21,7 +21,7 @@ import { Type } from '@kbn/config-schema'; import { ConfigService, Env, Config, ConfigPath } from './config'; import { ElasticsearchService } from './elasticsearch'; -import { HttpService, HttpServiceSetup, Router } from './http'; +import { HttpService, HttpServiceSetup } from './http'; import { LegacyService } from './legacy'; import { Logger, LoggerFactory } from './logging'; import { PluginsService, config as pluginsConfig } from './plugins'; @@ -110,8 +110,10 @@ export class Server { } private registerDefaultRoute(httpSetup: HttpServiceSetup) { - const router = new Router('/core'); - router.get({ path: '/', validate: false }, async (req, res) => res.ok({ version: '0.0.1' })); + const router = httpSetup.createRouter('/core'); + router.get({ path: '/', validate: false }, async (context, req, res) => + res.ok({ version: '0.0.1' }) + ); httpSetup.registerRouter(router); } From 6cae95a778f2d65e8d6d001d029374536a22de40 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Tue, 6 Aug 2019 14:23:03 +0200 Subject: [PATCH 2/8] fix tests --- .../server/http/cookie_sesson_storage.test.ts | 73 +++-- src/core/server/http/http_server.test.ts | 235 +++++++------ src/core/server/http/http_service.mock.ts | 12 + src/core/server/http/http_service.test.ts | 14 +- src/core/server/http/http_tools.test.ts | 7 +- .../integration_tests/http_service.test.ts | 38 +-- .../http/integration_tests/router.test.ts | 308 +++++++++--------- src/core/server/http/router/router.test.ts | 10 +- .../integration_tests/legacy_service.test.ts | 15 +- src/core/server/mocks.ts | 15 +- .../on_request_interceptor.test.ts | 17 +- 11 files changed, 432 insertions(+), 312 deletions(-) diff --git a/src/core/server/http/cookie_sesson_storage.test.ts b/src/core/server/http/cookie_sesson_storage.test.ts index e2cc4e5abec85e..b0f60103a9434d 100644 --- a/src/core/server/http/cookie_sesson_storage.test.ts +++ b/src/core/server/http/cookie_sesson_storage.test.ts @@ -19,27 +19,47 @@ import request from 'request'; import supertest from 'supertest'; import { ByteSizeValue } from '@kbn/config-schema'; +import { BehaviorSubject } from 'rxjs'; -import { HttpServer } from './http_server'; -import { HttpConfig } from './http_config'; -import { Router, KibanaRequest } from './router'; +import { CoreContext } from '../core_context'; +import { HttpService } from './http_service'; +import { KibanaRequest } from './router'; + +import { Env } from '../config'; +import { getEnvOptions } from '../config/__mocks__/env'; +import { configServiceMock } from '../config/config_service.mock'; import { loggingServiceMock } from '../logging/logging_service.mock'; import { httpServerMock } from './http_server.mocks'; import { createCookieSessionStorageFactory } from './cookie_session_storage'; -let server: HttpServer; +let server: HttpService; let logger: ReturnType; -const config = { - host: '127.0.0.1', - maxPayload: new ByteSizeValue(1024), - ssl: {}, -} as HttpConfig; +let env: Env; +let coreContext: CoreContext; +const configService = configServiceMock.create(); + +configService.atPath.mockReturnValue( + new BehaviorSubject({ + hosts: ['http://1.2.3.4'], + maxPayload: new ByteSizeValue(1024), + autoListen: true, + healthCheck: { + delay: 2000, + }, + ssl: { + verificationMode: 'none', + }, + } as any) +); beforeEach(() => { logger = loggingServiceMock.create(); - server = new HttpServer(logger, 'tests'); + env = Env.createDefault(getEnvOptions()); + + coreContext = { env, logger, configService: configService as any }; + server = new HttpService(coreContext); }); afterEach(async () => { @@ -78,15 +98,14 @@ const cookieOptions = { describe('Cookie based SessionStorage', () => { describe('#set()', () => { it('Should write to session storage & set cookies', async () => { - const router = new Router(''); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter(''); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const sessionStorage = factory.asScoped(req); sessionStorage.set({ value: userData, expires: Date.now() + sessionDurationMs }); return res.ok({}); }); - - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); const factory = await createCookieSessionStorageFactory( @@ -114,9 +133,10 @@ describe('Cookie based SessionStorage', () => { }); describe('#get()', () => { it('reads from session storage', async () => { - const router = new Router(''); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter(''); - router.get({ path: '/', validate: false }, async (req, res) => { + router.get({ path: '/', validate: false }, async (context, req, res) => { const sessionStorage = factory.asScoped(req); const sessionValue = await sessionStorage.get(); if (!sessionValue) { @@ -126,7 +146,6 @@ describe('Cookie based SessionStorage', () => { return res.ok({ value: sessionValue.value }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); const factory = await createCookieSessionStorageFactory( @@ -152,15 +171,15 @@ describe('Cookie based SessionStorage', () => { .expect(200, { value: userData }); }); it('returns null for empty session', async () => { - const router = new Router(''); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); - router.get({ path: '/', validate: false }, async (req, res) => { + const router = createRouter(''); + router.get({ path: '/', validate: false }, async (context, req, res) => { const sessionStorage = factory.asScoped(req); const sessionValue = await sessionStorage.get(); return res.ok({ value: sessionValue }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); const factory = await createCookieSessionStorageFactory( @@ -179,10 +198,12 @@ describe('Cookie based SessionStorage', () => { }); it('returns null for invalid session & clean cookies', async () => { - const router = new Router(''); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + + const router = createRouter(''); let setOnce = false; - router.get({ path: '/', validate: false }, async (req, res) => { + router.get({ path: '/', validate: false }, async (context, req, res) => { const sessionStorage = factory.asScoped(req); if (!setOnce) { setOnce = true; @@ -193,7 +214,6 @@ describe('Cookie based SessionStorage', () => { return res.ok({ value: sessionValue }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); const factory = await createCookieSessionStorageFactory( @@ -313,9 +333,11 @@ describe('Cookie based SessionStorage', () => { describe('#clear()', () => { it('clears session storage & remove cookies', async () => { - const router = new Router(''); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + + const router = createRouter(''); - router.get({ path: '/', validate: false }, async (req, res) => { + router.get({ path: '/', validate: false }, async (context, req, res) => { const sessionStorage = factory.asScoped(req); if (await sessionStorage.get()) { sessionStorage.clear(); @@ -325,7 +347,6 @@ describe('Cookie based SessionStorage', () => { return res.ok({}); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); const factory = await createCookieSessionStorageFactory( diff --git a/src/core/server/http/http_server.test.ts b/src/core/server/http/http_server.test.ts index 83e569e8752c5a..5ffa68a9841003 100644 --- a/src/core/server/http/http_server.test.ts +++ b/src/core/server/http/http_server.test.ts @@ -25,11 +25,11 @@ jest.mock('fs', () => ({ readFileSync: jest.fn(), })); -import Chance from 'chance'; import supertest from 'supertest'; import { ByteSizeValue, schema } from '@kbn/config-schema'; -import { HttpConfig, Router } from '.'; +import { HttpConfig } from './http_config'; +import { Router } from './router'; import { loggingServiceMock } from '../logging/logging_service.mock'; import { HttpServer } from './http_server'; @@ -50,19 +50,18 @@ interface StorageData { expires: number; } -const chance = new Chance(); - let server: HttpServer; let config: HttpConfig; let configWithSSL: HttpConfig; -const logger = loggingServiceMock.create(); +const loggingService = loggingServiceMock.create(); +const logger = loggingService.get(); beforeEach(() => { config = { host: '127.0.0.1', maxPayload: new ByteSizeValue(1024), - port: chance.integer({ min: 10000, max: 15000 }), + port: 10002, ssl: { enabled: false }, } as HttpConfig; @@ -78,7 +77,7 @@ beforeEach(() => { }, } as HttpConfig; - server = new HttpServer(logger, 'tests'); + server = new HttpServer(loggingService, 'tests'); }); afterEach(async () => { @@ -86,24 +85,56 @@ afterEach(async () => { jest.clearAllMocks(); }); -test('listening after started', async () => { +test('log listening address after started', async () => { expect(server.isListening()).toBe(false); await server.setup(config); await server.start(); expect(server.isListening()).toBe(true); - expect(loggingServiceMock.collect(logger).info).toMatchInlineSnapshot(` -Array [ - Array [ - "http server running", - ], -] -`); + expect(loggingServiceMock.collect(loggingService).info).toMatchInlineSnapshot(` + Array [ + Array [ + "http server running on http://127.0.0.1:10002", + ], + ] + `); +}); + +test('log listening address after started when configured with BasePath and rewriteBasePath = false', async () => { + expect(server.isListening()).toBe(false); + + await server.setup({ ...config, basePath: '/bar', rewriteBasePath: false }); + await server.start(); + + expect(server.isListening()).toBe(true); + expect(loggingServiceMock.collect(loggingService).info).toMatchInlineSnapshot(` + Array [ + Array [ + "http server running on http://127.0.0.1:10002", + ], + ] + `); +}); + +test('log listening address after started when configured with BasePath and rewriteBasePath = true', async () => { + expect(server.isListening()).toBe(false); + + await server.setup({ ...config, basePath: '/bar', rewriteBasePath: true }); + await server.start(); + + expect(server.isListening()).toBe(true); + expect(loggingServiceMock.collect(loggingService).info).toMatchInlineSnapshot(` + Array [ + Array [ + "http server running on http://127.0.0.1:10002/bar", + ], + ] + `); }); test('valid params', async () => { - const router = new Router('/foo'); + const router = new Router('/foo', logger); router.get( { @@ -114,7 +145,7 @@ test('valid params', async () => { }), }, }, - (req, res) => { + (context, req, res) => { return res.ok({ key: req.params.test }); } ); @@ -133,7 +164,7 @@ test('valid params', async () => { }); test('invalid params', async () => { - const router = new Router('/foo'); + const router = new Router('/foo', logger); router.get( { @@ -144,7 +175,7 @@ test('invalid params', async () => { }), }, }, - (req, res) => { + (context, req, res) => { return res.ok({ key: req.params.test }); } ); @@ -165,7 +196,7 @@ test('invalid params', async () => { }); test('valid query', async () => { - const router = new Router('/foo'); + const router = new Router('/foo', logger); router.get( { @@ -177,7 +208,7 @@ test('valid query', async () => { }), }, }, - (req, res) => { + (context, req, res) => { return res.ok(req.query); } ); @@ -196,7 +227,7 @@ test('valid query', async () => { }); test('invalid query', async () => { - const router = new Router('/foo'); + const router = new Router('/foo', logger); router.get( { @@ -207,7 +238,7 @@ test('invalid query', async () => { }), }, }, - (req, res) => { + (context, req, res) => { return res.ok(req.query); } ); @@ -228,7 +259,7 @@ test('invalid query', async () => { }); test('valid body', async () => { - const router = new Router('/foo'); + const router = new Router('/foo', logger); router.post( { @@ -240,7 +271,7 @@ test('valid body', async () => { }), }, }, - (req, res) => { + (context, req, res) => { return res.ok(req.body); } ); @@ -263,7 +294,7 @@ test('valid body', async () => { }); test('invalid body', async () => { - const router = new Router('/foo'); + const router = new Router('/foo', logger); router.post( { @@ -274,7 +305,7 @@ test('invalid body', async () => { }), }, }, - (req, res) => { + (context, req, res) => { return res.ok(req.body); } ); @@ -296,7 +327,7 @@ test('invalid body', async () => { }); test('handles putting', async () => { - const router = new Router('/foo'); + const router = new Router('/foo', logger); router.put( { @@ -307,7 +338,7 @@ test('handles putting', async () => { }), }, }, - (req, res) => { + (context, req, res) => { return res.ok(req.body); } ); @@ -327,7 +358,7 @@ test('handles putting', async () => { }); test('handles deleting', async () => { - const router = new Router('/foo'); + const router = new Router('/foo', logger); router.delete( { @@ -338,7 +369,7 @@ test('handles deleting', async () => { }), }, }, - (req, res) => { + (context, req, res) => { return res.ok({ key: req.params.id }); } ); @@ -367,9 +398,11 @@ describe('with `basepath: /bar` and `rewriteBasePath: false`', () => { rewriteBasePath: false, } as HttpConfig; - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok({ key: 'value:/' })); - router.get({ path: '/foo', validate: false }, (req, res) => res.ok({ key: 'value:/foo' })); + const router = new Router('/', logger); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ key: 'value:/' })); + router.get({ path: '/foo', validate: false }, (context, req, res) => + res.ok({ key: 'value:/foo' }) + ); const { registerRouter, server: innerServer } = await server.setup(configWithBasePath); registerRouter(router); @@ -426,9 +459,11 @@ describe('with `basepath: /bar` and `rewriteBasePath: true`', () => { rewriteBasePath: true, } as HttpConfig; - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok({ key: 'value:/' })); - router.get({ path: '/foo', validate: false }, (req, res) => res.ok({ key: 'value:/foo' })); + const router = new Router('/', logger); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ key: 'value:/' })); + router.get({ path: '/foo', validate: false }, (context, req, res) => + res.ok({ key: 'value:/foo' }) + ); const { registerRouter, server: innerServer } = await server.setup(configWithBasePath); registerRouter(router); @@ -478,8 +513,8 @@ describe('with `basepath: /bar` and `rewriteBasePath: true`', () => { }); test('with defined `redirectHttpFromPort`', async () => { - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok({ key: 'value:/' })); + const router = new Router('/', logger); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ key: 'value:/' })); const { registerRouter } = await server.setup(configWithSSL); registerRouter(router); @@ -515,8 +550,8 @@ test('throws an error if starts without set up', async () => { test('enables auth for a route by default if registerAuth has been called', async () => { const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false }, (req, res) => + const router = new Router('', logger); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ authRequired: req.route.options.authRequired }) ); registerRouter(router); @@ -535,9 +570,10 @@ test('enables auth for a route by default if registerAuth has been called', asyn test('supports disabling auth for a route explicitly', async () => { const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false, options: { authRequired: false } }, (req, res) => - res.ok({ authRequired: req.route.options.authRequired }) + const router = new Router('', logger); + router.get( + { path: '/', validate: false, options: { authRequired: false } }, + (context, req, res) => res.ok({ authRequired: req.route.options.authRequired }) ); registerRouter(router); const authenticate = jest.fn(); @@ -554,8 +590,8 @@ test('supports disabling auth for a route explicitly', async () => { test('supports enabling auth for a route explicitly', async () => { const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false, options: { authRequired: true } }, (req, res) => + const router = new Router('', logger); + router.get({ path: '/', validate: false, options: { authRequired: true } }, (context, req, res) => res.ok({ authRequired: req.route.options.authRequired }) ); registerRouter(router); @@ -574,11 +610,11 @@ test('allows attaching metadata to attach meta-data tag strings to a route', asy const tags = ['my:tag']; const { registerRouter, server: innerServer } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/with-tags', validate: false, options: { tags } }, (req, res) => + const router = new Router('', logger); + router.get({ path: '/with-tags', validate: false, options: { tags } }, (context, req, res) => res.ok({ tags: req.route.options.tags }) ); - router.get({ path: '/without-tags', validate: false }, (req, res) => + router.get({ path: '/without-tags', validate: false }, (context, req, res) => res.ok({ tags: req.route.options.tags }) ); registerRouter(router); @@ -596,8 +632,8 @@ test('allows attaching metadata to attach meta-data tag strings to a route', asy test('exposes route details of incoming request to a route handler', async () => { const { registerRouter, server: innerServer } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false }, (req, res) => res.ok(req.route)); + const router = new Router('', logger); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok(req.route)); registerRouter(router); await server.start(); @@ -640,8 +676,10 @@ describe('setup contract', () => { it('may grant access to a resource', async () => { const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false }, async (req, res) => res.ok({ content: 'ok' })); + const router = new Router('', logger); + router.get({ path: '/', validate: false }, async (context, req, res) => + res.ok({ content: 'ok' }) + ); registerRouter(router); await registerAuth((req, t) => t.authenticated()); @@ -654,8 +692,10 @@ describe('setup contract', () => { it('supports rejecting a request from an unauthenticated user', async () => { const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false }, async (req, res) => res.ok({ content: 'ok' })); + const router = new Router('', logger); + router.get({ path: '/', validate: false }, async (context, req, res) => + res.ok({ content: 'ok' }) + ); registerRouter(router); await registerAuth((req, t) => t.rejected(Boom.unauthorized())); @@ -669,8 +709,10 @@ describe('setup contract', () => { it('supports redirecting', async () => { const redirectTo = '/redirect-url'; const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false }, async (req, res) => res.ok({ content: 'ok' })); + const router = new Router('', logger); + router.get({ path: '/', validate: false }, async (context, req, res) => + res.ok({ content: 'ok' }) + ); registerRouter(router); await registerAuth((req, t) => { @@ -686,8 +728,10 @@ describe('setup contract', () => { it(`doesn't expose internal error details`, async () => { const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false }, async (req, res) => res.ok({ content: 'ok' })); + const router = new Router('', logger); + router.get({ path: '/', validate: false }, async (context, req, res) => + res.ok({ content: 'ok' }) + ); registerRouter(router); await registerAuth((req, t) => { @@ -705,8 +749,10 @@ describe('setup contract', () => { }); it('allows manipulating cookies via cookie session storage', async () => { - const router = new Router(''); - router.get({ path: '/', validate: false }, async (req, res) => res.ok({ content: 'ok' })); + const router = new Router('', logger); + router.get({ path: '/', validate: false }, async (context, req, res) => + res.ok({ content: 'ok' }) + ); const { createCookieSessionStorageFactory, @@ -762,9 +808,9 @@ describe('setup contract', () => { return t.authenticated(); }); - const router = new Router(''); - router.get({ path: '/', validate: false }, (req, res) => res.ok({ content: 'ok' })); - router.get({ path: '/with-cookie', validate: false }, (req, res) => { + const router = new Router('', logger); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); + router.get({ path: '/with-cookie', validate: false }, (context, req, res) => { const sessionStorage = sessionStorageFactory.asScoped(req); sessionStorage.clear(); return res.ok({ content: 'ok' }); @@ -817,8 +863,8 @@ describe('setup contract', () => { }); let fromRouteHandler; - const router = new Router(''); - router.get({ path: '/', validate: false }, (req, res) => { + const router = new Router('', logger); + router.get({ path: '/', validate: false }, (context, req, res) => { fromRouteHandler = req.headers.authorization; return res.ok({ content: 'ok' }); }); @@ -847,8 +893,8 @@ describe('setup contract', () => { return t.authenticated({ responseHeaders: authResponseHeader }); }); - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok({ header: 'ok' })); + const router = new Router('/', logger); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ header: 'ok' })); registerRouter(router); await server.start(); @@ -870,8 +916,10 @@ describe('setup contract', () => { return t.authenticated({ responseHeaders: authResponseHeader }); }); - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.badRequest(new Error('reason'))); + const router = new Router('/', logger); + router.get({ path: '/', validate: false }, (context, req, res) => + res.badRequest(new Error('reason')) + ); registerRouter(router); await server.start(); @@ -894,8 +942,8 @@ describe('setup contract', () => { return t.authenticated({ responseHeaders: authResponseHeader }); }); - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok({})); + const router = new Router('/', logger); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({})); registerRouter(router); await server.start(); @@ -904,7 +952,7 @@ describe('setup contract', () => { .get('/') .expect(200); - expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(); + expect(loggingServiceMock.collect(loggingService).warn).toMatchInlineSnapshot(); }); it.skip('logs warning if Auth Security Header rewrites response header for error response', async () => { @@ -917,8 +965,10 @@ describe('setup contract', () => { return t.authenticated({ responseHeaders: authResponseHeader }); }); - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.badRequest(new Error('reason'))); + const router = new Router('/', logger); + router.get({ path: '/', validate: false }, (context, req, res) => + res.badRequest(new Error('reason')) + ); registerRouter(router); await server.start(); @@ -927,7 +977,7 @@ describe('setup contract', () => { .get('/') .expect(400); - expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot(); + expect(loggingServiceMock.collect(loggingService).warn).toMatchInlineSnapshot(); }); }); @@ -937,8 +987,8 @@ describe('setup contract', () => { config ); - const router = new Router(''); - router.get({ path: '/', validate: false }, (req, res) => + const router = new Router('', logger); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ isAuthenticated: auth.isAuthenticated(req) }) ); registerRouter(router); @@ -956,9 +1006,10 @@ describe('setup contract', () => { config ); - const router = new Router(''); - router.get({ path: '/', validate: false, options: { authRequired: false } }, (req, res) => - res.ok({ isAuthenticated: auth.isAuthenticated(req) }) + const router = new Router('', logger); + router.get( + { path: '/', validate: false, options: { authRequired: false } }, + (context, req, res) => res.ok({ isAuthenticated: auth.isAuthenticated(req) }) ); registerRouter(router); @@ -973,9 +1024,10 @@ describe('setup contract', () => { it('returns false if no authorization mechanism has been registered', async () => { const { registerRouter, server: innerServer, auth } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false, options: { authRequired: false } }, (req, res) => - res.ok({ isAuthenticated: auth.isAuthenticated(req) }) + const router = new Router('', logger); + router.get( + { path: '/', validate: false, options: { authRequired: false } }, + (context, req, res) => res.ok({ isAuthenticated: auth.isAuthenticated(req) }) ); registerRouter(router); @@ -1002,8 +1054,8 @@ describe('setup contract', () => { return t.authenticated({ state: user }); }); - const router = new Router(''); - router.get({ path: '/', validate: false }, (req, res) => res.ok(auth.get(req))); + const router = new Router('', logger); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok(auth.get(req))); registerRouter(router); await server.start(); @@ -1014,8 +1066,8 @@ describe('setup contract', () => { it('returns correct authentication unknown status', async () => { const { registerRouter, server: innerServer, auth } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false }, (req, res) => res.ok(auth.get(req))); + const router = new Router('', logger); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok(auth.get(req))); registerRouter(router); await server.start(); @@ -1031,9 +1083,10 @@ describe('setup contract', () => { config ); await registerAuth(authenticate); - const router = new Router(''); - router.get({ path: '/', validate: false, options: { authRequired: false } }, (req, res) => - res.ok(auth.get(req)) + const router = new Router('', logger); + router.get( + { path: '/', validate: false, options: { authRequired: false } }, + (context, req, res) => res.ok(auth.get(req)) ); registerRouter(router); diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index b4e509c8a05a5a..1e26e981bca28e 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -24,6 +24,7 @@ import { OnPreAuthToolkit } from './lifecycle/on_pre_auth'; import { AuthToolkit } from './lifecycle/auth'; import { OnPostAuthToolkit } from './lifecycle/on_post_auth'; import { sessionStorageMock } from './cookie_session_storage.mocks'; +import { IRouter } from './router'; type ServiceSetupMockType = jest.Mocked & { basePath: jest.Mocked; @@ -36,6 +37,15 @@ const createBasePathMock = (): jest.Mocked => ({ remove: jest.fn(), }); +const createRouterMock = (): jest.Mocked => ({ + routerPath: '/', + get: jest.fn(), + post: jest.fn(), + put: jest.fn(), + delete: jest.fn(), + getRoutes: jest.fn(), +}); + const createSetupContractMock = () => { const setupContract: ServiceSetupMockType = { // we can mock some hapi server method when we need it @@ -45,6 +55,7 @@ const createSetupContractMock = () => { registerAuth: jest.fn(), registerOnPostAuth: jest.fn(), registerRouter: jest.fn(), + createRouter: jest.fn(), basePath: createBasePathMock(), auth: { get: jest.fn(), @@ -56,6 +67,7 @@ const createSetupContractMock = () => { setupContract.createCookieSessionStorageFactory.mockResolvedValue( sessionStorageMock.createFactory() ); + setupContract.createRouter.mockImplementation(createRouterMock); return setupContract; }; diff --git a/src/core/server/http/http_service.test.ts b/src/core/server/http/http_service.test.ts index cde06dc31802fc..4f4a83294011aa 100644 --- a/src/core/server/http/http_service.test.ts +++ b/src/core/server/http/http_service.test.ts @@ -21,7 +21,7 @@ import { mockHttpServer } from './http_service.test.mocks'; import { noop } from 'lodash'; import { BehaviorSubject } from 'rxjs'; -import { HttpService, Router } from '.'; +import { HttpService } from '.'; import { HttpConfigType, config } from './http_config'; import { httpServerMock } from './http_server.mocks'; import { Config, ConfigService, Env, ObjectToConfigAdapter } from '../config'; @@ -214,8 +214,9 @@ test('register route handler', async () => { const service = new HttpService({ configService, env, logger }); - const router = new Router('/foo'); - const { registerRouter } = await service.setup(); + const { registerRouter, createRouter } = await service.setup(); + const router = createRouter('/foo'); + registerRouter(router); expect(registerRouterMock).toHaveBeenCalledTimes(1); @@ -233,8 +234,11 @@ test('returns http server contract on setup', async () => { })); const service = new HttpService({ configService, env, logger }); - const setupHttpServer = await service.setup(); - expect(setupHttpServer).toEqual(httpServer); + const setupContract = await service.setup(); + expect(setupContract).toMatchObject(httpServer); + expect(setupContract).toMatchObject({ + createRouter: expect.any(Function), + }); }); test('does not start http server if process is dev cluster master', async () => { diff --git a/src/core/server/http/http_tools.test.ts b/src/core/server/http/http_tools.test.ts index 40344ed2c7034d..6a5e7126d9b500 100644 --- a/src/core/server/http/http_tools.test.ts +++ b/src/core/server/http/http_tools.test.ts @@ -70,13 +70,12 @@ describe('timeouts', () => { const server = new HttpServer(logger, 'foo'); test('returns 408 on timeout error', async () => { - const router = new Router(''); - router.get({ path: '/a', validate: false }, async (req, res) => { + const router = new Router('', logger.get()); + router.get({ path: '/a', validate: false }, async (context, req, res) => { await new Promise(resolve => setTimeout(resolve, 2000)); return res.ok({}); }); - router.get({ path: '/b', validate: false }, (req, res) => res.ok({})); - + router.get({ path: '/b', validate: false }, (context, req, res) => res.ok({})); const { registerRouter, server: innerServer } = await server.setup({ socketTimeout: 1000, host: '127.0.0.1', diff --git a/src/core/server/http/integration_tests/http_service.test.ts b/src/core/server/http/integration_tests/http_service.test.ts index 303b81a73da3cf..264f640ae079f3 100644 --- a/src/core/server/http/integration_tests/http_service.test.ts +++ b/src/core/server/http/integration_tests/http_service.test.ts @@ -21,7 +21,6 @@ import { Request } from 'hapi'; import { first } from 'rxjs/operators'; import { clusterClientMock } from './http_service.test.mocks'; -import { Router } from '../router'; import * as kbnTestServer from '../../../../test_utils/kbn_server'; interface User { @@ -164,14 +163,14 @@ describe('http service', () => { it('rewrites authorization header via authHeaders to make a request to Elasticsearch', async () => { const authHeaders = { authorization: 'Basic: user:password' }; const { http, elasticsearch } = await root.setup(); - const { registerAuth, registerRouter } = http; + const { registerAuth, registerRouter, createRouter } = http; await registerAuth((req, t) => { return t.authenticated({ requestHeaders: authHeaders }); }); - const router = new Router('/new-platform'); - router.get({ path: '/', validate: false }, async (req, res) => { + const router = createRouter('/new-platform'); + router.get({ path: '/', validate: false }, async (context, req, res) => { const client = await elasticsearch.dataClient$.pipe(first()).toPromise(); client.asScoped(req); return res.ok({ header: 'ok' }); @@ -190,10 +189,10 @@ describe('http service', () => { it('passes request authorization header to Elasticsearch if registerAuth was not set', async () => { const authorizationHeader = 'Basic: username:password'; const { http, elasticsearch } = await root.setup(); - const { registerRouter } = http; + const { registerRouter, createRouter } = http; - const router = new Router('/new-platform'); - router.get({ path: '/', validate: false }, async (req, res) => { + const router = createRouter('/new-platform'); + router.get({ path: '/', validate: false }, async (context, req, res) => { const client = await elasticsearch.dataClient$.pipe(first()).toPromise(); client.asScoped(req); return res.ok({ header: 'ok' }); @@ -274,10 +273,12 @@ describe('http service', () => { afterEach(async () => await root.shutdown()); it('supports passing request through to the route handler', async () => { - const router = new Router('/new-platform'); - router.get({ path: '/', validate: false }, async (req, res) => res.ok({ content: 'ok' })); - const { http } = await root.setup(); + const router = http.createRouter('/new-platform'); + router.get({ path: '/', validate: false }, async (context, req, res) => + res.ok({ content: 'ok' }) + ); + http.registerOnPostAuth((req, t) => t.next()); http.registerOnPostAuth(async (req, t) => { await Promise.resolve(); @@ -328,21 +329,18 @@ describe('http service', () => { it(`doesn't share request object between interceptors`, async () => { const { http } = await root.setup(); http.registerOnPostAuth(async (req, t) => { - // @ts-ignore. don't complain customField is not defined on Request type - req.customField = { value: 42 }; + (req as any).customField = { value: 42 }; return t.next(); }); http.registerOnPostAuth((req, t) => { - // @ts-ignore don't complain customField is not defined on Request type - if (typeof req.customField !== 'undefined') { + if (typeof (req as any).customField !== 'undefined') { throw new Error('Request object was mutated'); } return t.next(); }); - const router = new Router('/new-platform'); - router.get({ path: '/', validate: false }, async (req, res) => - // @ts-ignore. don't complain customField is not defined on Request type - res.ok({ customField: String(req.customField) }) + const router = http.createRouter('/new-platform'); + router.get({ path: '/', validate: false }, async (context, req, res) => + res.ok({ customField: String((req as any).customField) }) ); http.registerRouter(router); await root.start(); @@ -366,8 +364,8 @@ describe('http service', () => { return t.redirected('/new-platform/new-url', { forward: true }); }); - const router = new Router('/new-platform'); - router.get({ path: '/new-url', validate: false }, async (req, res) => + const router = http.createRouter('/new-platform'); + router.get({ path: '/new-url', validate: false }, async (context, req, res) => res.ok({ key: 'new-url-reached' }) ); http.registerRouter(router); diff --git a/src/core/server/http/integration_tests/router.test.ts b/src/core/server/http/integration_tests/router.test.ts index 1d3ee4643830d9..b32ba999b27f1f 100644 --- a/src/core/server/http/integration_tests/router.test.ts +++ b/src/core/server/http/integration_tests/router.test.ts @@ -20,27 +20,45 @@ import { Stream } from 'stream'; import Boom from 'boom'; import supertest from 'supertest'; +import { BehaviorSubject } from 'rxjs'; import { ByteSizeValue, schema } from '@kbn/config-schema'; -import { HttpConfig, Router } from '..'; -import { HttpServer } from '../http_server'; +import { CoreContext } from '../../core_context'; +import { HttpService } from '../http_service'; -import { LoggerFactory } from '../../logging'; +import { Env } from '../../config'; +import { getEnvOptions } from '../../config/__mocks__/env'; +import { configServiceMock } from '../../config/config_service.mock'; import { loggingServiceMock } from '../../logging/logging_service.mock'; -let server: HttpServer; -let logger: LoggerFactory; - -const config = { - host: '127.0.0.1', - maxPayload: new ByteSizeValue(1024), - port: 10000, - ssl: { enabled: false }, -} as HttpConfig; +let server: HttpService; + +let logger: ReturnType; +let env: Env; +let coreContext: CoreContext; +const configService = configServiceMock.create(); + +configService.atPath.mockReturnValue( + new BehaviorSubject({ + hosts: ['http://1.2.3.4'], + maxPayload: new ByteSizeValue(1024), + autoListen: true, + healthCheck: { + delay: 2000, + }, + ssl: { + enabled: false, + verificationMode: 'none', + }, + } as any) +); beforeEach(() => { logger = loggingServiceMock.create(); - server = new HttpServer(logger, 'tests'); + env = Env.createDefault(getEnvOptions()); + + coreContext = { env, logger, configService: configService as any }; + server = new HttpService(coreContext); }); afterEach(async () => { @@ -49,13 +67,13 @@ afterEach(async () => { describe('Handler', () => { it("Doesn't expose error details if handler throws", async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { throw new Error('unexpected error'); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -74,13 +92,13 @@ describe('Handler', () => { }); it('returns 500 Server error if handler throws Boom error', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { throw Boom.unauthorized(); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -99,11 +117,11 @@ describe('Handler', () => { }); it('returns 500 Server error if handler returns unexpected result', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); - router.get({ path: '/', validate: false }, (req, res) => 'ok' as any); + const router = createRouter('/'); + router.get({ path: '/', validate: false }, (context, req, res) => 'ok' as any); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -122,7 +140,8 @@ describe('Handler', () => { }); it('returns 400 Bad request if request validation failed', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); router.get( { @@ -133,10 +152,9 @@ describe('Handler', () => { }), }, }, - (req, res) => res.noContent() + (context, req, res) => res.noContent() ); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -154,13 +172,13 @@ describe('Handler', () => { describe('Response factory', () => { describe('Success', () => { it('supports answering with json object', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.ok({ key: 'value' }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -173,13 +191,13 @@ describe('Response factory', () => { }); it('supports answering with string', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.ok('result'); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -192,13 +210,13 @@ describe('Response factory', () => { }); it('supports answering with undefined', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.ok(undefined); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -208,9 +226,10 @@ describe('Response factory', () => { }); it('supports answering with Stream', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const stream = new Stream.Readable({ read() { this.push('a'); @@ -223,7 +242,6 @@ describe('Response factory', () => { return res.ok(stream); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -236,9 +254,10 @@ describe('Response factory', () => { }); it('supports answering with chunked Stream', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const stream = new Stream.PassThrough(); stream.write('a'); stream.write('b'); @@ -250,7 +269,6 @@ describe('Response factory', () => { return res.ok(stream); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -263,9 +281,10 @@ describe('Response factory', () => { }); it('supports answering with Buffer', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const buffer = Buffer.alloc(1028, '.'); return res.ok(buffer, { @@ -275,7 +294,6 @@ describe('Response factory', () => { }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -290,9 +308,10 @@ describe('Response factory', () => { }); it('supports answering with Buffer text', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const buffer = new Buffer('abc'); return res.ok(buffer, { @@ -302,7 +321,6 @@ describe('Response factory', () => { }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -317,9 +335,10 @@ describe('Response factory', () => { }); it('supports configuring standard headers', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.ok('value', { headers: { etag: '1234', @@ -327,7 +346,6 @@ describe('Response factory', () => { }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -340,9 +358,10 @@ describe('Response factory', () => { }); it('supports configuring non-standard headers', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.ok('value', { headers: { etag: '1234', @@ -351,7 +370,6 @@ describe('Response factory', () => { }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -365,9 +383,10 @@ describe('Response factory', () => { }); it('accepted headers are case-insensitive.', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.ok('value', { headers: { ETag: '1234', @@ -375,7 +394,6 @@ describe('Response factory', () => { }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -387,9 +405,10 @@ describe('Response factory', () => { }); it('accept array of headers', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.ok('value', { headers: { 'set-cookie': ['foo', 'bar'], @@ -397,7 +416,6 @@ describe('Response factory', () => { }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -409,15 +427,15 @@ describe('Response factory', () => { }); it('throws if given invalid json object as response payload', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const payload: any = { key: {} }; payload.key.payload = payload; return res.ok(payload); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -430,13 +448,13 @@ describe('Response factory', () => { }); it('200 OK with body', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.ok({ key: 'value' }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -449,13 +467,13 @@ describe('Response factory', () => { }); it('202 Accepted with body', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.accepted({ location: 'somewhere' }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -468,13 +486,13 @@ describe('Response factory', () => { }); it('204 No content', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.noContent(); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -488,9 +506,10 @@ describe('Response factory', () => { describe('Redirection', () => { it('302 supports redirection to configured URL', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.redirected('The document has moved', { headers: { location: '/new-url', @@ -499,7 +518,6 @@ describe('Response factory', () => { }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -513,9 +531,10 @@ describe('Response factory', () => { }); it('throws if redirection url not provided', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.redirected(undefined, { headers: { 'x-kibana': 'tag', @@ -523,7 +542,6 @@ describe('Response factory', () => { } as any); // location headers is required }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -544,14 +562,14 @@ describe('Response factory', () => { describe('Error', () => { it('400 Bad request', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const error = new Error('some message'); return res.badRequest(error); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -563,13 +581,13 @@ describe('Response factory', () => { }); it('400 Bad request with default message', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.badRequest(); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -581,13 +599,13 @@ describe('Response factory', () => { }); it('400 Bad request with additional data', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.badRequest({ message: 'some message', meta: { data: ['good', 'bad'] } }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -604,9 +622,10 @@ describe('Response factory', () => { }); it('401 Unauthorized', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const error = new Error('no access'); return res.unauthorized(error, { headers: { @@ -615,7 +634,6 @@ describe('Response factory', () => { }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -628,13 +646,13 @@ describe('Response factory', () => { }); it('401 Unauthorized with default message', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.unauthorized(); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -646,14 +664,14 @@ describe('Response factory', () => { }); it('403 Forbidden', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const error = new Error('reason'); return res.forbidden(error); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -665,13 +683,13 @@ describe('Response factory', () => { }); it('403 Forbidden with default message', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.forbidden(); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -683,14 +701,14 @@ describe('Response factory', () => { }); it('404 Not Found', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const error = new Error('file is not found'); return res.notFound(error); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -702,13 +720,13 @@ describe('Response factory', () => { }); it('404 Not Found with default message', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.notFound(); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -720,14 +738,14 @@ describe('Response factory', () => { }); it('409 Conflict', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const error = new Error('stale version'); return res.conflict(error); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -739,13 +757,13 @@ describe('Response factory', () => { }); it('409 Conflict with default message', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.conflict(); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -759,9 +777,10 @@ describe('Response factory', () => { describe('Custom', () => { it('creates success response', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.custom(undefined, { statusCode: 201, headers: { @@ -770,7 +789,6 @@ describe('Response factory', () => { }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -782,9 +800,10 @@ describe('Response factory', () => { }); it('creates redirect response', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.custom('The document has moved', { headers: { location: '/new-url', @@ -793,7 +812,6 @@ describe('Response factory', () => { }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -805,16 +823,16 @@ describe('Response factory', () => { }); it('throws if redirects without location header to be set', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.custom('The document has moved', { headers: {}, statusCode: 301, }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -832,16 +850,16 @@ describe('Response factory', () => { }); it('creates error response', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const error = new Error('unauthorized'); return res.custom(error, { statusCode: 401, }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -853,9 +871,10 @@ describe('Response factory', () => { }); it('creates error response with additional data', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.custom( { message: 'unauthorized', @@ -867,7 +886,6 @@ describe('Response factory', () => { ); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -882,9 +900,10 @@ describe('Response factory', () => { }); it('creates error response with additional data and error object', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.custom( { message: new Error('unauthorized'), @@ -896,7 +915,6 @@ describe('Response factory', () => { ); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -911,16 +929,16 @@ describe('Response factory', () => { }); it('creates error response with Boom error', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const error = Boom.unauthorized(); return res.custom(error, { statusCode: 401, }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -932,15 +950,15 @@ describe('Response factory', () => { }); it("Doesn't log details of created 500 Server error response", async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.custom('reason', { statusCode: 500, }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -953,9 +971,10 @@ describe('Response factory', () => { }); it('throws an error if not valid error is provided', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.custom( { error: 'error-message' }, { @@ -964,7 +983,6 @@ describe('Response factory', () => { ); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -983,15 +1001,15 @@ describe('Response factory', () => { }); it('throws if an error not provided', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { return res.custom(undefined, { statusCode: 401, }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -1010,14 +1028,14 @@ describe('Response factory', () => { }); it('throws an error if statusCode is not specified', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const error = new Error('error message'); return res.custom(error, undefined as any); // options.statusCode is required }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -1036,14 +1054,14 @@ describe('Response factory', () => { }); it('throws an error if statusCode is not valid', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const error = new Error('error message'); return res.custom(error, { statusCode: 20 }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); diff --git a/src/core/server/http/router/router.test.ts b/src/core/server/http/router/router.test.ts index a0ab96257adc3f..3e9f5e2d186b1c 100644 --- a/src/core/server/http/router/router.test.ts +++ b/src/core/server/http/router/router.test.ts @@ -18,24 +18,26 @@ */ import { Router } from './router'; +import { loggingServiceMock } from '../../logging/logging_service.mock'; +const logger = loggingServiceMock.create().get(); describe('Router', () => { describe('Options', () => { it('throws if validation for a route is not defined explicitly', () => { - const router = new Router('/foo'); + const router = new Router('', logger); expect( // we use 'any' because validate is a required field - () => router.get({ path: '/' } as any, (req, res) => res.ok({})) + () => router.get({ path: '/' } as any, (context, req, res) => res.ok({})) ).toThrowErrorMatchingInlineSnapshot( `"The [get] at [/] does not have a 'validate' specified. Use 'false' as the value if you want to bypass validation."` ); }); it('throws if validation for a route is declared wrong', () => { - const router = new Router('/foo'); + const router = new Router('', logger); expect(() => router.get( // we use 'any' because validate requires @kbn/config-schema usage { path: '/', validate: { params: { validate: () => 'error' } } } as any, - (req, res) => res.ok({}) + (context, req, res) => res.ok({}) ) ).toThrowErrorMatchingInlineSnapshot( `"Expected a valid schema declared with '@kbn/config-schema' package at key: [params]."` diff --git a/src/core/server/legacy/integration_tests/legacy_service.test.ts b/src/core/server/legacy/integration_tests/legacy_service.test.ts index f4b2d274700870..1c80d655e73de9 100644 --- a/src/core/server/legacy/integration_tests/legacy_service.test.ts +++ b/src/core/server/legacy/integration_tests/legacy_service.test.ts @@ -16,7 +16,6 @@ * specific language governing permissions and limitations * under the License. */ -import { Router } from '../../http/'; import * as kbnTestServer from '../../../../test_utils/kbn_server'; describe('legacy service', () => { @@ -29,13 +28,12 @@ describe('legacy service', () => { afterEach(async () => await root.shutdown()); it("handles http request in Legacy platform if New platform doesn't handle it", async () => { + const { http } = await root.setup(); const rootUrl = '/route'; - const router = new Router(rootUrl); - router.get({ path: '/new-platform', validate: false }, (req, res) => + const router = http.createRouter(rootUrl); + router.get({ path: '/new-platform', validate: false }, (context, req, res) => res.ok({ content: 'from-new-platform' }) ); - - const { http } = await root.setup(); http.registerRouter(router); await root.start(); @@ -54,13 +52,12 @@ describe('legacy service', () => { await kbnTestServer.request.get(root, legacyPlatformUrl).expect(200, 'ok from legacy server'); }); it('throws error if Legacy and New platforms register handler for the same route', async () => { + const { http } = await root.setup(); const rootUrl = '/route'; - const router = new Router(rootUrl); - router.get({ path: '', validate: false }, (req, res) => + const router = http.createRouter(rootUrl); + router.get({ path: '', validate: false }, (context, req, res) => res.ok({ content: 'from-new-platform' }) ); - - const { http } = await root.setup(); http.registerRouter(router); await root.start(); diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 518221d2b9bd52..91ca3089d67a46 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -56,9 +56,22 @@ function pluginInitializerContextMock(config: T) { } function createCoreSetupMock() { + const httpService = httpServiceMock.createSetupContract(); + const httpMock: jest.Mocked = { + createCookieSessionStorageFactory: httpService.createCookieSessionStorageFactory, + registerOnPreAuth: httpService.registerOnPreAuth, + registerAuth: httpService.registerAuth, + registerOnPostAuth: httpService.registerOnPostAuth, + basePath: httpService.basePath, + isTlsEnabled: httpService.isTlsEnabled, + registerRouter: httpService.registerRouter, + createRouter: jest.fn(), + }; + httpMock.createRouter.mockImplementation(() => httpService.createRouter('')); + const mock: MockedKeys = { elasticsearch: elasticsearchServiceMock.createSetupContract(), - http: httpServiceMock.createSetupContract(), + http: httpMock, }; return mock; diff --git a/x-pack/legacy/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.test.ts b/x-pack/legacy/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.test.ts index 44d41778792668..b14963e36e1160 100644 --- a/x-pack/legacy/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.test.ts +++ b/x-pack/legacy/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.test.ts @@ -7,7 +7,7 @@ import { Legacy } from 'kibana'; import { schema } from '@kbn/config-schema'; import { initSpacesOnRequestInterceptor } from './on_request_interceptor'; -import { HttpServiceSetup, Router, KibanaRequest } from '../../../../../../../src/core/server'; +import { HttpServiceSetup, KibanaRequest } from '../../../../../../../src/core/server'; import * as kbnTestServer from '../../../../../../../src/test_utils/kbn_server'; import { KibanaConfig } from '../../../../../../../src/legacy/server/kbn_server'; @@ -55,15 +55,18 @@ describe('onRequestInterceptor', () => { } if (routes === 'new-platform') { - const router = new Router('/'); + const router = http.createRouter('/'); - router.get({ path: '/foo', validate: false }, (req: KibanaRequest, h: any) => { - return h.ok({ path: req.url.pathname, basePath: http.basePath.get(req) }); - }); + router.get( + { path: '/foo', validate: false }, + (context: unknown, req: KibanaRequest, h: any) => { + return h.ok({ path: req.url.pathname, basePath: http.basePath.get(req) }); + } + ); router.get( { path: '/some/path/s/foo/bar', validate: false }, - (req: KibanaRequest, h: any) => { + (context: unknown, req: KibanaRequest, h: any) => { return h.ok({ path: req.url.pathname, basePath: http.basePath.get(req) }); } ); @@ -79,7 +82,7 @@ describe('onRequestInterceptor', () => { }), }, }, - (req: KibanaRequest, h: any) => { + (context: unknown, req: KibanaRequest, h: any) => { return h.ok({ path: req.url.pathname, basePath: http.basePath.get(req), From 8f94589f315619080f3f93fb80528ef88d325ac4 Mon Sep 17 00:00:00 2001 From: restrry Date: Tue, 6 Aug 2019 14:39:53 +0200 Subject: [PATCH 3/8] update examples in docs --- src/core/server/http/http_server.ts | 8 +++----- src/core/server/http/router/router.ts | 2 +- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/core/server/http/http_server.ts b/src/core/server/http/http_server.ts index 1d395c5c301c97..05181f4acd2e9a 100644 --- a/src/core/server/http/http_server.ts +++ b/src/core/server/http/http_server.ts @@ -45,10 +45,9 @@ import { BasePath } from './base_path_service'; * * @example * To handle an incoming request in your plugin you should: - * - Create a `Router` instance. Use `plugin-id` as a prefix path segment for your routes. + * - Create a `Router` instance. Router is already configured to use `plugin-id` to prefix path segment for your routes. * ```ts - * import { Router } from 'src/core/server'; - * const router = new Router('my-app'); + * const router = httpSetup.createRouter(); * ``` * * - Use `@kbn/config-schema` package to create a schema to validate the request `params`, `query`, and `body`. Every incoming request will be validated against the created schema. If validation failed, the request is rejected with `400` status and `Bad request` error without calling the route's handler. @@ -83,8 +82,7 @@ import { BasePath } from './base_path_service'; * - Register route handler for GET request to 'my-app/path/{id}' path * ```ts * import { schema, TypeOf } from '@kbn/config-schema'; - * import { Router } from 'src/core/server'; - * const router = new Router('my-app'); + * const router = httpSetup.createRouter(); * * const validate = { * params: schema.object({ diff --git a/src/core/server/http/router/router.ts b/src/core/server/http/router/router.ts index 35f8e726d29dac..84975daa099813 100644 --- a/src/core/server/http/router/router.ts +++ b/src/core/server/http/router/router.ts @@ -276,7 +276,7 @@ type RequestHandlerEnhanced< * * @example * ```ts - * const router = new Router('my-app'); + * const router = httpSetup.createRouter(); * // creates a route handler for GET request on 'my-app/path/{id}' path * router.get( * { From 4d9338c27d424f53b4514e8d95984a3d6d288efe Mon Sep 17 00:00:00 2001 From: restrry Date: Thu, 8 Aug 2019 15:18:12 +0200 Subject: [PATCH 4/8] update tests --- .../server/http/cookie_sesson_storage.test.ts | 2 +- src/core/server/http/http_server.mocks.ts | 3 + src/core/server/http/http_server.test.ts | 6 +- .../integration_tests/core_services.test.ts | 13 +- .../integration_tests/http_service.test.ts | 426 --------------- .../http/integration_tests/lifecycle.test.ts | 486 ++++++++++++------ .../http/integration_tests/router.test.ts | 34 +- 7 files changed, 365 insertions(+), 605 deletions(-) delete mode 100644 src/core/server/http/integration_tests/http_service.test.ts diff --git a/src/core/server/http/cookie_sesson_storage.test.ts b/src/core/server/http/cookie_sesson_storage.test.ts index b0f60103a9434d..ad8cac0decf379 100644 --- a/src/core/server/http/cookie_sesson_storage.test.ts +++ b/src/core/server/http/cookie_sesson_storage.test.ts @@ -58,7 +58,7 @@ beforeEach(() => { logger = loggingServiceMock.create(); env = Env.createDefault(getEnvOptions()); - coreContext = { env, logger, configService: configService as any }; + coreContext = { coreId: Symbol(), env, logger, configService: configService as any }; server = new HttpService(coreContext); }); diff --git a/src/core/server/http/http_server.mocks.ts b/src/core/server/http/http_server.mocks.ts index d676a67b734d8d..33a98127aa6303 100644 --- a/src/core/server/http/http_server.mocks.ts +++ b/src/core/server/http/http_server.mocks.ts @@ -92,6 +92,9 @@ function createRawRequestMock(customization: DeepPartial = {}) { headers: {}, path: '/', route: { settings: {} }, + url: { + href: '/', + }, raw: { req: { url: '/', diff --git a/src/core/server/http/http_server.test.ts b/src/core/server/http/http_server.test.ts index 00a192504c3795..21a6490fb54cee 100644 --- a/src/core/server/http/http_server.test.ts +++ b/src/core/server/http/http_server.test.ts @@ -83,7 +83,7 @@ test('log listening address after started', async () => { expect(loggingServiceMock.collect(loggingService).info).toMatchInlineSnapshot(` Array [ Array [ - "http server running on http://127.0.0.1:10002", + "http server running at http://127.0.0.1:10002", ], ] `); @@ -99,7 +99,7 @@ test('log listening address after started when configured with BasePath and rewr expect(loggingServiceMock.collect(loggingService).info).toMatchInlineSnapshot(` Array [ Array [ - "http server running on http://127.0.0.1:10002", + "http server running at http://127.0.0.1:10002", ], ] `); @@ -115,7 +115,7 @@ test('log listening address after started when configured with BasePath and rewr expect(loggingServiceMock.collect(loggingService).info).toMatchInlineSnapshot(` Array [ Array [ - "http server running on http://127.0.0.1:10002/bar", + "http server running at http://127.0.0.1:10002/bar", ], ] `); diff --git a/src/core/server/http/integration_tests/core_services.test.ts b/src/core/server/http/integration_tests/core_services.test.ts index 691b1d01687148..b145e3f789113b 100644 --- a/src/core/server/http/integration_tests/core_services.test.ts +++ b/src/core/server/http/integration_tests/core_services.test.ts @@ -21,7 +21,6 @@ import { Request } from 'hapi'; import { first } from 'rxjs/operators'; import { clusterClientMock } from './core_service.test.mocks'; -import { Router } from '../router'; import * as kbnTestServer from '../../../../test_utils/kbn_server'; interface User { @@ -254,14 +253,14 @@ describe('http service', () => { it('rewrites authorization header via authHeaders to make a request to Elasticsearch', async () => { const authHeaders = { authorization: 'Basic: user:password' }; const { http, elasticsearch } = await root.setup(); - const { registerAuth, registerRouter } = http; + const { registerAuth, registerRouter, createRouter } = http; await registerAuth((req, res, toolkit) => toolkit.authenticated({ requestHeaders: authHeaders }) ); - const router = new Router('/new-platform'); - router.get({ path: '/', validate: false }, async (req, res) => { + const router = createRouter('/new-platform'); + router.get({ path: '/', validate: false }, async (context, req, res) => { const client = await elasticsearch.dataClient$.pipe(first()).toPromise(); client.asScoped(req); return res.ok({ header: 'ok' }); @@ -280,10 +279,10 @@ describe('http service', () => { it('passes request authorization header to Elasticsearch if registerAuth was not set', async () => { const authorizationHeader = 'Basic: username:password'; const { http, elasticsearch } = await root.setup(); - const { registerRouter } = http; + const { registerRouter, createRouter } = http; - const router = new Router('/new-platform'); - router.get({ path: '/', validate: false }, async (req, res) => { + const router = createRouter('/new-platform'); + router.get({ path: '/', validate: false }, async (context, req, res) => { const client = await elasticsearch.dataClient$.pipe(first()).toPromise(); client.asScoped(req); return res.ok({ header: 'ok' }); diff --git a/src/core/server/http/integration_tests/http_service.test.ts b/src/core/server/http/integration_tests/http_service.test.ts deleted file mode 100644 index 264f640ae079f3..00000000000000 --- a/src/core/server/http/integration_tests/http_service.test.ts +++ /dev/null @@ -1,426 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -import Boom from 'boom'; -import { Request } from 'hapi'; -import { first } from 'rxjs/operators'; -import { clusterClientMock } from './http_service.test.mocks'; - -import * as kbnTestServer from '../../../../test_utils/kbn_server'; - -interface User { - id: string; - roles?: string[]; -} - -interface StorageData { - value: User; - expires: number; -} - -describe('http service', () => { - describe('setup contract', () => { - describe('#registerAuth()', () => { - const sessionDurationMs = 1000; - const cookieOptions = { - name: 'sid', - encryptionKey: 'something_at_least_32_characters', - validate: (session: StorageData) => true, - isSecure: false, - path: '/', - }; - - let root: ReturnType; - beforeEach(async () => { - root = kbnTestServer.createRoot(); - }, 30000); - - afterEach(async () => { - clusterClientMock.mockClear(); - await root.shutdown(); - }); - - it('runs auth for legacy routes and proxy request to legacy server route handlers', async () => { - const { http } = await root.setup(); - const sessionStorageFactory = await http.createCookieSessionStorageFactory( - cookieOptions - ); - http.registerAuth((req, t) => { - if (req.headers.authorization) { - const user = { id: '42' }; - const sessionStorage = sessionStorageFactory.asScoped(req); - sessionStorage.set({ value: user, expires: Date.now() + sessionDurationMs }); - return t.authenticated({ state: user }); - } else { - return t.rejected(Boom.unauthorized()); - } - }); - await root.start(); - - const legacyUrl = '/legacy'; - const kbnServer = kbnTestServer.getKbnServer(root); - kbnServer.server.route({ - method: 'GET', - path: legacyUrl, - handler: () => 'ok from legacy server', - }); - - const response = await kbnTestServer.request - .get(root, legacyUrl) - .expect(200, 'ok from legacy server'); - - expect(response.header['set-cookie']).toHaveLength(1); - }); - - it('passes authHeaders as request headers to the legacy platform', async () => { - const token = 'Basic: name:password'; - const { http } = await root.setup(); - const sessionStorageFactory = await http.createCookieSessionStorageFactory( - cookieOptions - ); - http.registerAuth((req, t) => { - if (req.headers.authorization) { - const user = { id: '42' }; - const sessionStorage = sessionStorageFactory.asScoped(req); - sessionStorage.set({ value: user, expires: Date.now() + sessionDurationMs }); - return t.authenticated({ - state: user, - requestHeaders: { - authorization: token, - }, - }); - } else { - return t.rejected(Boom.unauthorized()); - } - }); - await root.start(); - - const legacyUrl = '/legacy'; - const kbnServer = kbnTestServer.getKbnServer(root); - kbnServer.server.route({ - method: 'GET', - path: legacyUrl, - handler: (req: Request) => ({ - authorization: req.headers.authorization, - custom: req.headers.custom, - }), - }); - - await kbnTestServer.request - .get(root, legacyUrl) - .set({ custom: 'custom-header' }) - .expect(200, { authorization: token, custom: 'custom-header' }); - }); - - it('passes associated auth state to Legacy platform', async () => { - const user = { id: '42' }; - - const { http } = await root.setup(); - const sessionStorageFactory = await http.createCookieSessionStorageFactory( - cookieOptions - ); - http.registerAuth((req, t) => { - if (req.headers.authorization) { - const sessionStorage = sessionStorageFactory.asScoped(req); - sessionStorage.set({ value: user, expires: Date.now() + sessionDurationMs }); - return t.authenticated({ state: user }); - } else { - return t.rejected(Boom.unauthorized()); - } - }); - await root.start(); - - const legacyUrl = '/legacy'; - const kbnServer = kbnTestServer.getKbnServer(root); - kbnServer.server.route({ - method: 'GET', - path: legacyUrl, - handler: kbnServer.newPlatform.setup.core.http.auth.get, - }); - - const response = await kbnTestServer.request.get(root, legacyUrl).expect(200); - expect(response.body.state).toEqual(user); - expect(response.body.status).toEqual('authenticated'); - - expect(response.header['set-cookie']).toHaveLength(1); - }); - - it('rewrites authorization header via authHeaders to make a request to Elasticsearch', async () => { - const authHeaders = { authorization: 'Basic: user:password' }; - const { http, elasticsearch } = await root.setup(); - const { registerAuth, registerRouter, createRouter } = http; - - await registerAuth((req, t) => { - return t.authenticated({ requestHeaders: authHeaders }); - }); - - const router = createRouter('/new-platform'); - router.get({ path: '/', validate: false }, async (context, req, res) => { - const client = await elasticsearch.dataClient$.pipe(first()).toPromise(); - client.asScoped(req); - return res.ok({ header: 'ok' }); - }); - registerRouter(router); - - await root.start(); - - await kbnTestServer.request.get(root, '/new-platform/').expect(200); - expect(clusterClientMock).toBeCalledTimes(1); - const [firstCall] = clusterClientMock.mock.calls; - const [, , headers] = firstCall; - expect(headers).toEqual(authHeaders); - }); - - it('passes request authorization header to Elasticsearch if registerAuth was not set', async () => { - const authorizationHeader = 'Basic: username:password'; - const { http, elasticsearch } = await root.setup(); - const { registerRouter, createRouter } = http; - - const router = createRouter('/new-platform'); - router.get({ path: '/', validate: false }, async (context, req, res) => { - const client = await elasticsearch.dataClient$.pipe(first()).toPromise(); - client.asScoped(req); - return res.ok({ header: 'ok' }); - }); - registerRouter(router); - - await root.start(); - - await kbnTestServer.request - .get(root, '/new-platform/') - .set('Authorization', authorizationHeader) - .expect(200); - - expect(clusterClientMock).toBeCalledTimes(1); - const [firstCall] = clusterClientMock.mock.calls; - const [, , headers] = firstCall; - expect(headers).toEqual({ - authorization: authorizationHeader, - }); - }); - - it('attach security header to a successful response handled by Legacy platform', async () => { - const authResponseHeader = { - 'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca', - }; - const { http } = await root.setup(); - const { registerAuth } = http; - - await registerAuth((req, t) => { - return t.authenticated({ responseHeaders: authResponseHeader }); - }); - - await root.start(); - - const kbnServer = kbnTestServer.getKbnServer(root); - kbnServer.server.route({ - method: 'GET', - path: '/legacy', - handler: () => 'ok', - }); - - const response = await kbnTestServer.request.get(root, '/legacy').expect(200); - expect(response.header['www-authenticate']).toBe(authResponseHeader['www-authenticate']); - }); - - it('attach security header to an error response handled by Legacy platform', async () => { - const authResponseHeader = { - 'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca', - }; - const { http } = await root.setup(); - const { registerAuth } = http; - - await registerAuth((req, t) => { - return t.authenticated({ responseHeaders: authResponseHeader }); - }); - - await root.start(); - - const kbnServer = kbnTestServer.getKbnServer(root); - kbnServer.server.route({ - method: 'GET', - path: '/legacy', - handler: () => { - throw Boom.badRequest(); - }, - }); - - const response = await kbnTestServer.request.get(root, '/legacy').expect(400); - expect(response.header['www-authenticate']).toBe(authResponseHeader['www-authenticate']); - }); - }); - - describe('#registerOnPostAuth()', () => { - let root: ReturnType; - beforeEach(async () => { - root = kbnTestServer.createRoot(); - }, 30000); - afterEach(async () => await root.shutdown()); - - it('supports passing request through to the route handler', async () => { - const { http } = await root.setup(); - const router = http.createRouter('/new-platform'); - router.get({ path: '/', validate: false }, async (context, req, res) => - res.ok({ content: 'ok' }) - ); - - http.registerOnPostAuth((req, t) => t.next()); - http.registerOnPostAuth(async (req, t) => { - await Promise.resolve(); - return t.next(); - }); - http.registerRouter(router); - await root.start(); - - await kbnTestServer.request.get(root, '/new-platform/').expect(200, { content: 'ok' }); - }); - - it('supports redirecting to configured url', async () => { - const redirectTo = '/redirect-url'; - const { http } = await root.setup(); - http.registerOnPostAuth(async (req, t) => t.redirected(redirectTo)); - await root.start(); - - const response = await kbnTestServer.request.get(root, '/new-platform/').expect(302); - expect(response.header.location).toBe(redirectTo); - }); - - it('fails a request with configured error and status code', async () => { - const { http } = await root.setup(); - http.registerOnPostAuth(async (req, t) => - t.rejected(new Error('unexpected error'), { statusCode: 400 }) - ); - await root.start(); - - await kbnTestServer.request - .get(root, '/new-platform/') - .expect(400, { statusCode: 400, error: 'Bad Request', message: 'unexpected error' }); - }); - - it(`doesn't expose internal error details`, async () => { - const { http } = await root.setup(); - http.registerOnPostAuth(async (req, t) => { - throw new Error('sensitive info'); - }); - await root.start(); - - await kbnTestServer.request.get(root, '/new-platform/').expect({ - statusCode: 500, - error: 'Internal Server Error', - message: 'An internal server error occurred', - }); - }); - - it(`doesn't share request object between interceptors`, async () => { - const { http } = await root.setup(); - http.registerOnPostAuth(async (req, t) => { - (req as any).customField = { value: 42 }; - return t.next(); - }); - http.registerOnPostAuth((req, t) => { - if (typeof (req as any).customField !== 'undefined') { - throw new Error('Request object was mutated'); - } - return t.next(); - }); - const router = http.createRouter('/new-platform'); - router.get({ path: '/', validate: false }, async (context, req, res) => - res.ok({ customField: String((req as any).customField) }) - ); - http.registerRouter(router); - await root.start(); - - await kbnTestServer.request - .get(root, '/new-platform/') - .expect(200, { customField: 'undefined' }); - }); - }); - - describe('#registerOnPostAuth() toolkit', () => { - let root: ReturnType; - beforeEach(async () => { - root = kbnTestServer.createRoot(); - }, 30000); - - afterEach(async () => await root.shutdown()); - it('supports Url change on the flight', async () => { - const { http } = await root.setup(); - http.registerOnPreAuth((req, t) => { - return t.redirected('/new-platform/new-url', { forward: true }); - }); - - const router = http.createRouter('/new-platform'); - router.get({ path: '/new-url', validate: false }, async (context, req, res) => - res.ok({ key: 'new-url-reached' }) - ); - http.registerRouter(router); - - await root.start(); - - await kbnTestServer.request.get(root, '/').expect(200, { key: 'new-url-reached' }); - }); - - it('url re-write works for legacy server as well', async () => { - const { http } = await root.setup(); - const newUrl = '/new-url'; - http.registerOnPreAuth((req, t) => { - return t.redirected(newUrl, { forward: true }); - }); - - await root.start(); - const kbnServer = kbnTestServer.getKbnServer(root); - kbnServer.server.route({ - method: 'GET', - path: newUrl, - handler: () => 'ok-from-legacy', - }); - - await kbnTestServer.request.get(root, '/').expect(200, 'ok-from-legacy'); - }); - }); - - describe('#basePath()', () => { - let root: ReturnType; - beforeEach(async () => { - root = kbnTestServer.createRoot(); - }, 30000); - - afterEach(async () => await root.shutdown()); - it('basePath information for an incoming request is available in legacy server', async () => { - const reqBasePath = '/requests-specific-base-path'; - const { http } = await root.setup(); - http.registerOnPreAuth((req, t) => { - http.basePath.set(req, reqBasePath); - return t.next(); - }); - - await root.start(); - - const legacyUrl = '/legacy'; - const kbnServer = kbnTestServer.getKbnServer(root); - kbnServer.server.route({ - method: 'GET', - path: legacyUrl, - handler: kbnServer.newPlatform.setup.core.http.basePath.get, - }); - - await kbnTestServer.request.get(root, legacyUrl).expect(200, reqBasePath); - }); - }); - }); -}); diff --git a/src/core/server/http/integration_tests/lifecycle.test.ts b/src/core/server/http/integration_tests/lifecycle.test.ts index 068e52ea34460e..13a0fdbb82ca28 100644 --- a/src/core/server/http/integration_tests/lifecycle.test.ts +++ b/src/core/server/http/integration_tests/lifecycle.test.ts @@ -20,23 +20,46 @@ import supertest from 'supertest'; import { ByteSizeValue } from '@kbn/config-schema'; import request from 'request'; +import { BehaviorSubject } from 'rxjs'; -import { HttpConfig, Router } from '..'; import { ensureRawRequest } from '../router'; -import { HttpServer } from '../http_server'; +import { HttpService } from '../http_service'; -import { LoggerFactory } from '../../logging'; +import { CoreContext } from '../../core_context'; +import { Env } from '../../config'; +import { getEnvOptions } from '../../config/__mocks__/env'; +import { configServiceMock } from '../../config/config_service.mock'; import { loggingServiceMock } from '../../logging/logging_service.mock'; -let server: HttpServer; -let logger: LoggerFactory; +let server: HttpService; -const config = { - host: '127.0.0.1', - maxPayload: new ByteSizeValue(1024), - port: 10001, - ssl: { enabled: false }, -} as HttpConfig; +let logger: ReturnType; +let env: Env; +let coreContext: CoreContext; +const configService = configServiceMock.create(); + +configService.atPath.mockReturnValue( + new BehaviorSubject({ + hosts: ['localhost'], + maxPayload: new ByteSizeValue(1024), + autoListen: true, + ssl: { + enabled: false, + }, + } as any) +); + +beforeEach(() => { + logger = loggingServiceMock.create(); + env = Env.createDefault(getEnvOptions()); + + coreContext = { coreId: Symbol('core'), env, logger, configService: configService as any }; + server = new HttpService(coreContext); +}); + +afterEach(async () => { + await server.stop(); +}); interface User { id: string; @@ -48,22 +71,18 @@ interface StorageData { expires: number; } -beforeEach(() => { - logger = loggingServiceMock.create(); - server = new HttpServer(logger, 'tests'); -}); - -afterEach(async () => { - await server.stop(); -}); - describe('OnPreAuth', () => { it('supports registering request inceptors', async () => { - const router = new Router('/'); + const { + registerRouter, + registerOnPreAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok('ok')); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok('ok')); - const { registerRouter, registerOnPreAuth, server: innerServer } = await server.setup(config); registerRouter(router); const callingOrder: string[] = []; @@ -86,12 +105,19 @@ describe('OnPreAuth', () => { }); it('supports request forwarding to specified url', async () => { - const router = new Router('/'); + const { + registerRouter, + registerOnPreAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/initial', validate: false }, (req, res) => res.ok('initial')); - router.get({ path: '/redirectUrl', validate: false }, (req, res) => res.ok('redirected')); + router.get({ path: '/initial', validate: false }, (context, req, res) => res.ok('initial')); + router.get({ path: '/redirectUrl', validate: false }, (context, req, res) => + res.ok('redirected') + ); - const { registerRouter, registerOnPreAuth, server: innerServer } = await server.setup(config); registerRouter(router); let urlBeforeForwarding; @@ -118,11 +144,17 @@ describe('OnPreAuth', () => { }); it('supports redirection from the interceptor', async () => { - const router = new Router('/'); + const { + registerRouter, + registerOnPreAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + const redirectUrl = '/redirectUrl'; - router.get({ path: '/initial', validate: false }, (req, res) => res.ok('initial')); + router.get({ path: '/initial', validate: false }, (context, req, res) => res.ok('initial')); - const { registerRouter, registerOnPreAuth, server: innerServer } = await server.setup(config); registerRouter(router); registerOnPreAuth((req, res, t) => @@ -142,10 +174,16 @@ describe('OnPreAuth', () => { }); it('supports rejecting request and adjusting response headers', async () => { - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok(undefined)); + const { + registerRouter, + registerOnPreAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + + router.get({ path: '/', validate: false }, (context, req, res) => res.ok(undefined)); - const { registerRouter, registerOnPreAuth, server: innerServer } = await server.setup(config); registerRouter(router); registerOnPreAuth((req, res, t) => @@ -165,10 +203,16 @@ describe('OnPreAuth', () => { }); it("doesn't expose error details if interceptor throws", async () => { - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok(undefined)); + const { + registerRouter, + registerOnPreAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + + router.get({ path: '/', validate: false }, (context, req, res) => res.ok(undefined)); - const { registerRouter, registerOnPreAuth, server: innerServer } = await server.setup(config); registerRouter(router); registerOnPreAuth((req, res, t) => { @@ -191,10 +235,16 @@ describe('OnPreAuth', () => { }); it('returns internal error if interceptor returns unexpected result', async () => { - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok('ok')); + const { + registerRouter, + registerOnPreAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + + router.get({ path: '/', validate: false }, (context, req, res) => res.ok('ok')); - const { registerRouter, registerOnPreAuth, server: innerServer } = await server.setup(config); registerRouter(router); registerOnPreAuth((req, res, t) => ({} as any)); @@ -215,24 +265,31 @@ describe('OnPreAuth', () => { }); it(`doesn't share request object between interceptors`, async () => { - const { registerRouter, registerOnPreAuth, server: innerServer } = await server.setup(config); + const { + registerRouter, + registerOnPreAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + registerOnPreAuth((req, res, t) => { - // @ts-ignore. don't complain customField is not defined on Request type - req.customField = { value: 42 }; + // don't complain customField is not defined on Request type + (req as any).customField = { value: 42 }; return t.next(); }); registerOnPreAuth((req, res, t) => { - // @ts-ignore don't complain customField is not defined on Request type - if (typeof req.customField !== 'undefined') { + // don't complain customField is not defined on Request type + if (typeof (req as any).customField !== 'undefined') { throw new Error('Request object was mutated'); } return t.next(); }); - const router = new Router('/'); - router.get({ path: '/', validate: false }, async (req, res) => - // @ts-ignore. don't complain customField is not defined on Request type - res.ok({ customField: String(req.customField) }) + router.get({ path: '/', validate: false }, (context, req, res) => + // don't complain customField is not defined on Request type + res.ok({ customField: String((req as any).customField) }) ); + registerRouter(router); await server.start(); @@ -244,11 +301,16 @@ describe('OnPreAuth', () => { describe('OnPostAuth', () => { it('supports registering request inceptors', async () => { - const router = new Router('/'); + const { + registerRouter, + registerOnPostAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok('ok')); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok('ok')); - const { registerRouter, registerOnPostAuth, server: innerServer } = await server.setup(config); registerRouter(router); const callingOrder: string[] = []; @@ -271,11 +333,17 @@ describe('OnPostAuth', () => { }); it('supports redirection from the interceptor', async () => { - const router = new Router('/'); + const { + registerRouter, + registerOnPostAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + const redirectUrl = '/redirectUrl'; - router.get({ path: '/initial', validate: false }, (req, res) => res.ok('initial')); + router.get({ path: '/initial', validate: false }, (context, req, res) => res.ok('initial')); - const { registerRouter, registerOnPostAuth, server: innerServer } = await server.setup(config); registerRouter(router); registerOnPostAuth((req, res, t) => @@ -295,10 +363,15 @@ describe('OnPostAuth', () => { }); it('supports rejecting request and adjusting response headers', async () => { - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok(undefined)); + const { + registerRouter, + registerOnPostAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); - const { registerRouter, registerOnPostAuth, server: innerServer } = await server.setup(config); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok(undefined)); registerRouter(router); registerOnPostAuth((req, res, t) => @@ -318,10 +391,15 @@ describe('OnPostAuth', () => { }); it("doesn't expose error details if interceptor throws", async () => { - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok(undefined)); + const { + registerRouter, + registerOnPostAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); - const { registerRouter, registerOnPostAuth, server: innerServer } = await server.setup(config); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok(undefined)); registerRouter(router); registerOnPostAuth((req, res, t) => { @@ -344,10 +422,15 @@ describe('OnPostAuth', () => { }); it('returns internal error if interceptor returns unexpected result', async () => { - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok('ok')); + const { + registerRouter, + registerOnPostAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); - const { registerRouter, registerOnPostAuth, server: innerServer } = await server.setup(config); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok('ok')); registerRouter(router); registerOnPostAuth((req, res, t) => ({} as any)); @@ -368,23 +451,30 @@ describe('OnPostAuth', () => { }); it(`doesn't share request object between interceptors`, async () => { - const { registerRouter, registerOnPostAuth, server: innerServer } = await server.setup(config); + const { + registerRouter, + registerOnPostAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + registerOnPostAuth((req, res, t) => { - // @ts-ignore. don't complain customField is not defined on Request type - req.customField = { value: 42 }; + // don't complain customField is not defined on Request type + (req as any).customField = { value: 42 }; return t.next(); }); registerOnPostAuth((req, res, t) => { - // @ts-ignore don't complain customField is not defined on Request type - if (typeof req.customField !== 'undefined') { + // don't complain customField is not defined on Request type + if (typeof (req as any).customField !== 'undefined') { throw new Error('Request object was mutated'); } return t.next(); }); - const router = new Router('/'); - router.get({ path: '/', validate: false }, async (req, res) => - // @ts-ignore. don't complain customField is not defined on Request type - res.ok({ customField: String(req.customField) }) + + router.get({ path: '/', validate: false }, (context, req, res) => + // don't complain customField is not defined on Request type + res.ok({ customField: String((req as any).customField) }) ); registerRouter(router); await server.start(); @@ -404,17 +494,23 @@ describe('Auth', () => { }; it('registers auth request interceptor only once', async () => { - const { registerAuth } = await server.setup(config); + const { registerAuth } = await server.setup(); const doRegister = () => registerAuth(() => null as any); - doRegister(); + expect(doRegister).toThrowError('Auth interceptor was already registered'); }); it('may grant access to a resource', async () => { - const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false }, async (req, res) => res.ok({ content: 'ok' })); + const { + registerRouter, + registerAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); registerRouter(router); registerAuth((req, res, t) => t.authenticated()); @@ -426,10 +522,15 @@ describe('Auth', () => { }); it('enables auth for a route by default if registerAuth has been called', async () => { - const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); + const { + registerRouter, + registerAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); - const router = new Router(''); - router.get({ path: '/', validate: false }, (req, res) => + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ authRequired: req.route.options.authRequired }) ); registerRouter(router); @@ -446,11 +547,17 @@ describe('Auth', () => { }); test('supports disabling auth for a route explicitly', async () => { - const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); + const { + registerRouter, + registerAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); - const router = new Router(''); - router.get({ path: '/', validate: false, options: { authRequired: false } }, (req, res) => - res.ok({ authRequired: req.route.options.authRequired }) + router.get( + { path: '/', validate: false, options: { authRequired: false } }, + (context, req, res) => res.ok({ authRequired: req.route.options.authRequired }) ); registerRouter(router); const authenticate = jest.fn(); @@ -465,11 +572,17 @@ describe('Auth', () => { }); test('supports enabling auth for a route explicitly', async () => { - const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); + const { + registerRouter, + registerAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); - const router = new Router(''); - router.get({ path: '/', validate: false, options: { authRequired: true } }, (req, res) => - res.ok({ authRequired: req.route.options.authRequired }) + router.get( + { path: '/', validate: false, options: { authRequired: true } }, + (context, req, res) => res.ok({ authRequired: req.route.options.authRequired }) ); registerRouter(router); const authenticate = jest.fn().mockImplementation((req, res, t) => t.authenticated({})); @@ -484,9 +597,15 @@ describe('Auth', () => { }); it('supports rejecting a request from an unauthenticated user', async () => { - const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false }, async (req, res) => res.ok({ content: 'ok' })); + const { + registerRouter, + registerAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); registerRouter(router); registerAuth((req, res) => res.unauthorized()); @@ -498,12 +617,18 @@ describe('Auth', () => { }); it('supports redirecting', async () => { - const redirectTo = '/redirect-url'; - const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false }, async (req, res) => res.ok({ content: 'ok' })); + const { + registerRouter, + registerAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); registerRouter(router); + const redirectTo = '/redirect-url'; registerAuth((req, res) => res.redirected(undefined, { headers: { @@ -520,9 +645,15 @@ describe('Auth', () => { }); it(`doesn't expose internal error details`, async () => { - const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); - const router = new Router(''); - router.get({ path: '/', validate: false }, async (req, res) => res.ok({ content: 'ok' })); + const { + registerRouter, + registerAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); registerRouter(router); registerAuth((req, t) => { @@ -545,15 +676,17 @@ describe('Auth', () => { }); it('allows manipulating cookies via cookie session storage', async () => { - const router = new Router(''); - router.get({ path: '/', validate: false }, async (req, res) => res.ok({ content: 'ok' })); - const { createCookieSessionStorageFactory, - registerAuth, registerRouter, + registerAuth, server: innerServer, - } = await server.setup(config); + createRouter, + } = await server.setup(); + const router = createRouter('/'); + + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); + const sessionStorageFactory = await createCookieSessionStorageFactory( cookieOptions ); @@ -588,10 +721,13 @@ describe('Auth', () => { it('allows manipulating cookies from route handler', async () => { const { createCookieSessionStorageFactory, - registerAuth, registerRouter, + registerAuth, server: innerServer, - } = await server.setup(config); + createRouter, + } = await server.setup(); + const router = createRouter('/'); + const sessionStorageFactory = await createCookieSessionStorageFactory( cookieOptions ); @@ -602,9 +738,8 @@ describe('Auth', () => { return toolkit.authenticated(); }); - const router = new Router(''); - router.get({ path: '/', validate: false }, (req, res) => res.ok({ content: 'ok' })); - router.get({ path: '/with-cookie', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); + router.get({ path: '/with-cookie', validate: false }, (context, req, res) => { const sessionStorage = sessionStorageFactory.asScoped(req); sessionStorage.clear(); return res.ok({ content: 'ok' }); @@ -629,14 +764,15 @@ describe('Auth', () => { }); it.skip('is the only place with access to the authorization header', async () => { - const token = 'Basic: user:password'; const { - registerAuth, + registerRouter, registerOnPreAuth, + registerAuth, registerOnPostAuth, - registerRouter, server: innerServer, - } = await server.setup(config); + createRouter, + } = await server.setup(); + const router = createRouter('/'); let fromRegisterOnPreAuth; await registerOnPreAuth((req, res, toolkit) => { @@ -657,8 +793,8 @@ describe('Auth', () => { }); let fromRouteHandler; - const router = new Router(''); - router.get({ path: '/', validate: false }, (req, res) => { + + router.get({ path: '/', validate: false }, (context, req, res) => { fromRouteHandler = req.headers.authorization; return res.ok({ content: 'ok' }); }); @@ -666,6 +802,7 @@ describe('Auth', () => { await server.start(); + const token = 'Basic: user:password'; await supertest(innerServer.listener) .get('/') .set('Authorization', token) @@ -678,17 +815,22 @@ describe('Auth', () => { }); it('attach security header to a successful response', async () => { + const { + registerRouter, + registerAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + const authResponseHeader = { 'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca', }; - const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); - registerAuth((req, res, toolkit) => { return toolkit.authenticated({ responseHeaders: authResponseHeader }); }); - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok({ header: 'ok' })); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ header: 'ok' })); registerRouter(router); await server.start(); @@ -701,17 +843,24 @@ describe('Auth', () => { }); it('attach security header to an error response', async () => { + const { + registerRouter, + registerAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); const authResponseHeader = { 'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca', }; - const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); registerAuth((req, res, toolkit) => { return toolkit.authenticated({ responseHeaders: authResponseHeader }); }); - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.badRequest(new Error('reason'))); + router.get({ path: '/', validate: false }, (context, req, res) => + res.badRequest(new Error('reason')) + ); registerRouter(router); await server.start(); @@ -724,17 +873,23 @@ describe('Auth', () => { }); it('logs warning if Auth Security Header rewrites response header for success response', async () => { + const { + registerRouter, + registerAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + const authResponseHeader = { 'www-authenticate': 'from auth interceptor', }; - const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); registerAuth((req, res, toolkit) => { return toolkit.authenticated({ responseHeaders: authResponseHeader }); }); - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => + router.get({ path: '/', validate: false }, (context, req, res) => res.ok( {}, { @@ -763,17 +918,23 @@ describe('Auth', () => { }); it('logs warning if Auth Security Header rewrites response header for error response', async () => { + const { + registerRouter, + registerAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + const authResponseHeader = { 'www-authenticate': 'from auth interceptor', }; - const { registerAuth, registerRouter, server: innerServer } = await server.setup(config); registerAuth((req, res, toolkit) => { return toolkit.authenticated({ responseHeaders: authResponseHeader }); }); - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => + router.get({ path: '/', validate: false }, (context, req, res) => res.badRequest('reason', { headers: { 'www-authenticate': 'from handler', @@ -799,11 +960,16 @@ describe('Auth', () => { }); it('supports redirection from the interceptor', async () => { - const router = new Router('/'); - const redirectUrl = '/redirectUrl'; - router.get({ path: '/initial', validate: false }, (req, res) => res.ok('initial')); + const { + registerRouter, + registerOnPostAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); - const { registerRouter, registerOnPostAuth, server: innerServer } = await server.setup(config); + const redirectUrl = '/redirectUrl'; + router.get({ path: '/initial', validate: false }, (context, req, res) => res.ok('initial')); registerRouter(router); registerOnPostAuth((req, res, t) => @@ -823,10 +989,16 @@ describe('Auth', () => { }); it('supports rejecting request and adjusting response headers', async () => { - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok(undefined)); + const { + registerRouter, + registerOnPostAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + + router.get({ path: '/', validate: false }, (context, req, res) => res.ok(undefined)); - const { registerRouter, registerOnPostAuth, server: innerServer } = await server.setup(config); registerRouter(router); registerOnPostAuth((req, res, t) => @@ -846,10 +1018,15 @@ describe('Auth', () => { }); it("doesn't expose error details if interceptor throws", async () => { - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok(undefined)); + const { + registerRouter, + registerOnPostAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); - const { registerRouter, registerOnPostAuth, server: innerServer } = await server.setup(config); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok(undefined)); registerRouter(router); registerOnPostAuth((req, res, t) => { @@ -872,10 +1049,15 @@ describe('Auth', () => { }); it('returns internal error if interceptor returns unexpected result', async () => { - const router = new Router('/'); - router.get({ path: '/', validate: false }, (req, res) => res.ok('ok')); + const { + registerRouter, + registerOnPostAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); - const { registerRouter, registerOnPostAuth, server: innerServer } = await server.setup(config); + router.get({ path: '/', validate: false }, (context, req, res) => res.ok('ok')); registerRouter(router); registerOnPostAuth((req, res, t) => ({} as any)); @@ -895,23 +1077,29 @@ describe('Auth', () => { `); }); it(`doesn't share request object between interceptors`, async () => { - const { registerRouter, registerOnPostAuth, server: innerServer } = await server.setup(config); + const { + registerRouter, + registerOnPostAuth, + server: innerServer, + createRouter, + } = await server.setup(); + const router = createRouter('/'); + registerOnPostAuth((req, res, t) => { - // @ts-ignore. don't complain customField is not defined on Request type - req.customField = { value: 42 }; + // don't complain customField is not defined on Request type + (req as any).customField = { value: 42 }; return t.next(); }); registerOnPostAuth((req, res, t) => { - // @ts-ignore don't complain customField is not defined on Request type - if (typeof req.customField !== 'undefined') { + // don't complain customField is not defined on Request type + if (typeof (req as any).customField !== 'undefined') { throw new Error('Request object was mutated'); } return t.next(); }); - const router = new Router('/'); - router.get({ path: '/', validate: false }, async (req, res) => - // @ts-ignore. don't complain customField is not defined on Request type - res.ok({ customField: String(req.customField) }) + router.get({ path: '/', validate: false }, (context, req, res) => + // don't complain customField is not defined on Request type + res.ok({ customField: String((req as any).customField) }) ); registerRouter(router); await server.start(); diff --git a/src/core/server/http/integration_tests/router.test.ts b/src/core/server/http/integration_tests/router.test.ts index ec623a6d6d82a6..e9755175537bba 100644 --- a/src/core/server/http/integration_tests/router.test.ts +++ b/src/core/server/http/integration_tests/router.test.ts @@ -23,9 +23,9 @@ import supertest from 'supertest'; import { BehaviorSubject } from 'rxjs'; import { ByteSizeValue, schema } from '@kbn/config-schema'; -import { CoreContext } from '../../core_context'; import { HttpService } from '../http_service'; +import { CoreContext } from '../../core_context'; import { Env } from '../../config'; import { getEnvOptions } from '../../config/__mocks__/env'; import { configServiceMock } from '../../config/config_service.mock'; @@ -40,15 +40,11 @@ const configService = configServiceMock.create(); configService.atPath.mockReturnValue( new BehaviorSubject({ - hosts: ['http://1.2.3.4'], + hosts: ['localhost'], maxPayload: new ByteSizeValue(1024), autoListen: true, - healthCheck: { - delay: 2000, - }, ssl: { enabled: false, - verificationMode: 'none', }, } as any) ); @@ -57,7 +53,7 @@ beforeEach(() => { logger = loggingServiceMock.create(); env = Env.createDefault(getEnvOptions()); - coreContext = { env, logger, configService: configService as any }; + coreContext = { coreId: Symbol('core'), env, logger, configService: configService as any }; server = new HttpService(coreContext); }); @@ -787,16 +783,16 @@ describe('Response factory', () => { }); it('Custom error response', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); - router.get({ path: '/', validate: false }, (req, res) => { + const router = createRouter('/'); + router.get({ path: '/', validate: false }, (context, req, res) => { const error = new Error('some message'); return res.customError(error, { statusCode: 418, }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -812,9 +808,10 @@ describe('Response factory', () => { }); it('Custom error response for server error', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const error = new Error('some message'); return res.customError(error, { @@ -822,7 +819,6 @@ describe('Response factory', () => { }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -838,9 +834,10 @@ describe('Response factory', () => { }); it('Custom error response for Boom server error', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const error = new Error('some message'); return res.customError(Boom.boomify(error), { @@ -848,7 +845,6 @@ describe('Response factory', () => { }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); @@ -864,16 +860,16 @@ describe('Response factory', () => { }); it('Custom error response requires error status code', async () => { - const router = new Router('/'); + const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const router = createRouter('/'); - router.get({ path: '/', validate: false }, (req, res) => { + router.get({ path: '/', validate: false }, (context, req, res) => { const error = new Error('some message'); return res.customError(error, { statusCode: 200, }); }); - const { registerRouter, server: innerServer } = await server.setup(config); registerRouter(router); await server.start(); From 255a9b171bb3a7a0f74a583f43281b576ec4a729 Mon Sep 17 00:00:00 2001 From: restrry Date: Thu, 8 Aug 2019 15:26:11 +0200 Subject: [PATCH 5/8] re-genereated docs --- .../kibana-plugin-server.coresetup.http.md | 2 + .../server/kibana-plugin-server.coresetup.md | 2 +- .../kibana-plugin-server.httpserversetup.md | 12 +++-- ...n-server.httpserversetup.registerrouter.md | 2 +- .../kibana-plugin-server.httpservicesetup.md | 4 +- .../kibana-plugin-server.irouter.delete.md | 13 ++++++ .../kibana-plugin-server.irouter.get.md | 13 ++++++ .../server/kibana-plugin-server.irouter.md | 24 ++++++++++ .../kibana-plugin-server.irouter.post.md | 13 ++++++ .../kibana-plugin-server.irouter.put.md | 13 ++++++ ...kibana-plugin-server.irouter.routerpath.md | 13 ++++++ .../core/server/kibana-plugin-server.md | 2 +- .../kibana-plugin-server.requesthandler.md | 8 ++-- ...bana-plugin-server.router.(constructor).md | 20 --------- .../kibana-plugin-server.router.delete.md | 25 ----------- .../server/kibana-plugin-server.router.get.md | 25 ----------- .../server/kibana-plugin-server.router.md | 45 ------------------- .../kibana-plugin-server.router.path.md | 11 ----- .../kibana-plugin-server.router.post.md | 25 ----------- .../server/kibana-plugin-server.router.put.md | 25 ----------- src/core/server/http/router/router.ts | 7 +++ src/core/server/server.api.md | 38 ++++++++-------- 22 files changed, 133 insertions(+), 209 deletions(-) create mode 100644 docs/development/core/server/kibana-plugin-server.irouter.delete.md create mode 100644 docs/development/core/server/kibana-plugin-server.irouter.get.md create mode 100644 docs/development/core/server/kibana-plugin-server.irouter.md create mode 100644 docs/development/core/server/kibana-plugin-server.irouter.post.md create mode 100644 docs/development/core/server/kibana-plugin-server.irouter.put.md create mode 100644 docs/development/core/server/kibana-plugin-server.irouter.routerpath.md delete mode 100644 docs/development/core/server/kibana-plugin-server.router.(constructor).md delete mode 100644 docs/development/core/server/kibana-plugin-server.router.delete.md delete mode 100644 docs/development/core/server/kibana-plugin-server.router.get.md delete mode 100644 docs/development/core/server/kibana-plugin-server.router.md delete mode 100644 docs/development/core/server/kibana-plugin-server.router.path.md delete mode 100644 docs/development/core/server/kibana-plugin-server.router.post.md delete mode 100644 docs/development/core/server/kibana-plugin-server.router.put.md diff --git a/docs/development/core/server/kibana-plugin-server.coresetup.http.md b/docs/development/core/server/kibana-plugin-server.coresetup.http.md index e5347dd7c66259..bb11aef427b26c 100644 --- a/docs/development/core/server/kibana-plugin-server.coresetup.http.md +++ b/docs/development/core/server/kibana-plugin-server.coresetup.http.md @@ -14,5 +14,7 @@ http: { registerOnPostAuth: HttpServiceSetup['registerOnPostAuth']; basePath: HttpServiceSetup['basePath']; isTlsEnabled: HttpServiceSetup['isTlsEnabled']; + createRouter: () => IRouter; + registerRouter: (router: IRouter) => void; }; ``` diff --git a/docs/development/core/server/kibana-plugin-server.coresetup.md b/docs/development/core/server/kibana-plugin-server.coresetup.md index 8af0c6e62fb598..8eab68ef984ca4 100644 --- a/docs/development/core/server/kibana-plugin-server.coresetup.md +++ b/docs/development/core/server/kibana-plugin-server.coresetup.md @@ -18,5 +18,5 @@ export interface CoreSetup | --- | --- | --- | | [context](./kibana-plugin-server.coresetup.context.md) | {
createContextContainer: ContextSetup['createContextContainer'];
} | | | [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | {
adminClient$: Observable<ClusterClient>;
dataClient$: Observable<ClusterClient>;
createClient: (type: string, clientConfig?: Partial<ElasticsearchClientConfig>) => ClusterClient;
} | | -| [http](./kibana-plugin-server.coresetup.http.md) | {
createCookieSessionStorageFactory: HttpServiceSetup['createCookieSessionStorageFactory'];
registerOnPreAuth: HttpServiceSetup['registerOnPreAuth'];
registerAuth: HttpServiceSetup['registerAuth'];
registerOnPostAuth: HttpServiceSetup['registerOnPostAuth'];
basePath: HttpServiceSetup['basePath'];
isTlsEnabled: HttpServiceSetup['isTlsEnabled'];
} | | +| [http](./kibana-plugin-server.coresetup.http.md) | {
createCookieSessionStorageFactory: HttpServiceSetup['createCookieSessionStorageFactory'];
registerOnPreAuth: HttpServiceSetup['registerOnPreAuth'];
registerAuth: HttpServiceSetup['registerAuth'];
registerOnPostAuth: HttpServiceSetup['registerOnPostAuth'];
basePath: HttpServiceSetup['basePath'];
isTlsEnabled: HttpServiceSetup['isTlsEnabled'];
createRouter: () => IRouter;
registerRouter: (router: IRouter) => void;
} | | diff --git a/docs/development/core/server/kibana-plugin-server.httpserversetup.md b/docs/development/core/server/kibana-plugin-server.httpserversetup.md index 143ae66c0b32cc..98edda8f15f415 100644 --- a/docs/development/core/server/kibana-plugin-server.httpserversetup.md +++ b/docs/development/core/server/kibana-plugin-server.httpserversetup.md @@ -23,16 +23,15 @@ export interface HttpServerSetup | [registerAuth](./kibana-plugin-server.httpserversetup.registerauth.md) | (handler: AuthenticationHandler) => void | To define custom authentication and/or authorization mechanism for incoming requests. A handler should return a state to associate with the incoming request. The state can be retrieved later via http.auth.get(..) Only one AuthenticationHandler can be registered. | | [registerOnPostAuth](./kibana-plugin-server.httpserversetup.registeronpostauth.md) | (handler: OnPostAuthHandler) => void | To define custom logic to perform for incoming requests. Runs the handler after Auth interceptor did make sure a user has access to the requested resource. The auth state is available at stage via http.auth.get(..) Can register any number of registerOnPreAuth, which are called in sequence (from the first registered to the last). | | [registerOnPreAuth](./kibana-plugin-server.httpserversetup.registeronpreauth.md) | (handler: OnPreAuthHandler) => void | To define custom logic to perform for incoming requests. Runs the handler before Auth interceptor performs a check that user has access to requested resources, so it's the only place when you can forward a request to another URL right on the server. Can register any number of registerOnPostAuth, which are called in sequence (from the first registered to the last). | -| [registerRouter](./kibana-plugin-server.httpserversetup.registerrouter.md) | (router: Router) => void | Add all the routes registered with router to HTTP server request listeners. | +| [registerRouter](./kibana-plugin-server.httpserversetup.registerrouter.md) | (router: IRouter) => void | Add all the routes registered with router to HTTP server request listeners. | | [server](./kibana-plugin-server.httpserversetup.server.md) | Server | | ## Example -To handle an incoming request in your plugin you should: - Create a `Router` instance. Use `plugin-id` as a prefix path segment for your routes. +To handle an incoming request in your plugin you should: - Create a `Router` instance. Router is already configured to use `plugin-id` to prefix path segment for your routes. ```ts -import { Router } from 'src/core/server'; -const router = new Router('my-app'); +const router = httpSetup.createRouter(); ``` - Use `@kbn/config-schema` package to create a schema to validate the request `params`, `query`, and `body`. Every incoming request will be validated against the created schema. If validation failed, the request is rejected with `400` status and `Bad request` error without calling the route's handler. To opt out of validating the request, specify `false`. @@ -66,8 +65,7 @@ const handler = async (request: KibanaRequest, response: ResponseFactory) => { ```ts import { schema, TypeOf } from '@kbn/config-schema'; -import { Router } from 'src/core/server'; -const router = new Router('my-app'); +const router = httpSetup.createRouter(); const validate = { params: schema.object({ @@ -79,7 +77,7 @@ router.get({ path: 'path/{id}', validate }, -async (request, response) => { +async (context, request, response) => { const data = await findObject(request.params.id); if (!data) return response.notFound(); return response.ok(data, { diff --git a/docs/development/core/server/kibana-plugin-server.httpserversetup.registerrouter.md b/docs/development/core/server/kibana-plugin-server.httpserversetup.registerrouter.md index 4c2a9ae3274068..32daa650f8d5d9 100644 --- a/docs/development/core/server/kibana-plugin-server.httpserversetup.registerrouter.md +++ b/docs/development/core/server/kibana-plugin-server.httpserversetup.registerrouter.md @@ -9,5 +9,5 @@ Add all the routes registered with `router` to HTTP server request listeners. Signature: ```typescript -registerRouter: (router: Router) => void; +registerRouter: (router: IRouter) => void; ``` diff --git a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md index 7e8f17510c8eed..0ac85f535f47a4 100644 --- a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md +++ b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md @@ -8,5 +8,7 @@ Signature: ```typescript -export declare type HttpServiceSetup = HttpServerSetup; +export declare type HttpServiceSetup = HttpServerSetup & { + createRouter: (path: string) => IRouter; +}; ``` diff --git a/docs/development/core/server/kibana-plugin-server.irouter.delete.md b/docs/development/core/server/kibana-plugin-server.irouter.delete.md new file mode 100644 index 00000000000000..9124b4a1b21c4c --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.irouter.delete.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [IRouter](./kibana-plugin-server.irouter.md) > [delete](./kibana-plugin-server.irouter.delete.md) + +## IRouter.delete property + +Register a route handler for `DELETE` request. + +Signature: + +```typescript +delete:

(route: RouteConfig, handler: RequestHandler) => void; +``` diff --git a/docs/development/core/server/kibana-plugin-server.irouter.get.md b/docs/development/core/server/kibana-plugin-server.irouter.get.md new file mode 100644 index 00000000000000..0291906c6fc6b9 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.irouter.get.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [IRouter](./kibana-plugin-server.irouter.md) > [get](./kibana-plugin-server.irouter.get.md) + +## IRouter.get property + +Register a route handler for `GET` request. + +Signature: + +```typescript +get:

(route: RouteConfig, handler: RequestHandler) => void; +``` diff --git a/docs/development/core/server/kibana-plugin-server.irouter.md b/docs/development/core/server/kibana-plugin-server.irouter.md new file mode 100644 index 00000000000000..2fb2d30eb31d72 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.irouter.md @@ -0,0 +1,24 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [IRouter](./kibana-plugin-server.irouter.md) + +## IRouter interface + +Registers route handlers for specified resource path and method. + +Signature: + +```typescript +export interface IRouter +``` + +## Properties + +| Property | Type | Description | +| --- | --- | --- | +| [delete](./kibana-plugin-server.irouter.delete.md) | <P extends ObjectType, Q extends ObjectType, B extends ObjectType>(route: RouteConfig<P, Q, B>, handler: RequestHandler<P, Q, B>) => void | Register a route handler for DELETE request. | +| [get](./kibana-plugin-server.irouter.get.md) | <P extends ObjectType, Q extends ObjectType, B extends ObjectType>(route: RouteConfig<P, Q, B>, handler: RequestHandler<P, Q, B>) => void | Register a route handler for GET request. | +| [post](./kibana-plugin-server.irouter.post.md) | <P extends ObjectType, Q extends ObjectType, B extends ObjectType>(route: RouteConfig<P, Q, B>, handler: RequestHandler<P, Q, B>) => void | Register a route handler for POST request. | +| [put](./kibana-plugin-server.irouter.put.md) | <P extends ObjectType, Q extends ObjectType, B extends ObjectType>(route: RouteConfig<P, Q, B>, handler: RequestHandler<P, Q, B>) => void | Register a route handler for PUT request. | +| [routerPath](./kibana-plugin-server.irouter.routerpath.md) | string | Resulted path | + diff --git a/docs/development/core/server/kibana-plugin-server.irouter.post.md b/docs/development/core/server/kibana-plugin-server.irouter.post.md new file mode 100644 index 00000000000000..e97a32e433ce95 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.irouter.post.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [IRouter](./kibana-plugin-server.irouter.md) > [post](./kibana-plugin-server.irouter.post.md) + +## IRouter.post property + +Register a route handler for `POST` request. + +Signature: + +```typescript +post:

(route: RouteConfig, handler: RequestHandler) => void; +``` diff --git a/docs/development/core/server/kibana-plugin-server.irouter.put.md b/docs/development/core/server/kibana-plugin-server.irouter.put.md new file mode 100644 index 00000000000000..25db91e3899397 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.irouter.put.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [IRouter](./kibana-plugin-server.irouter.md) > [put](./kibana-plugin-server.irouter.put.md) + +## IRouter.put property + +Register a route handler for `PUT` request. + +Signature: + +```typescript +put:

(route: RouteConfig, handler: RequestHandler) => void; +``` diff --git a/docs/development/core/server/kibana-plugin-server.irouter.routerpath.md b/docs/development/core/server/kibana-plugin-server.irouter.routerpath.md new file mode 100644 index 00000000000000..ab1b4a6baa7e95 --- /dev/null +++ b/docs/development/core/server/kibana-plugin-server.irouter.routerpath.md @@ -0,0 +1,13 @@ + + +[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [IRouter](./kibana-plugin-server.irouter.md) > [routerPath](./kibana-plugin-server.irouter.routerpath.md) + +## IRouter.routerPath property + +Resulted path + +Signature: + +```typescript +routerPath: string; +``` diff --git a/docs/development/core/server/kibana-plugin-server.md b/docs/development/core/server/kibana-plugin-server.md index d5e8b899d26e85..db561bf909b3e2 100644 --- a/docs/development/core/server/kibana-plugin-server.md +++ b/docs/development/core/server/kibana-plugin-server.md @@ -19,7 +19,6 @@ The plugin integrates with the core system via lifecycle events: `setup` | [ClusterClient](./kibana-plugin-server.clusterclient.md) | Represents an Elasticsearch cluster API client and allows to call API on behalf of the internal Kibana user and the actual user that is derived from the request headers (via asScoped(...)). | | [ElasticsearchErrorHelpers](./kibana-plugin-server.elasticsearcherrorhelpers.md) | Helpers for working with errors returned from the Elasticsearch service.Since the internal data of errors are subject to change, consumers of the Elasticsearch service should always use these helpers to classify errors instead of checking error internals such as body.error.header[WWW-Authenticate] | | [KibanaRequest](./kibana-plugin-server.kibanarequest.md) | Kibana specific abstraction for an incoming request. | -| [Router](./kibana-plugin-server.router.md) | Provides ability to declare a handler function for a particular path and HTTP request method. Each route can have only one handler functions, which is executed when the route is matched. | | [SavedObjectsErrorHelpers](./kibana-plugin-server.savedobjectserrorhelpers.md) | | | [SavedObjectsSchema](./kibana-plugin-server.savedobjectsschema.md) | | | [SavedObjectsSerializer](./kibana-plugin-server.savedobjectsserializer.md) | | @@ -50,6 +49,7 @@ The plugin integrates with the core system via lifecycle events: `setup` | [HttpServerSetup](./kibana-plugin-server.httpserversetup.md) | Kibana HTTP Service provides own abstraction for work with HTTP stack. Plugins don't have direct access to hapi server and its primitives anymore. Moreover, plugins shouldn't rely on the fact that HTTP Service uses one or another library under the hood. This gives the platform flexibility to upgrade or changing our internal HTTP stack without breaking plugins. If the HTTP Service lacks functionality you need, we are happy to discuss and support your needs. | | [HttpServiceStart](./kibana-plugin-server.httpservicestart.md) | | | [InternalCoreStart](./kibana-plugin-server.internalcorestart.md) | | +| [IRouter](./kibana-plugin-server.irouter.md) | Registers route handlers for specified resource path and method. | | [KibanaRequestRoute](./kibana-plugin-server.kibanarequestroute.md) | Request specific route information exposed to a handler. | | [LegacyRequest](./kibana-plugin-server.legacyrequest.md) | | | [Logger](./kibana-plugin-server.logger.md) | Logger exposes all the necessary methods to log any type of information and this is the interface used by the logging consumers including plugins. | diff --git a/docs/development/core/server/kibana-plugin-server.requesthandler.md b/docs/development/core/server/kibana-plugin-server.requesthandler.md index b7e593c30f2f3c..bde197fd0f0940 100644 --- a/docs/development/core/server/kibana-plugin-server.requesthandler.md +++ b/docs/development/core/server/kibana-plugin-server.requesthandler.md @@ -9,14 +9,14 @@ A function executed when route path matched requested resource path. Request han Signature: ```typescript -export declare type RequestHandler

= (request: KibanaRequest, TypeOf, TypeOf>, response: KibanaResponseFactory) => KibanaResponse | Promise>; +export declare type RequestHandler

= (context: {}, request: KibanaRequest, TypeOf, TypeOf>, response: KibanaResponseFactory) => KibanaResponse | Promise>; ``` ## Example ```ts -const router = new Router('my-app'); +const router = httpSetup.createRouter(); // creates a route handler for GET request on 'my-app/path/{id}' path router.get( { @@ -29,8 +29,8 @@ router.get( }, }, // function to execute to create a responses - async (request, response) => { - const data = await findObject(request.params.id); + async (context, request, response) => { + const data = await context.findObject(request.params.id); // creates a command to respond with 'not found' error if (!data) return response.notFound(); // creates a command to send found data to the client diff --git a/docs/development/core/server/kibana-plugin-server.router.(constructor).md b/docs/development/core/server/kibana-plugin-server.router.(constructor).md deleted file mode 100644 index 5f8e1e5e293ab7..00000000000000 --- a/docs/development/core/server/kibana-plugin-server.router.(constructor).md +++ /dev/null @@ -1,20 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [Router](./kibana-plugin-server.router.md) > [(constructor)](./kibana-plugin-server.router.(constructor).md) - -## Router.(constructor) - -Constructs a new instance of the `Router` class - -Signature: - -```typescript -constructor(path: string); -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| path | string | | - diff --git a/docs/development/core/server/kibana-plugin-server.router.delete.md b/docs/development/core/server/kibana-plugin-server.router.delete.md deleted file mode 100644 index 565dc10ce76e83..00000000000000 --- a/docs/development/core/server/kibana-plugin-server.router.delete.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [Router](./kibana-plugin-server.router.md) > [delete](./kibana-plugin-server.router.delete.md) - -## Router.delete() method - -Register a route handler for `DELETE` request. - -Signature: - -```typescript -delete

(route: RouteConfig, handler: RequestHandler): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| route | RouteConfig<P, Q, B> | | -| handler | RequestHandler<P, Q, B> | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-server.router.get.md b/docs/development/core/server/kibana-plugin-server.router.get.md deleted file mode 100644 index a3899eaa678f7c..00000000000000 --- a/docs/development/core/server/kibana-plugin-server.router.get.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [Router](./kibana-plugin-server.router.md) > [get](./kibana-plugin-server.router.get.md) - -## Router.get() method - -Register a route handler for `GET` request. - -Signature: - -```typescript -get

(route: RouteConfig, handler: RequestHandler): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| route | RouteConfig<P, Q, B> | | -| handler | RequestHandler<P, Q, B> | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-server.router.md b/docs/development/core/server/kibana-plugin-server.router.md deleted file mode 100644 index 59a0a22ec7b5e1..00000000000000 --- a/docs/development/core/server/kibana-plugin-server.router.md +++ /dev/null @@ -1,45 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [Router](./kibana-plugin-server.router.md) - -## Router class - -Provides ability to declare a handler function for a particular path and HTTP request method. Each route can have only one handler functions, which is executed when the route is matched. - -Signature: - -```typescript -export declare class Router -``` - -## Constructors - -| Constructor | Modifiers | Description | -| --- | --- | --- | -| [(constructor)(path)](./kibana-plugin-server.router.(constructor).md) | | Constructs a new instance of the Router class | - -## Properties - -| Property | Modifiers | Type | Description | -| --- | --- | --- | --- | -| [path](./kibana-plugin-server.router.path.md) | | string | | - -## Methods - -| Method | Modifiers | Description | -| --- | --- | --- | -| [delete(route, handler)](./kibana-plugin-server.router.delete.md) | | Register a route handler for DELETE request. | -| [get(route, handler)](./kibana-plugin-server.router.get.md) | | Register a route handler for GET request. | -| [post(route, handler)](./kibana-plugin-server.router.post.md) | | Register a route handler for POST request. | -| [put(route, handler)](./kibana-plugin-server.router.put.md) | | Register a route handler for PUT request. | - -## Example - - -```ts -const router = new Router('my-app'); -// handler is called when 'my-app/path' resource is requested with `GET` method -router.get({ path: '/path', validate: false }, (req, res) => res.ok({ content: 'ok' })); - -``` - diff --git a/docs/development/core/server/kibana-plugin-server.router.path.md b/docs/development/core/server/kibana-plugin-server.router.path.md deleted file mode 100644 index bc799e6abfad56..00000000000000 --- a/docs/development/core/server/kibana-plugin-server.router.path.md +++ /dev/null @@ -1,11 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [Router](./kibana-plugin-server.router.md) > [path](./kibana-plugin-server.router.path.md) - -## Router.path property - -Signature: - -```typescript -readonly path: string; -``` diff --git a/docs/development/core/server/kibana-plugin-server.router.post.md b/docs/development/core/server/kibana-plugin-server.router.post.md deleted file mode 100644 index 7aca35466d643a..00000000000000 --- a/docs/development/core/server/kibana-plugin-server.router.post.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [Router](./kibana-plugin-server.router.md) > [post](./kibana-plugin-server.router.post.md) - -## Router.post() method - -Register a route handler for `POST` request. - -Signature: - -```typescript -post

(route: RouteConfig, handler: RequestHandler): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| route | RouteConfig<P, Q, B> | | -| handler | RequestHandler<P, Q, B> | | - -Returns: - -`void` - diff --git a/docs/development/core/server/kibana-plugin-server.router.put.md b/docs/development/core/server/kibana-plugin-server.router.put.md deleted file mode 100644 index 760ccf9ef88e8d..00000000000000 --- a/docs/development/core/server/kibana-plugin-server.router.put.md +++ /dev/null @@ -1,25 +0,0 @@ - - -[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [Router](./kibana-plugin-server.router.md) > [put](./kibana-plugin-server.router.put.md) - -## Router.put() method - -Register a route handler for `PUT` request. - -Signature: - -```typescript -put

(route: RouteConfig, handler: RequestHandler): void; -``` - -## Parameters - -| Parameter | Type | Description | -| --- | --- | --- | -| route | RouteConfig<P, Q, B> | | -| handler | RequestHandler<P, Q, B> | | - -Returns: - -`void` - diff --git a/src/core/server/http/router/router.ts b/src/core/server/http/router/router.ts index 645cebf0c30e18..0b2e54a13624aa 100644 --- a/src/core/server/http/router/router.ts +++ b/src/core/server/http/router/router.ts @@ -34,6 +34,10 @@ interface RouterRoute { handler: (req: Request, responseToolkit: ResponseToolkit) => Promise>; } +/** + * Registers route handlers for specified resource path and method. + * @public + */ export interface IRouter { /** * Resulted path @@ -49,6 +53,7 @@ export interface IRouter { route: RouteConfig, handler: RequestHandler ) => void; + /** * Register a route handler for `POST` request. * @param route {@link RouteConfig} - a route configuration. @@ -58,6 +63,7 @@ export interface IRouter { route: RouteConfig, handler: RequestHandler ) => void; + /** * Register a route handler for `PUT` request. * @param route {@link RouteConfig} - a route configuration. @@ -77,6 +83,7 @@ export interface IRouter { route: RouteConfig, handler: RequestHandler ) => void; + /** * Returns all routes registered with the this router. * @returns List of registered routes. diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index a743bb7719b567..4bc6cdc48b5434 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -123,6 +123,8 @@ export interface CoreSetup { registerOnPostAuth: HttpServiceSetup['registerOnPostAuth']; basePath: HttpServiceSetup['basePath']; isTlsEnabled: HttpServiceSetup['isTlsEnabled']; + createRouter: () => IRouter; + registerRouter: (router: IRouter) => void; }; } @@ -233,13 +235,15 @@ export interface HttpServerSetup { registerAuth: (handler: AuthenticationHandler) => void; registerOnPostAuth: (handler: OnPostAuthHandler) => void; registerOnPreAuth: (handler: OnPreAuthHandler) => void; - registerRouter: (router: Router) => void; + registerRouter: (router: IRouter) => void; // (undocumented) server: Server; } // @public (undocumented) -export type HttpServiceSetup = HttpServerSetup; +export type HttpServiceSetup = HttpServerSetup & { + createRouter: (path: string) => IRouter; +}; // @public (undocumented) export interface HttpServiceStart { @@ -262,6 +266,19 @@ export interface InternalCoreStart { plugins: PluginsServiceStart; } +// @public +export interface IRouter { + delete:

(route: RouteConfig, handler: RequestHandler) => void; + get:

(route: RouteConfig, handler: RequestHandler) => void; + // Warning: (ae-forgotten-export) The symbol "RouterRoute" needs to be exported by the entry point index.d.ts + // + // @internal + getRoutes: () => RouterRoute[]; + post:

(route: RouteConfig, handler: RequestHandler) => void; + put:

(route: RouteConfig, handler: RequestHandler) => void; + routerPath: string; +} + // @public export type IsAuthenticated = (request: KibanaRequest | LegacyRequest) => boolean; @@ -504,7 +521,7 @@ export type RedirectResponseOptions = HttpResponseOptions & { }; // @public -export type RequestHandler

= (request: KibanaRequest, TypeOf, TypeOf>, response: KibanaResponseFactory) => KibanaResponse | Promise>; +export type RequestHandler

= (context: {}, request: KibanaRequest, TypeOf, TypeOf>, response: KibanaResponseFactory) => KibanaResponse | Promise>; // @public export type ResponseError = string | Error | { @@ -538,21 +555,6 @@ export interface RouteConfigOptions { // @public export type RouteMethod = 'get' | 'post' | 'put' | 'delete'; -// @public -export class Router { - constructor(path: string); - delete

(route: RouteConfig, handler: RequestHandler): void; - get

(route: RouteConfig, handler: RequestHandler): void; - // Warning: (ae-forgotten-export) The symbol "RouterRoute" needs to be exported by the entry point index.d.ts - // - // @internal - getRoutes(): Readonly[]; - // (undocumented) - readonly path: string; - post

(route: RouteConfig, handler: RequestHandler): void; - put

(route: RouteConfig, handler: RequestHandler): void; - } - // @public (undocumented) export interface SavedObject { attributes: T; From 8717cdb4f8b651b4cc777d3d2521028847fdd929 Mon Sep 17 00:00:00 2001 From: restrry Date: Fri, 9 Aug 2019 10:18:08 +0200 Subject: [PATCH 6/8] remove registerRouter from http service contract createRouter registers a router under the hood. that reduces API surface for consumers --- .../server/http/cookie_sesson_storage.test.ts | 19 +- src/core/server/http/http_service.mock.ts | 9 +- src/core/server/http/http_service.test.ts | 10 +- src/core/server/http/http_service.ts | 8 +- .../integration_tests/core_services.test.ts | 6 +- .../http/integration_tests/lifecycle.test.ts | 270 +++--------------- .../http/integration_tests/router.test.ts | 148 ++++------ src/core/server/index.ts | 1 - .../integration_tests/legacy_service.test.ts | 4 +- src/core/server/mocks.ts | 1 - src/core/server/plugins/plugin_context.ts | 2 - src/core/server/server.ts | 1 - .../on_request_interceptor.test.ts | 2 - 13 files changed, 107 insertions(+), 374 deletions(-) diff --git a/src/core/server/http/cookie_sesson_storage.test.ts b/src/core/server/http/cookie_sesson_storage.test.ts index ad8cac0decf379..0c4973f70bd251 100644 --- a/src/core/server/http/cookie_sesson_storage.test.ts +++ b/src/core/server/http/cookie_sesson_storage.test.ts @@ -98,7 +98,7 @@ const cookieOptions = { describe('Cookie based SessionStorage', () => { describe('#set()', () => { it('Should write to session storage & set cookies', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter(''); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -106,7 +106,6 @@ describe('Cookie based SessionStorage', () => { sessionStorage.set({ value: userData, expires: Date.now() + sessionDurationMs }); return res.ok({}); }); - registerRouter(router); const factory = await createCookieSessionStorageFactory( logger.get(), @@ -133,7 +132,7 @@ describe('Cookie based SessionStorage', () => { }); describe('#get()', () => { it('reads from session storage', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter(''); router.get({ path: '/', validate: false }, async (context, req, res) => { @@ -146,8 +145,6 @@ describe('Cookie based SessionStorage', () => { return res.ok({ value: sessionValue.value }); }); - registerRouter(router); - const factory = await createCookieSessionStorageFactory( logger.get(), innerServer, @@ -171,7 +168,7 @@ describe('Cookie based SessionStorage', () => { .expect(200, { value: userData }); }); it('returns null for empty session', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter(''); router.get({ path: '/', validate: false }, async (context, req, res) => { @@ -180,8 +177,6 @@ describe('Cookie based SessionStorage', () => { return res.ok({ value: sessionValue }); }); - registerRouter(router); - const factory = await createCookieSessionStorageFactory( logger.get(), innerServer, @@ -198,7 +193,7 @@ describe('Cookie based SessionStorage', () => { }); it('returns null for invalid session & clean cookies', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter(''); @@ -214,8 +209,6 @@ describe('Cookie based SessionStorage', () => { return res.ok({ value: sessionValue }); }); - registerRouter(router); - const factory = await createCookieSessionStorageFactory( logger.get(), innerServer, @@ -333,7 +326,7 @@ describe('Cookie based SessionStorage', () => { describe('#clear()', () => { it('clears session storage & remove cookies', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter(''); @@ -347,8 +340,6 @@ describe('Cookie based SessionStorage', () => { return res.ok({}); }); - registerRouter(router); - const factory = await createCookieSessionStorageFactory( logger.get(), innerServer, diff --git a/src/core/server/http/http_service.mock.ts b/src/core/server/http/http_service.mock.ts index d59fdaa9e02877..ee09e55200853a 100644 --- a/src/core/server/http/http_service.mock.ts +++ b/src/core/server/http/http_service.mock.ts @@ -46,13 +46,16 @@ const createRouterMock = (): jest.Mocked => ({ const createSetupContractMock = () => { const setupContract: ServiceSetupMockType = { - // we can mock some hapi server method when we need it - server: {} as Server, + // we can mock other hapi server methods when we need it + server: ({ + route: jest.fn(), + start: jest.fn(), + stop: jest.fn(), + } as unknown) as Server, createCookieSessionStorageFactory: jest.fn(), registerOnPreAuth: jest.fn(), registerAuth: jest.fn(), registerOnPostAuth: jest.fn(), - registerRouter: jest.fn(), createRouter: jest.fn(), basePath: createBasePathMock(), auth: { diff --git a/src/core/server/http/http_service.test.ts b/src/core/server/http/http_service.test.ts index 6259719723294f..d1e906f300d683 100644 --- a/src/core/server/http/http_service.test.ts +++ b/src/core/server/http/http_service.test.ts @@ -87,7 +87,7 @@ test('spins up notReady server until started if configured with `autoListen:true const configService = createConfigService(); const httpServer = { isListening: () => false, - setup: jest.fn(), + setup: jest.fn().mockReturnValue({}), start: jest.fn(), stop: jest.fn(), }; @@ -216,11 +216,9 @@ test('register route handler', async () => { const service = new HttpService({ coreId, configService, env, logger }); - const { registerRouter, createRouter } = await service.setup(); + const { createRouter } = await service.setup(); const router = createRouter('/foo'); - registerRouter(router); - expect(registerRouterMock).toHaveBeenCalledTimes(1); expect(registerRouterMock).toHaveBeenLastCalledWith(router); }); @@ -247,7 +245,7 @@ test('does not start http server if process is dev cluster master', async () => const configService = createConfigService(); const httpServer = { isListening: () => false, - setup: noop, + setup: jest.fn().mockReturnValue({}), start: jest.fn(), stop: noop, }; @@ -272,7 +270,7 @@ test('does not start http server if configured with `autoListen:false`', async ( }); const httpServer = { isListening: () => false, - setup: noop, + setup: jest.fn().mockReturnValue({}), start: jest.fn(), stop: noop, }; diff --git a/src/core/server/http/http_service.ts b/src/core/server/http/http_service.ts index 4013db490cc49e..211b11612de12e 100644 --- a/src/core/server/http/http_service.ts +++ b/src/core/server/http/http_service.ts @@ -33,7 +33,7 @@ import { HttpServer, HttpServerSetup } from './http_server'; import { HttpsRedirectServer } from './https_redirect_server'; /** @public */ -export type HttpServiceSetup = HttpServerSetup & { +export type HttpServiceSetup = Omit & { /** * Provides ability to declare a handler function for a particular path and HTTP request method. * Each route can have only one handler functions, which is executed when the route is matched. @@ -97,12 +97,14 @@ export class HttpService implements CoreService { - return new Router(path, this.log); + const router = new Router(path, this.log); + registerRouter(router); + return router; }, }; diff --git a/src/core/server/http/integration_tests/core_services.test.ts b/src/core/server/http/integration_tests/core_services.test.ts index b145e3f789113b..d5f7e262c418ab 100644 --- a/src/core/server/http/integration_tests/core_services.test.ts +++ b/src/core/server/http/integration_tests/core_services.test.ts @@ -253,7 +253,7 @@ describe('http service', () => { it('rewrites authorization header via authHeaders to make a request to Elasticsearch', async () => { const authHeaders = { authorization: 'Basic: user:password' }; const { http, elasticsearch } = await root.setup(); - const { registerAuth, registerRouter, createRouter } = http; + const { registerAuth, createRouter } = http; await registerAuth((req, res, toolkit) => toolkit.authenticated({ requestHeaders: authHeaders }) @@ -265,7 +265,6 @@ describe('http service', () => { client.asScoped(req); return res.ok({ header: 'ok' }); }); - registerRouter(router); await root.start(); @@ -279,7 +278,7 @@ describe('http service', () => { it('passes request authorization header to Elasticsearch if registerAuth was not set', async () => { const authorizationHeader = 'Basic: username:password'; const { http, elasticsearch } = await root.setup(); - const { registerRouter, createRouter } = http; + const { createRouter } = http; const router = createRouter('/new-platform'); router.get({ path: '/', validate: false }, async (context, req, res) => { @@ -287,7 +286,6 @@ describe('http service', () => { client.asScoped(req); return res.ok({ header: 'ok' }); }); - registerRouter(router); await root.start(); diff --git a/src/core/server/http/integration_tests/lifecycle.test.ts b/src/core/server/http/integration_tests/lifecycle.test.ts index 13a0fdbb82ca28..cbc7cff46186c6 100644 --- a/src/core/server/http/integration_tests/lifecycle.test.ts +++ b/src/core/server/http/integration_tests/lifecycle.test.ts @@ -73,18 +73,11 @@ interface StorageData { describe('OnPreAuth', () => { it('supports registering request inceptors', async () => { - const { - registerRouter, - registerOnPreAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPreAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok('ok')); - registerRouter(router); - const callingOrder: string[] = []; registerOnPreAuth((req, res, t) => { callingOrder.push('first'); @@ -105,12 +98,7 @@ describe('OnPreAuth', () => { }); it('supports request forwarding to specified url', async () => { - const { - registerRouter, - registerOnPreAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPreAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/initial', validate: false }, (context, req, res) => res.ok('initial')); @@ -118,8 +106,6 @@ describe('OnPreAuth', () => { res.ok('redirected') ); - registerRouter(router); - let urlBeforeForwarding; registerOnPreAuth((req, res, t) => { urlBeforeForwarding = ensureRawRequest(req).raw.req.url; @@ -144,19 +130,12 @@ describe('OnPreAuth', () => { }); it('supports redirection from the interceptor', async () => { - const { - registerRouter, - registerOnPreAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPreAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); const redirectUrl = '/redirectUrl'; router.get({ path: '/initial', validate: false }, (context, req, res) => res.ok('initial')); - registerRouter(router); - registerOnPreAuth((req, res, t) => res.redirected(undefined, { headers: { @@ -174,18 +153,11 @@ describe('OnPreAuth', () => { }); it('supports rejecting request and adjusting response headers', async () => { - const { - registerRouter, - registerOnPreAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPreAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok(undefined)); - registerRouter(router); - registerOnPreAuth((req, res, t) => res.unauthorized('not found error', { headers: { @@ -203,18 +175,11 @@ describe('OnPreAuth', () => { }); it("doesn't expose error details if interceptor throws", async () => { - const { - registerRouter, - registerOnPreAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPreAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok(undefined)); - registerRouter(router); - registerOnPreAuth((req, res, t) => { throw new Error('reason'); }); @@ -235,18 +200,11 @@ describe('OnPreAuth', () => { }); it('returns internal error if interceptor returns unexpected result', async () => { - const { - registerRouter, - registerOnPreAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPreAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok('ok')); - registerRouter(router); - registerOnPreAuth((req, res, t) => ({} as any)); await server.start(); @@ -265,12 +223,7 @@ describe('OnPreAuth', () => { }); it(`doesn't share request object between interceptors`, async () => { - const { - registerRouter, - registerOnPreAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPreAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); registerOnPreAuth((req, res, t) => { @@ -290,7 +243,6 @@ describe('OnPreAuth', () => { res.ok({ customField: String((req as any).customField) }) ); - registerRouter(router); await server.start(); await supertest(innerServer.listener) @@ -301,18 +253,11 @@ describe('OnPreAuth', () => { describe('OnPostAuth', () => { it('supports registering request inceptors', async () => { - const { - registerRouter, - registerOnPostAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPostAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok('ok')); - registerRouter(router); - const callingOrder: string[] = []; registerOnPostAuth((req, res, t) => { callingOrder.push('first'); @@ -333,19 +278,12 @@ describe('OnPostAuth', () => { }); it('supports redirection from the interceptor', async () => { - const { - registerRouter, - registerOnPostAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPostAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); const redirectUrl = '/redirectUrl'; router.get({ path: '/initial', validate: false }, (context, req, res) => res.ok('initial')); - registerRouter(router); - registerOnPostAuth((req, res, t) => res.redirected(undefined, { headers: { @@ -363,17 +301,10 @@ describe('OnPostAuth', () => { }); it('supports rejecting request and adjusting response headers', async () => { - const { - registerRouter, - registerOnPostAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPostAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok(undefined)); - registerRouter(router); - registerOnPostAuth((req, res, t) => res.unauthorized('not found error', { headers: { @@ -391,17 +322,10 @@ describe('OnPostAuth', () => { }); it("doesn't expose error details if interceptor throws", async () => { - const { - registerRouter, - registerOnPostAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPostAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok(undefined)); - registerRouter(router); - registerOnPostAuth((req, res, t) => { throw new Error('reason'); }); @@ -422,17 +346,10 @@ describe('OnPostAuth', () => { }); it('returns internal error if interceptor returns unexpected result', async () => { - const { - registerRouter, - registerOnPostAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPostAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok('ok')); - registerRouter(router); - registerOnPostAuth((req, res, t) => ({} as any)); await server.start(); @@ -451,12 +368,7 @@ describe('OnPostAuth', () => { }); it(`doesn't share request object between interceptors`, async () => { - const { - registerRouter, - registerOnPostAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPostAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); registerOnPostAuth((req, res, t) => { @@ -476,7 +388,7 @@ describe('OnPostAuth', () => { // don't complain customField is not defined on Request type res.ok({ customField: String((req as any).customField) }) ); - registerRouter(router); + await server.start(); await supertest(innerServer.listener) @@ -502,17 +414,10 @@ describe('Auth', () => { }); it('may grant access to a resource', async () => { - const { - registerRouter, - registerAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); - registerRouter(router); - registerAuth((req, res, t) => t.authenticated()); await server.start(); @@ -522,19 +427,12 @@ describe('Auth', () => { }); it('enables auth for a route by default if registerAuth has been called', async () => { - const { - registerRouter, - registerAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ authRequired: req.route.options.authRequired }) ); - registerRouter(router); - const authenticate = jest.fn().mockImplementation((req, res, t) => t.authenticated()); registerAuth(authenticate); @@ -547,19 +445,14 @@ describe('Auth', () => { }); test('supports disabling auth for a route explicitly', async () => { - const { - registerRouter, - registerAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get( { path: '/', validate: false, options: { authRequired: false } }, (context, req, res) => res.ok({ authRequired: req.route.options.authRequired }) ); - registerRouter(router); + const authenticate = jest.fn(); registerAuth(authenticate); @@ -572,19 +465,14 @@ describe('Auth', () => { }); test('supports enabling auth for a route explicitly', async () => { - const { - registerRouter, - registerAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get( { path: '/', validate: false, options: { authRequired: true } }, (context, req, res) => res.ok({ authRequired: req.route.options.authRequired }) ); - registerRouter(router); + const authenticate = jest.fn().mockImplementation((req, res, t) => t.authenticated({})); await registerAuth(authenticate); @@ -597,17 +485,10 @@ describe('Auth', () => { }); it('supports rejecting a request from an unauthenticated user', async () => { - const { - registerRouter, - registerAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); - registerRouter(router); - registerAuth((req, res) => res.unauthorized()); await server.start(); @@ -617,17 +498,10 @@ describe('Auth', () => { }); it('supports redirecting', async () => { - const { - registerRouter, - registerAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); - registerRouter(router); - const redirectTo = '/redirect-url'; registerAuth((req, res) => res.redirected(undefined, { @@ -645,17 +519,10 @@ describe('Auth', () => { }); it(`doesn't expose internal error details`, async () => { - const { - registerRouter, - registerAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ content: 'ok' })); - registerRouter(router); - registerAuth((req, t) => { throw new Error('reason'); }); @@ -678,7 +545,6 @@ describe('Auth', () => { it('allows manipulating cookies via cookie session storage', async () => { const { createCookieSessionStorageFactory, - registerRouter, registerAuth, server: innerServer, createRouter, @@ -696,7 +562,7 @@ describe('Auth', () => { sessionStorage.set({ value: user, expires: Date.now() + 1000 }); return toolkit.authenticated({ state: user }); }); - registerRouter(router); + await server.start(); const response = await supertest(innerServer.listener) @@ -721,7 +587,6 @@ describe('Auth', () => { it('allows manipulating cookies from route handler', async () => { const { createCookieSessionStorageFactory, - registerRouter, registerAuth, server: innerServer, createRouter, @@ -744,8 +609,6 @@ describe('Auth', () => { sessionStorage.clear(); return res.ok({ content: 'ok' }); }); - registerRouter(router); - await server.start(); const responseToSetCookie = await supertest(innerServer.listener) @@ -765,7 +628,6 @@ describe('Auth', () => { it.skip('is the only place with access to the authorization header', async () => { const { - registerRouter, registerOnPreAuth, registerAuth, registerOnPostAuth, @@ -798,8 +660,6 @@ describe('Auth', () => { fromRouteHandler = req.headers.authorization; return res.ok({ content: 'ok' }); }); - registerRouter(router); - await server.start(); const token = 'Basic: user:password'; @@ -815,12 +675,7 @@ describe('Auth', () => { }); it('attach security header to a successful response', async () => { - const { - registerRouter, - registerAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); const authResponseHeader = { @@ -831,8 +686,6 @@ describe('Auth', () => { }); router.get({ path: '/', validate: false }, (context, req, res) => res.ok({ header: 'ok' })); - registerRouter(router); - await server.start(); const response = await supertest(innerServer.listener) @@ -843,12 +696,7 @@ describe('Auth', () => { }); it('attach security header to an error response', async () => { - const { - registerRouter, - registerAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); const authResponseHeader = { 'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca', @@ -861,8 +709,6 @@ describe('Auth', () => { router.get({ path: '/', validate: false }, (context, req, res) => res.badRequest(new Error('reason')) ); - registerRouter(router); - await server.start(); const response = await supertest(innerServer.listener) @@ -873,12 +719,7 @@ describe('Auth', () => { }); it('logs warning if Auth Security Header rewrites response header for success response', async () => { - const { - registerRouter, - registerAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); const authResponseHeader = { @@ -899,8 +740,6 @@ describe('Auth', () => { } ) ); - registerRouter(router); - await server.start(); const response = await supertest(innerServer.listener) @@ -918,12 +757,7 @@ describe('Auth', () => { }); it('logs warning if Auth Security Header rewrites response header for error response', async () => { - const { - registerRouter, - registerAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); const authResponseHeader = { @@ -941,8 +775,6 @@ describe('Auth', () => { }, }) ); - registerRouter(router); - await server.start(); const response = await supertest(innerServer.listener) @@ -960,18 +792,11 @@ describe('Auth', () => { }); it('supports redirection from the interceptor', async () => { - const { - registerRouter, - registerOnPostAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPostAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); const redirectUrl = '/redirectUrl'; router.get({ path: '/initial', validate: false }, (context, req, res) => res.ok('initial')); - registerRouter(router); - registerOnPostAuth((req, res, t) => res.redirected(undefined, { headers: { @@ -989,18 +814,11 @@ describe('Auth', () => { }); it('supports rejecting request and adjusting response headers', async () => { - const { - registerRouter, - registerOnPostAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPostAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok(undefined)); - registerRouter(router); - registerOnPostAuth((req, res, t) => res.unauthorized('not found error', { headers: { @@ -1018,17 +836,10 @@ describe('Auth', () => { }); it("doesn't expose error details if interceptor throws", async () => { - const { - registerRouter, - registerOnPostAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPostAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok(undefined)); - registerRouter(router); - registerOnPostAuth((req, res, t) => { throw new Error('reason'); }); @@ -1049,17 +860,10 @@ describe('Auth', () => { }); it('returns internal error if interceptor returns unexpected result', async () => { - const { - registerRouter, - registerOnPostAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPostAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => res.ok('ok')); - registerRouter(router); - registerOnPostAuth((req, res, t) => ({} as any)); await server.start(); @@ -1076,13 +880,9 @@ describe('Auth', () => { ] `); }); + // eslint-disable-next-line it(`doesn't share request object between interceptors`, async () => { - const { - registerRouter, - registerOnPostAuth, - server: innerServer, - createRouter, - } = await server.setup(); + const { registerOnPostAuth, server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); registerOnPostAuth((req, res, t) => { @@ -1101,7 +901,7 @@ describe('Auth', () => { // don't complain customField is not defined on Request type res.ok({ customField: String((req as any).customField) }) ); - registerRouter(router); + await server.start(); await supertest(innerServer.listener) diff --git a/src/core/server/http/integration_tests/router.test.ts b/src/core/server/http/integration_tests/router.test.ts index e9755175537bba..49beae161b9a36 100644 --- a/src/core/server/http/integration_tests/router.test.ts +++ b/src/core/server/http/integration_tests/router.test.ts @@ -63,14 +63,12 @@ afterEach(async () => { describe('Handler', () => { it("Doesn't expose error details if handler throws", async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { throw new Error('unexpected error'); }); - - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -88,14 +86,12 @@ describe('Handler', () => { }); it('returns 500 Server error if handler throws Boom error', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { throw Boom.unauthorized(); }); - - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -113,12 +109,10 @@ describe('Handler', () => { }); it('returns 500 Server error if handler returns unexpected result', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => 'ok' as any); - - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -136,7 +130,7 @@ describe('Handler', () => { }); it('returns 400 Bad request if request validation failed', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get( @@ -150,8 +144,6 @@ describe('Handler', () => { }, (context, req, res) => res.noContent() ); - - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -170,14 +162,13 @@ describe('Handler', () => { describe('Response factory', () => { describe('Success', () => { it('supports answering with json object', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { return res.ok({ key: 'value' }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -189,14 +180,13 @@ describe('Response factory', () => { }); it('supports answering with string', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { return res.ok('result'); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -208,14 +198,13 @@ describe('Response factory', () => { }); it('supports answering with undefined', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { return res.ok(undefined); }); - registerRouter(router); await server.start(); await supertest(innerServer.listener) @@ -224,7 +213,7 @@ describe('Response factory', () => { }); it('supports answering with Stream', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -240,7 +229,6 @@ describe('Response factory', () => { return res.ok(stream); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -252,7 +240,7 @@ describe('Response factory', () => { }); it('supports answering with chunked Stream', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -267,7 +255,6 @@ describe('Response factory', () => { return res.ok(stream); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -279,7 +266,7 @@ describe('Response factory', () => { }); it('supports answering with Buffer', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -292,7 +279,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -306,7 +292,7 @@ describe('Response factory', () => { }); it('supports answering with Buffer text', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -319,7 +305,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -333,7 +318,7 @@ describe('Response factory', () => { }); it('supports configuring standard headers', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -344,7 +329,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -356,7 +340,7 @@ describe('Response factory', () => { }); it('supports configuring non-standard headers', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -368,7 +352,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -381,7 +364,7 @@ describe('Response factory', () => { }); it('accepted headers are case-insensitive.', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -392,7 +375,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -403,7 +385,7 @@ describe('Response factory', () => { }); it('accept array of headers', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -414,7 +396,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -425,7 +406,7 @@ describe('Response factory', () => { }); it('throws if given invalid json object as response payload', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -434,7 +415,6 @@ describe('Response factory', () => { return res.ok(payload); }); - registerRouter(router); await server.start(); await supertest(innerServer.listener) @@ -446,14 +426,13 @@ describe('Response factory', () => { }); it('200 OK with body', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { return res.ok({ key: 'value' }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -465,14 +444,13 @@ describe('Response factory', () => { }); it('202 Accepted with body', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { return res.accepted({ location: 'somewhere' }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -484,14 +462,13 @@ describe('Response factory', () => { }); it('204 No content', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { return res.noContent(); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -504,7 +481,7 @@ describe('Response factory', () => { describe('Redirection', () => { it('302 supports redirection to configured URL', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -516,7 +493,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -529,7 +505,7 @@ describe('Response factory', () => { }); it('throws if redirection url not provided', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -540,7 +516,6 @@ describe('Response factory', () => { } as any); // location headers is required }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -560,7 +535,7 @@ describe('Response factory', () => { describe('Error', () => { it('400 Bad request', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -568,7 +543,6 @@ describe('Response factory', () => { return res.badRequest(error); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -583,14 +557,13 @@ describe('Response factory', () => { }); it('400 Bad request with default message', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { return res.badRequest(); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -605,14 +578,13 @@ describe('Response factory', () => { }); it('400 Bad request with additional data', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { return res.badRequest({ message: 'some message', meta: { data: ['good', 'bad'] } }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -630,7 +602,7 @@ describe('Response factory', () => { }); it('401 Unauthorized', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -642,7 +614,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -654,14 +625,13 @@ describe('Response factory', () => { }); it('401 Unauthorized with default message', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { return res.unauthorized(); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -672,7 +642,7 @@ describe('Response factory', () => { }); it('403 Forbidden', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -680,7 +650,6 @@ describe('Response factory', () => { return res.forbidden(error); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -691,14 +660,13 @@ describe('Response factory', () => { }); it('403 Forbidden with default message', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { return res.forbidden(); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -709,7 +677,7 @@ describe('Response factory', () => { }); it('404 Not Found', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -717,7 +685,6 @@ describe('Response factory', () => { return res.notFound(error); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -728,14 +695,13 @@ describe('Response factory', () => { }); it('404 Not Found with default message', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { return res.notFound(); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -746,7 +712,7 @@ describe('Response factory', () => { }); it('409 Conflict', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -754,7 +720,6 @@ describe('Response factory', () => { return res.conflict(error); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -765,14 +730,13 @@ describe('Response factory', () => { }); it('409 Conflict with default message', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { return res.conflict(); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -783,7 +747,7 @@ describe('Response factory', () => { }); it('Custom error response', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -793,7 +757,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -808,7 +771,7 @@ describe('Response factory', () => { }); it('Custom error response for server error', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -819,7 +782,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -834,7 +796,7 @@ describe('Response factory', () => { }); it('Custom error response for Boom server error', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -845,7 +807,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -860,7 +821,7 @@ describe('Response factory', () => { }); it('Custom error response requires error status code', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -870,7 +831,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -894,7 +854,7 @@ describe('Response factory', () => { describe('Custom', () => { it('creates success response', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -906,7 +866,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -917,7 +876,7 @@ describe('Response factory', () => { }); it('creates redirect response', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -929,7 +888,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -940,7 +898,7 @@ describe('Response factory', () => { }); it('throws if redirects without location header to be set', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -950,7 +908,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); await supertest(innerServer.listener) @@ -967,7 +924,7 @@ describe('Response factory', () => { }); it('creates error response', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -977,7 +934,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -988,7 +944,7 @@ describe('Response factory', () => { }); it('creates error response with additional data', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -1003,7 +959,6 @@ describe('Response factory', () => { ); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -1019,7 +974,7 @@ describe('Response factory', () => { }); it('creates error response with additional data and error object', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -1034,7 +989,6 @@ describe('Response factory', () => { ); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -1050,7 +1004,7 @@ describe('Response factory', () => { }); it('creates error response with Boom error', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -1060,7 +1014,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -1071,7 +1024,7 @@ describe('Response factory', () => { }); it("Doesn't log details of created 500 Server error response", async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -1080,7 +1033,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -1092,7 +1044,7 @@ describe('Response factory', () => { }); it('throws an error if not valid error is provided', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -1104,7 +1056,6 @@ describe('Response factory', () => { ); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -1122,7 +1073,7 @@ describe('Response factory', () => { }); it('throws if an error not provided', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -1131,7 +1082,6 @@ describe('Response factory', () => { }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -1149,7 +1099,7 @@ describe('Response factory', () => { }); it('throws an error if statusCode is not specified', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -1157,7 +1107,6 @@ describe('Response factory', () => { return res.custom(error, undefined as any); // options.statusCode is required }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) @@ -1175,7 +1124,7 @@ describe('Response factory', () => { }); it('throws an error if statusCode is not valid', async () => { - const { registerRouter, server: innerServer, createRouter } = await server.setup(); + const { server: innerServer, createRouter } = await server.setup(); const router = createRouter('/'); router.get({ path: '/', validate: false }, (context, req, res) => { @@ -1183,7 +1132,6 @@ describe('Response factory', () => { return res.custom(error, { statusCode: 20 }); }); - registerRouter(router); await server.start(); const result = await supertest(innerServer.listener) diff --git a/src/core/server/index.ts b/src/core/server/index.ts index 3c642c3b4cd1c4..9acd92a39ee29e 100644 --- a/src/core/server/index.ts +++ b/src/core/server/index.ts @@ -178,7 +178,6 @@ export interface CoreSetup { basePath: HttpServiceSetup['basePath']; isTlsEnabled: HttpServiceSetup['isTlsEnabled']; createRouter: () => IRouter; - registerRouter: (router: IRouter) => void; }; } diff --git a/src/core/server/legacy/integration_tests/legacy_service.test.ts b/src/core/server/legacy/integration_tests/legacy_service.test.ts index 1c80d655e73de9..67e05f9a7276eb 100644 --- a/src/core/server/legacy/integration_tests/legacy_service.test.ts +++ b/src/core/server/legacy/integration_tests/legacy_service.test.ts @@ -34,7 +34,7 @@ describe('legacy service', () => { router.get({ path: '/new-platform', validate: false }, (context, req, res) => res.ok({ content: 'from-new-platform' }) ); - http.registerRouter(router); + await root.start(); const legacyPlatformUrl = `${rootUrl}/legacy-platform`; @@ -58,7 +58,7 @@ describe('legacy service', () => { router.get({ path: '', validate: false }, (context, req, res) => res.ok({ content: 'from-new-platform' }) ); - http.registerRouter(router); + await root.start(); const kbnServer = kbnTestServer.getKbnServer(root); diff --git a/src/core/server/mocks.ts b/src/core/server/mocks.ts index 11b417224c4963..e7ff83d7ad4053 100644 --- a/src/core/server/mocks.ts +++ b/src/core/server/mocks.ts @@ -66,7 +66,6 @@ function createCoreSetupMock() { registerOnPostAuth: httpService.registerOnPostAuth, basePath: httpService.basePath, isTlsEnabled: httpService.isTlsEnabled, - registerRouter: httpService.registerRouter, createRouter: jest.fn(), }; httpMock.createRouter.mockImplementation(() => httpService.createRouter('')); diff --git a/src/core/server/plugins/plugin_context.ts b/src/core/server/plugins/plugin_context.ts index 7514a19f45290f..cbe911b2afa681 100644 --- a/src/core/server/plugins/plugin_context.ts +++ b/src/core/server/plugins/plugin_context.ts @@ -20,7 +20,6 @@ import { CoreContext } from '../core_context'; import { PluginWrapper } from './plugin'; import { PluginsServiceSetupDeps, PluginsServiceStartDeps } from './plugins_service'; -import { IRouter } from '../http'; import { PluginInitializerContext, PluginManifest, PluginOpaqueId } from './types'; import { CoreSetup, CoreStart } from '..'; @@ -111,7 +110,6 @@ export function createPluginSetupContext( http: { createCookieSessionStorageFactory: deps.http.createCookieSessionStorageFactory, createRouter: () => deps.http.createRouter(`/${plugin.name}`), - registerRouter: (router: IRouter) => deps.http.registerRouter(router), registerOnPreAuth: deps.http.registerOnPreAuth, registerAuth: deps.http.registerAuth, registerOnPostAuth: deps.http.registerOnPostAuth, diff --git a/src/core/server/server.ts b/src/core/server/server.ts index bf9e1793476338..a78c2e2a23c4a6 100644 --- a/src/core/server/server.ts +++ b/src/core/server/server.ts @@ -123,7 +123,6 @@ export class Server { router.get({ path: '/', validate: false }, async (context, req, res) => res.ok({ version: '0.0.1' }) ); - httpSetup.registerRouter(router); } public async setupConfigSchemas() { diff --git a/x-pack/legacy/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.test.ts b/x-pack/legacy/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.test.ts index b14963e36e1160..b97ade2966e592 100644 --- a/x-pack/legacy/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.test.ts +++ b/x-pack/legacy/plugins/spaces/server/lib/request_interceptors/on_request_interceptor.test.ts @@ -90,8 +90,6 @@ describe('onRequestInterceptor', () => { }); } ); - - http.registerRouter(router); } } From 4b66632927705cdd9a25cd4683d4f8b25e521c42 Mon Sep 17 00:00:00 2001 From: Mikhail Shustov Date: Fri, 9 Aug 2019 10:20:37 +0200 Subject: [PATCH 7/8] address comments --- src/core/server/http/router/router.ts | 177 +++++++++----------------- 1 file changed, 63 insertions(+), 114 deletions(-) diff --git a/src/core/server/http/router/router.ts b/src/core/server/http/router/router.ts index 0b2e54a13624aa..8f714c7b3c89a0 100644 --- a/src/core/server/http/router/router.ts +++ b/src/core/server/http/router/router.ts @@ -103,139 +103,88 @@ function getRouteFullPath(routerPath: string, routePath: string) { return `${routerPath}${routePath.slice(routePathStartIndex)}`; } +/** + * Create the validation schemas for a route + * + * @returns Route schemas if `validate` is specified on the route, otherwise + * undefined. + */ +function routeSchemasFromRouteConfig< + P extends ObjectType, + Q extends ObjectType, + B extends ObjectType +>(route: RouteConfig, routeMethod: RouteMethod) { + // The type doesn't allow `validate` to be undefined, but it can still + // happen when it's used from JavaScript. + if (route.validate === undefined) { + throw new Error( + `The [${routeMethod}] at [${route.path}] does not have a 'validate' specified. Use 'false' as the value if you want to bypass validation.` + ); + } + + if (route.validate !== false) { + Object.entries(route.validate).forEach(([key, schema]) => { + if (!(schema instanceof Type)) { + throw new Error( + `Expected a valid schema declared with '@kbn/config-schema' package at key: [${key}].` + ); + } + }); + } + + return route.validate ? route.validate : undefined; +} + /** * @internal */ export class Router implements IRouter { public routes: Array> = []; + public get: IRouter['get']; + public post: IRouter['post']; + public delete: IRouter['delete']; + public put: IRouter['put']; constructor( readonly routerPath: string, private readonly log: Logger, private readonly enhanceWithContext: ContextEnhancer = fn => fn.bind(null, {}) - ) {} - - public get

( - route: RouteConfig, - handler: RequestHandler - ) { - const { path, options = {} } = route; - const routeSchemas = this.routeSchemasFromRouteConfig(route, 'get'); - - this.routes.push({ - handler: async (req, responseToolkit) => { - return await this.handle({ - routeSchemas, - request: req, - responseToolkit, - handler: this.enhanceWithContext(handler), - }); - }, - method: 'get', - path: getRouteFullPath(this.routerPath, path), - options, - }); - } - - public post

( - route: RouteConfig, - handler: RequestHandler ) { - const { path, options = {} } = route; - const routeSchemas = this.routeSchemasFromRouteConfig(route, 'post'); - this.routes.push({ - handler: async (req, responseToolkit) => { - return await this.handle({ - routeSchemas, - request: req, - responseToolkit, - handler: this.enhanceWithContext(handler), - }); - }, - method: 'post', - path: getRouteFullPath(this.routerPath, path), - options, - }); - } + const buildMethod = (method: RouteMethod) => < + P extends ObjectType, + Q extends ObjectType, + B extends ObjectType + >( + route: RouteConfig, + handler: RequestHandler + ) => { + const { path, options = {} } = route; + const routeSchemas = routeSchemasFromRouteConfig(route, method); - public put

( - route: RouteConfig, - handler: RequestHandler - ) { - const { path, options = {} } = route; - const routeSchemas = this.routeSchemasFromRouteConfig(route, 'put'); - this.routes.push({ - handler: async (req, responseToolkit) => { - return await this.handle({ - routeSchemas, - request: req, - responseToolkit, - handler: this.enhanceWithContext(handler), - }); - }, - method: 'put', - path: getRouteFullPath(this.routerPath, path), - options, - }); - } + this.routes.push({ + handler: async (req, responseToolkit) => + await this.handle({ + routeSchemas, + request: req, + responseToolkit, + handler: this.enhanceWithContext(handler), + }), + method, + path: getRouteFullPath(this.routerPath, path), + options, + }); + }; - public delete

( - route: RouteConfig, - handler: RequestHandler - ) { - const { path, options = {} } = route; - const routeSchemas = this.routeSchemasFromRouteConfig(route, 'delete'); - this.routes.push({ - handler: async (req, responseToolkit) => { - return await this.handle({ - routeSchemas, - request: req, - responseToolkit, - handler: this.enhanceWithContext(handler), - }); - }, - method: 'delete', - path: getRouteFullPath(this.routerPath, path), - options, - }); + this.get = buildMethod('get'); + this.post = buildMethod('post'); + this.delete = buildMethod('delete'); + this.put = buildMethod('put'); } public getRoutes() { return [...this.routes]; } - /** - * Create the validation schemas for a route - * - * @returns Route schemas if `validate` is specified on the route, otherwise - * undefined. - */ - private routeSchemasFromRouteConfig< - P extends ObjectType, - Q extends ObjectType, - B extends ObjectType - >(route: RouteConfig, routeMethod: RouteMethod) { - // The type doesn't allow `validate` to be undefined, but it can still - // happen when it's used from JavaScript. - if (route.validate === undefined) { - throw new Error( - `The [${routeMethod}] at [${route.path}] does not have a 'validate' specified. Use 'false' as the value if you want to bypass validation.` - ); - } - - if (route.validate !== false) { - Object.entries(route.validate).forEach(([key, schema]) => { - if (!(schema instanceof Type)) { - throw new Error( - `Expected a valid schema declared with '@kbn/config-schema' package at key: [${key}].` - ); - } - }); - } - - return route.validate ? route.validate : undefined; - } - private async handle

({ routeSchemas, request, From 37e3a69c7d765293f63f98461bda32ad094886a2 Mon Sep 17 00:00:00 2001 From: restrry Date: Fri, 9 Aug 2019 10:21:56 +0200 Subject: [PATCH 8/8] update docs --- .../core/server/kibana-plugin-server.coresetup.http.md | 1 - docs/development/core/server/kibana-plugin-server.coresetup.md | 2 +- .../core/server/kibana-plugin-server.httpservicesetup.md | 2 +- src/core/server/server.api.md | 3 +-- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/development/core/server/kibana-plugin-server.coresetup.http.md b/docs/development/core/server/kibana-plugin-server.coresetup.http.md index bb11aef427b26c..30f1bb966175a1 100644 --- a/docs/development/core/server/kibana-plugin-server.coresetup.http.md +++ b/docs/development/core/server/kibana-plugin-server.coresetup.http.md @@ -15,6 +15,5 @@ http: { basePath: HttpServiceSetup['basePath']; isTlsEnabled: HttpServiceSetup['isTlsEnabled']; createRouter: () => IRouter; - registerRouter: (router: IRouter) => void; }; ``` diff --git a/docs/development/core/server/kibana-plugin-server.coresetup.md b/docs/development/core/server/kibana-plugin-server.coresetup.md index 8eab68ef984ca4..0fdb3ad7397716 100644 --- a/docs/development/core/server/kibana-plugin-server.coresetup.md +++ b/docs/development/core/server/kibana-plugin-server.coresetup.md @@ -18,5 +18,5 @@ export interface CoreSetup | --- | --- | --- | | [context](./kibana-plugin-server.coresetup.context.md) | {
createContextContainer: ContextSetup['createContextContainer'];
} | | | [elasticsearch](./kibana-plugin-server.coresetup.elasticsearch.md) | {
adminClient$: Observable<ClusterClient>;
dataClient$: Observable<ClusterClient>;
createClient: (type: string, clientConfig?: Partial<ElasticsearchClientConfig>) => ClusterClient;
} | | -| [http](./kibana-plugin-server.coresetup.http.md) | {
createCookieSessionStorageFactory: HttpServiceSetup['createCookieSessionStorageFactory'];
registerOnPreAuth: HttpServiceSetup['registerOnPreAuth'];
registerAuth: HttpServiceSetup['registerAuth'];
registerOnPostAuth: HttpServiceSetup['registerOnPostAuth'];
basePath: HttpServiceSetup['basePath'];
isTlsEnabled: HttpServiceSetup['isTlsEnabled'];
createRouter: () => IRouter;
registerRouter: (router: IRouter) => void;
} | | +| [http](./kibana-plugin-server.coresetup.http.md) | {
createCookieSessionStorageFactory: HttpServiceSetup['createCookieSessionStorageFactory'];
registerOnPreAuth: HttpServiceSetup['registerOnPreAuth'];
registerAuth: HttpServiceSetup['registerAuth'];
registerOnPostAuth: HttpServiceSetup['registerOnPostAuth'];
basePath: HttpServiceSetup['basePath'];
isTlsEnabled: HttpServiceSetup['isTlsEnabled'];
createRouter: () => IRouter;
} | | diff --git a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md index 0ac85f535f47a4..675a4ccfd287b0 100644 --- a/docs/development/core/server/kibana-plugin-server.httpservicesetup.md +++ b/docs/development/core/server/kibana-plugin-server.httpservicesetup.md @@ -8,7 +8,7 @@ Signature: ```typescript -export declare type HttpServiceSetup = HttpServerSetup & { +export declare type HttpServiceSetup = Omit & { createRouter: (path: string) => IRouter; }; ``` diff --git a/src/core/server/server.api.md b/src/core/server/server.api.md index 4bc6cdc48b5434..c700ec6dcfae96 100644 --- a/src/core/server/server.api.md +++ b/src/core/server/server.api.md @@ -124,7 +124,6 @@ export interface CoreSetup { basePath: HttpServiceSetup['basePath']; isTlsEnabled: HttpServiceSetup['isTlsEnabled']; createRouter: () => IRouter; - registerRouter: (router: IRouter) => void; }; } @@ -241,7 +240,7 @@ export interface HttpServerSetup { } // @public (undocumented) -export type HttpServiceSetup = HttpServerSetup & { +export type HttpServiceSetup = Omit & { createRouter: (path: string) => IRouter; };