diff --git a/docs/development/core/server/kibana-plugin-server.irouter.handlelegacyerrors.md b/docs/development/core/server/kibana-plugin-server.irouter.handlelegacyerrors.md index 2367420068064..ff71f13466cf8 100644 --- a/docs/development/core/server/kibana-plugin-server.irouter.handlelegacyerrors.md +++ b/docs/development/core/server/kibana-plugin-server.irouter.handlelegacyerrors.md @@ -9,5 +9,5 @@ Wrap a router handler to catch and converts legacy boom errors to proper custom Signature: ```typescript -handleLegacyErrors:
(handler: RequestHandler
) => RequestHandler
; +handleLegacyErrors:
(handler: RequestHandler
) => RequestHandler
;
```
diff --git a/docs/development/core/server/kibana-plugin-server.irouter.md b/docs/development/core/server/kibana-plugin-server.irouter.md
index 73e96191e02e7..a6536d2ed6763 100644
--- a/docs/development/core/server/kibana-plugin-server.irouter.md
+++ b/docs/development/core/server/kibana-plugin-server.irouter.md
@@ -18,7 +18,7 @@ export interface IRouter
| --- | --- | --- |
| [delete](./kibana-plugin-server.irouter.delete.md) | RouteRegistrar<'delete'>
| Register a route handler for DELETE
request. |
| [get](./kibana-plugin-server.irouter.get.md) | RouteRegistrar<'get'>
| Register a route handler for GET
request. |
-| [handleLegacyErrors](./kibana-plugin-server.irouter.handlelegacyerrors.md) | <P extends ObjectType, Q extends ObjectType, B extends ObjectType>(handler: RequestHandler<P, Q, B>) => RequestHandler<P, Q, B>
| Wrap a router handler to catch and converts legacy boom errors to proper custom errors. |
+| [handleLegacyErrors](./kibana-plugin-server.irouter.handlelegacyerrors.md) | <P, Q, B>(handler: RequestHandler<P, Q, B>) => RequestHandler<P, Q, B>
| Wrap a router handler to catch and converts legacy boom errors to proper custom errors. |
| [patch](./kibana-plugin-server.irouter.patch.md) | RouteRegistrar<'patch'>
| Register a route handler for PATCH
request. |
| [post](./kibana-plugin-server.irouter.post.md) | RouteRegistrar<'post'>
| Register a route handler for POST
request. |
| [put](./kibana-plugin-server.irouter.put.md) | RouteRegistrar<'put'>
| Register a route handler for PUT
request. |
diff --git a/docs/development/core/server/kibana-plugin-server.md b/docs/development/core/server/kibana-plugin-server.md
index a39c27a758f9d..9c8aafb158bfd 100644
--- a/docs/development/core/server/kibana-plugin-server.md
+++ b/docs/development/core/server/kibana-plugin-server.md
@@ -21,6 +21,7 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [CspConfig](./kibana-plugin-server.cspconfig.md) | CSP configuration for use in Kibana. |
| [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. |
+| [RouteValidationError](./kibana-plugin-server.routevalidationerror.md) | Error to return when the validation is not successful. |
| [SavedObjectsClient](./kibana-plugin-server.savedobjectsclient.md) | |
| [SavedObjectsErrorHelpers](./kibana-plugin-server.savedobjectserrorhelpers.md) | |
| [SavedObjectsRepository](./kibana-plugin-server.savedobjectsrepository.md) | |
@@ -94,7 +95,9 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [RouteConfig](./kibana-plugin-server.routeconfig.md) | Route specific configuration. |
| [RouteConfigOptions](./kibana-plugin-server.routeconfigoptions.md) | Additional route options. |
| [RouteConfigOptionsBody](./kibana-plugin-server.routeconfigoptionsbody.md) | Additional body options for a route |
-| [RouteSchemas](./kibana-plugin-server.routeschemas.md) | RouteSchemas contains the schemas for validating the different parts of a request. |
+| [RouteValidationResultFactory](./kibana-plugin-server.routevalidationresultfactory.md) | Validation result factory to be used in the custom validation function to return the valid data or validation errorsSee [RouteValidationFunction](./kibana-plugin-server.routevalidationfunction.md). |
+| [RouteValidatorConfig](./kibana-plugin-server.routevalidatorconfig.md) | The configuration object to the RouteValidator class. Set params
, query
and/or body
to specify the validation logic to follow for that property. |
+| [RouteValidatorOptions](./kibana-plugin-server.routevalidatoroptions.md) | Additional options for the RouteValidator class to modify its default behaviour. |
| [SavedObject](./kibana-plugin-server.savedobject.md) | |
| [SavedObjectAttributes](./kibana-plugin-server.savedobjectattributes.md) | The data for a Saved Object is stored as an object in the attributes
property. |
| [SavedObjectReference](./kibana-plugin-server.savedobjectreference.md) | A reference to another saved object. |
@@ -200,6 +203,9 @@ The plugin integrates with the core system via lifecycle events: `setup`
| [RouteContentType](./kibana-plugin-server.routecontenttype.md) | The set of supported parseable Content-Types |
| [RouteMethod](./kibana-plugin-server.routemethod.md) | The set of common HTTP methods supported by Kibana routing. |
| [RouteRegistrar](./kibana-plugin-server.routeregistrar.md) | Route handler common definition |
+| [RouteValidationFunction](./kibana-plugin-server.routevalidationfunction.md) | The custom validation function if @kbn/config-schema is not a valid solution for your specific plugin requirements. |
+| [RouteValidationSpec](./kibana-plugin-server.routevalidationspec.md) | Allowed property validation options: either @kbn/config-schema validations or custom validation functionsSee [RouteValidationFunction](./kibana-plugin-server.routevalidationfunction.md) for custom validation. |
+| [RouteValidatorFullConfig](./kibana-plugin-server.routevalidatorfullconfig.md) | Route validations config and options merged into one object |
| [SavedObjectAttribute](./kibana-plugin-server.savedobjectattribute.md) | Type definition for a Saved Object attribute value |
| [SavedObjectAttributeSingle](./kibana-plugin-server.savedobjectattributesingle.md) | Don't use this type, it's simply a helper type for [SavedObjectAttribute](./kibana-plugin-server.savedobjectattribute.md) |
| [SavedObjectsClientContract](./kibana-plugin-server.savedobjectsclientcontract.md) | Saved Objects is Kibana's data persisentence mechanism allowing plugins to use Elasticsearch for storing plugin state.\#\# SavedObjectsClient errorsSince the SavedObjectsClient has its hands in everything we are a little paranoid about the way we present errors back to to application code. Ideally, all errors will be either:1. Caused by bad implementation (ie. undefined is not a function) and as such unpredictable 2. An error that has been classified and decorated appropriately by the decorators in [SavedObjectsErrorHelpers](./kibana-plugin-server.savedobjectserrorhelpers.md)Type 1 errors are inevitable, but since all expected/handle-able errors should be Type 2 the isXYZError()
helpers exposed at SavedObjectsErrorHelpers
should be used to understand and manage error responses from the SavedObjectsClient
.Type 2 errors are decorated versions of the source error, so if the elasticsearch client threw an error it will be decorated based on its type. That means that rather than looking for error.body.error.type
or doing substring checks on error.body.error.reason
, just use the helpers to understand the meaning of the error:\`\`\`js if (SavedObjectsErrorHelpers.isNotFoundError(error)) { // handle 404 }if (SavedObjectsErrorHelpers.isNotAuthorizedError(error)) { // 401 handling should be automatic, but in case you wanted to know }// always rethrow the error unless you handle it throw error; \`\`\`\#\#\# 404s from missing indexFrom the perspective of application code and APIs the SavedObjectsClient is a black box that persists objects. One of the internal details that users have no control over is that we use an elasticsearch index for persistance and that index might be missing.At the time of writing we are in the process of transitioning away from the operating assumption that the SavedObjects index is always available. Part of this transition is handling errors resulting from an index missing. These used to trigger a 500 error in most cases, and in others cause 404s with different error messages.From my (Spencer) perspective, a 404 from the SavedObjectsApi is a 404; The object the request/call was targeting could not be found. This is why \#14141 takes special care to ensure that 404 errors are generic and don't distinguish between index missing or document missing.\#\#\# 503s from missing indexUnlike all other methods, create requests are supposed to succeed even when the Kibana index does not exist because it will be automatically created by elasticsearch. When that is not the case it is because Elasticsearch's action.auto_create_index
setting prevents it from being created automatically so we throw a special 503 with the intention of informing the user that their Elasticsearch settings need to be updated.See [SavedObjectsClient](./kibana-plugin-server.savedobjectsclient.md) See [SavedObjectsErrorHelpers](./kibana-plugin-server.savedobjectserrorhelpers.md) |
diff --git a/docs/development/core/server/kibana-plugin-server.requesthandler.md b/docs/development/core/server/kibana-plugin-server.requesthandler.md
index 79abfd4293e9f..9fc183ffc334b 100644
--- a/docs/development/core/server/kibana-plugin-server.requesthandler.md
+++ b/docs/development/core/server/kibana-plugin-server.requesthandler.md
@@ -9,7 +9,7 @@ A function executed when route path matched requested resource path. Request han
Signature:
```typescript
-export declare type RequestHandler
| Type = (context: RequestHandlerContext, request: KibanaRequest , response: KibanaResponseFactory) => IKibanaResponse | Type
```
## Properties
@@ -18,5 +18,5 @@ export interface RouteConfig RouteConfigOptions<Method> | Additional route options [RouteConfigOptions](./kibana-plugin-server.routeconfigoptions.md). |
| [path](./kibana-plugin-server.routeconfig.path.md) | | false;
+validate: RouteValidatorFullConfig | false;
```
## Remarks
diff --git a/docs/development/core/server/kibana-plugin-server.routeregistrar.md b/docs/development/core/server/kibana-plugin-server.routeregistrar.md
index 0f5f49636fdd5..901d260fee21d 100644
--- a/docs/development/core/server/kibana-plugin-server.routeregistrar.md
+++ b/docs/development/core/server/kibana-plugin-server.routeregistrar.md
@@ -9,5 +9,5 @@ Route handler common definition
Signature:
```typescript
-export declare type RouteRegistrar | Type , handler: RequestHandler ) => void;
+export declare type RouteRegistrar (route: RouteConfig , handler: RequestHandler ) => void;
```
diff --git a/docs/development/core/server/kibana-plugin-server.routeschemas.body.md b/docs/development/core/server/kibana-plugin-server.routeschemas.body.md
deleted file mode 100644
index 78a9d25c25d9d..0000000000000
--- a/docs/development/core/server/kibana-plugin-server.routeschemas.body.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteSchemas](./kibana-plugin-server.routeschemas.md) > [body](./kibana-plugin-server.routeschemas.body.md)
-
-## RouteSchemas.body property
-
-Signature:
-
-```typescript
-body?: B;
-```
diff --git a/docs/development/core/server/kibana-plugin-server.routeschemas.md b/docs/development/core/server/kibana-plugin-server.routeschemas.md
deleted file mode 100644
index 77b980551a8ff..0000000000000
--- a/docs/development/core/server/kibana-plugin-server.routeschemas.md
+++ /dev/null
@@ -1,22 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteSchemas](./kibana-plugin-server.routeschemas.md)
-
-## RouteSchemas interface
-
-RouteSchemas contains the schemas for validating the different parts of a request.
-
-Signature:
-
-```typescript
-export interface RouteSchemas | Type
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [body](./kibana-plugin-server.routevalidatorconfig.body.md) | ;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.routevalidatorconfig.query.md b/docs/development/core/server/kibana-plugin-server.routevalidatorconfig.query.md
new file mode 100644
index 0000000000000..510325c2dfff7
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.routevalidatorconfig.query.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteValidatorConfig](./kibana-plugin-server.routevalidatorconfig.md) > [query](./kibana-plugin-server.routevalidatorconfig.query.md)
+
+## RouteValidatorConfig.query property
+
+Validation logic for the Query params
+
+Signature:
+
+```typescript
+query?: RouteValidationSpec = RouteValidatorConfig & RouteValidatorOptions;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.routevalidatoroptions.md b/docs/development/core/server/kibana-plugin-server.routevalidatoroptions.md
new file mode 100644
index 0000000000000..00b029d9928e3
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.routevalidatoroptions.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteValidatorOptions](./kibana-plugin-server.routevalidatoroptions.md)
+
+## RouteValidatorOptions interface
+
+Additional options for the RouteValidator class to modify its default behaviour.
+
+Signature:
+
+```typescript
+export interface RouteValidatorOptions
+```
+
+## Properties
+
+| Property | Type | Description |
+| --- | --- | --- |
+| [unsafe](./kibana-plugin-server.routevalidatoroptions.unsafe.md) | (
+export const wrapErrors = (
handler: RequestHandler
): RequestHandler => {
return async (
context: RequestHandlerContext,
- request: KibanaRequest ,
response: KibanaResponseFactory
) => {
try {
diff --git a/src/core/server/http/router/index.ts b/src/core/server/http/router/index.ts
index 35bfb3ba9c33a..084d30d694474 100644
--- a/src/core/server/http/router/index.ts
+++ b/src/core/server/http/router/index.ts
@@ -31,7 +31,6 @@ export {
RouteMethod,
RouteConfig,
RouteConfigOptions,
- RouteSchemas,
RouteContentType,
RouteConfigOptionsBody,
validBodyOutput,
@@ -55,3 +54,13 @@ export {
} from './response';
export { IKibanaSocket } from './socket';
+
+export {
+ RouteValidatorConfig,
+ RouteValidationSpec,
+ RouteValidationFunction,
+ RouteValidatorOptions,
+ RouteValidationError,
+ RouteValidatorFullConfig,
+ RouteValidationResultFactory,
+} from './validator';
diff --git a/src/core/server/http/router/request.test.ts b/src/core/server/http/router/request.test.ts
index ebb7ffa7a6fc9..51162a2c258e9 100644
--- a/src/core/server/http/router/request.test.ts
+++ b/src/core/server/http/router/request.test.ts
@@ -18,6 +18,7 @@
*/
import { KibanaRequest } from './request';
import { httpServerMock } from '../http_server.mocks';
+import { schema } from '@kbn/config-schema';
describe('KibanaRequest', () => {
describe('get all headers', () => {
@@ -64,4 +65,56 @@ describe('KibanaRequest', () => {
});
});
});
+
+ describe('RouteSchema type inferring', () => {
+ it('should work with config-schema', () => {
+ const body = Buffer.from('body!');
+ const request = {
+ ...httpServerMock.createRawRequest({
+ params: { id: 'params' },
+ query: { search: 'query' },
+ }),
+ payload: body, // Set outside because the mock is using `merge` by lodash and breaks the Buffer into arrays
+ } as any;
+ const kibanaRequest = KibanaRequest.from(request, {
+ params: schema.object({ id: schema.string() }),
+ query: schema.object({ search: schema.string() }),
+ body: schema.buffer(),
+ });
+ expect(kibanaRequest.params).toStrictEqual({ id: 'params' });
+ expect(kibanaRequest.params.id.toUpperCase()).toEqual('PARAMS'); // infers it's a string
+ expect(kibanaRequest.query).toStrictEqual({ search: 'query' });
+ expect(kibanaRequest.query.search.toUpperCase()).toEqual('QUERY'); // infers it's a string
+ expect(kibanaRequest.body).toEqual(body);
+ expect(kibanaRequest.body.byteLength).toBeGreaterThan(0); // infers it's a buffer
+ });
+
+ it('should work with ValidationFunction', () => {
+ const body = Buffer.from('body!');
+ const request = {
+ ...httpServerMock.createRawRequest({
+ params: { id: 'params' },
+ query: { search: 'query' },
+ }),
+ payload: body, // Set outside because the mock is using `merge` by lodash and breaks the Buffer into arrays
+ } as any;
+ const kibanaRequest = KibanaRequest.from(request, {
+ params: schema.object({ id: schema.string() }),
+ query: schema.object({ search: schema.string() }),
+ body: (data, { ok, badRequest }) => {
+ if (Buffer.isBuffer(data)) {
+ return ok(data);
+ } else {
+ return badRequest('It should be a Buffer', []);
+ }
+ },
+ });
+ expect(kibanaRequest.params).toStrictEqual({ id: 'params' });
+ expect(kibanaRequest.params.id.toUpperCase()).toEqual('PARAMS'); // infers it's a string
+ expect(kibanaRequest.query).toStrictEqual({ search: 'query' });
+ expect(kibanaRequest.query.search.toUpperCase()).toEqual('QUERY'); // infers it's a string
+ expect(kibanaRequest.body).toEqual(body);
+ expect(kibanaRequest.body.byteLength).toBeGreaterThan(0); // infers it's a buffer
+ });
+ });
});
diff --git a/src/core/server/http/router/request.ts b/src/core/server/http/router/request.ts
index b132899910569..47b001700b015 100644
--- a/src/core/server/http/router/request.ts
+++ b/src/core/server/http/router/request.ts
@@ -20,13 +20,11 @@
import { Url } from 'url';
import { Request } from 'hapi';
-import { ObjectType, Type, TypeOf } from '@kbn/config-schema';
-
-import { Stream } from 'stream';
import { deepFreeze, RecursiveReadonly } from '../../../utils';
import { Headers } from './headers';
-import { RouteMethod, RouteSchemas, RouteConfigOptions, validBodyOutput } from './route';
+import { RouteMethod, RouteConfigOptions, validBodyOutput } from './route';
import { KibanaSocket, IKibanaSocket } from './socket';
+import { RouteValidator, RouteValidatorFullConfig } from './validator';
const requestSymbol = Symbol('request');
@@ -70,12 +68,13 @@ export class KibanaRequest<
* instance of a KibanaRequest.
* @internal
*/
- public static from<
- P extends ObjectType,
- Q extends ObjectType,
- B extends ObjectType | Type , withoutSecretHeaders: boolean = true) {
- const requestParts = KibanaRequest.validate(req, routeSchemas);
+ public static from (
+ req: Request,
+ routeSchemas: RouteValidator | RouteValidatorFullConfig = {},
+ withoutSecretHeaders: boolean = true
+ ) {
+ const routeValidator = RouteValidator.from (routeSchemas);
+ const requestParts = KibanaRequest.validate(req, routeValidator);
return new KibanaRequest(
req,
requestParts.params,
@@ -91,40 +90,17 @@ export class KibanaRequest<
* received in the route handler.
* @internal
*/
- private static validate<
- P extends ObjectType,
- Q extends ObjectType,
- B extends ObjectType | Type (
req: Request,
- routeSchemas: RouteSchemas | undefined
+ routeValidator: RouteValidator
): {
- params: TypeOf ;
- query: TypeOf {
/**
* The endpoint _within_ the router path to register the route.
*
@@ -201,25 +195,10 @@ export interface RouteConfig<
* });
* ```
*/
- validate: RouteSchemas | false;
+ validate: RouteValidatorFullConfig | false;
/**
* Additional route options {@link RouteConfigOptions}.
*/
options?: RouteConfigOptions (
route: RouteConfig ,
handler: RequestHandler
) => void;
@@ -108,9 +98,7 @@ export interface IRouter {
* Wrap a router handler to catch and converts legacy boom errors to proper custom errors.
* @param handler {@link RequestHandler} - a route handler to wrap
*/
- handleLegacyErrors: (
- handler: RequestHandler
- ) => RequestHandler ;
+ handleLegacyErrors: (handler: RequestHandler ) => RequestHandler ;
/**
* Returns all routes registered with this router.
@@ -120,12 +108,9 @@ export interface IRouter {
getRoutes: () => RouterRoute[];
}
-export type ContextEnhancer<
- P extends ObjectType,
- Q extends ObjectType,
- B extends ObjectType,
- Method extends RouteMethod
-> = (handler: RequestHandler ) => RequestHandlerEnhanced ;
+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,
@@ -140,11 +125,10 @@ function getRouteFullPath(routerPath: string, routePath: string) {
* @returns Route schemas if `validate` is specified on the route, otherwise
* undefined.
*/
-function routeSchemasFromRouteConfig<
- P extends ObjectType,
- Q extends ObjectType,
- B extends ObjectType | Type , routeMethod: RouteMethod) {
+function routeSchemasFromRouteConfig (
+ 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) {
@@ -155,15 +139,17 @@ function routeSchemasFromRouteConfig<
if (route.validate !== false) {
Object.entries(route.validate).forEach(([key, schema]) => {
- if (!(schema instanceof Type)) {
+ if (!(schema instanceof Type || typeof schema === 'function')) {
throw new Error(
- `Expected a valid schema declared with '@kbn/config-schema' package at key: [${key}].`
+ `Expected a valid validation logic declared with '@kbn/config-schema' package or a RouteValidationFunction at key: [${key}].`
);
}
});
}
- return route.validate ? route.validate : undefined;
+ if (route.validate) {
+ return RouteValidator.from(route.validate);
+ }
}
/**
@@ -174,12 +160,7 @@ function routeSchemasFromRouteConfig<
*/
function validOptions(
method: RouteMethod,
- routeConfig: RouteConfig<
- ObjectType,
- ObjectType,
- ObjectType | Type (
route: RouteConfig ,
handler: RequestHandler
) => {
@@ -260,17 +237,11 @@ export class Router implements IRouter {
return [...this.routes];
}
- public handleLegacyErrors (
- handler: RequestHandler
- ): RequestHandler {
+ public handleLegacyErrors (handler: RequestHandler ): RequestHandler {
return wrapErrors(handler);
}
- private async handle<
- P extends ObjectType,
- Q extends ObjectType,
- B extends ObjectType | Type ({
routeSchemas,
request,
responseToolkit,
@@ -279,9 +250,9 @@ export class Router implements IRouter {
request: Request;
responseToolkit: ResponseToolkit;
handler: RequestHandlerEnhanced ;
- routeSchemas?: RouteSchemas ;
+ routeSchemas?: RouteValidator ;
}) {
- let kibanaRequest: KibanaRequest ;
const hapiResponseAdapter = new HapiResponseAdapter(responseToolkit);
try {
kibanaRequest = KibanaRequest.from(request, routeSchemas);
@@ -303,12 +274,9 @@ type WithoutHeadArgument = WithoutHeadArgument<
+ RequestHandler
+>;
/**
* A function executed when route path matched requested resource path.
@@ -345,12 +313,12 @@ type RequestHandlerEnhanced<
* @public
*/
export type RequestHandler<
- P extends ObjectType,
- Q extends ObjectType,
- B extends ObjectType | Type ,
response: KibanaResponseFactory
) => IKibanaResponse {
+ /**
+ * Validation logic for the URL params
+ * @public
+ */
+ params?: RouteValidationSpec ;
+ /**
+ * Validation logic for the Query params
+ * @public
+ */
+ query?: RouteValidationSpec = RouteValidatorConfig &
+ RouteValidatorOptions;
+
+/**
+ * Route validator class to define the validation logic for each new route.
+ *
+ * @internal
+ */
+export class RouteValidator {
+ public static from (
+ opts: RouteValidator | RouteValidatorFullConfig
+ ) {
+ if (opts instanceof RouteValidator) {
+ return opts;
+ }
+ const { params, query, body, ...options } = opts;
+ return new RouteValidator({ params, query, body }, options);
+ }
+
+ private static ResultFactory: RouteValidationResultFactory = {
+ ok: ,
+ private readonly options: RouteValidatorOptions = {}
+ ) {}
+
+ /**
+ * Get validated URL params
+ * @internal
+ */
+ public getParams(data: unknown, namespace?: string): Readonly {
+ return this.validate(this.config.params, this.options.unsafe?.params, data, namespace);
+ }
+
+ /**
+ * Get validated query params
+ * @internal
+ */
+ public getQuery(data: unknown, namespace?: string): Readonly (handler: RequestHandler ) => RequestHandler ;
+ handleLegacyErrors: (handler: RequestHandler ) => RequestHandler ;
patch: RouteRegistrar<'patch'>;
post: RouteRegistrar<'post'>;
put: RouteRegistrar<'put'>;
@@ -841,8 +842,10 @@ export class KibanaRequest , withoutSecretHeaders?: boolean): KibanaRequest ;
+ static from (req: Request, routeSchemas?: RouteValidator | RouteValidatorFullConfig , withoutSecretHeaders?: boolean): KibanaRequest ;
readonly headers: Headers;
// (undocumented)
readonly params: Params;
@@ -1159,7 +1162,7 @@ export type RedirectResponseOptions = HttpResponseOptions & {
};
// @public
-export type RequestHandler | Type = (context: RequestHandlerContext, request: KibanaRequest , response: KibanaResponseFactory) => IKibanaResponse | Type {
options?: RouteConfigOptions | false;
+ validate: RouteValidatorFullConfig | false;
}
// @public
@@ -1229,16 +1232,54 @@ export type RouteContentType = 'application/json' | 'application/*+json' | 'appl
export type RouteMethod = 'get' | 'post' | 'put' | 'delete' | 'patch' | 'options';
// @public
-export type RouteRegistrar | Type , handler: RequestHandler ) => void;
+export type RouteRegistrar (route: RouteConfig , handler: RequestHandler ) => void;
// @public
-export interface RouteSchemas | Type {
+ body?: RouteValidationSpec;
+ params?: RouteValidationSpec ;
+ query?: RouteValidationSpec = RouteValidatorConfig & RouteValidatorOptions;
+
+// @public
+export interface RouteValidatorOptions {
+ unsafe?: {
+ params?: boolean;
+ query?: boolean;
+ body?: boolean;
+ };
}
// @public (undocumented)
diff --git a/src/plugins/home/server/services/tutorials/lib/tutorials_registry_types.ts b/src/plugins/home/server/services/tutorials/lib/tutorials_registry_types.ts
index d081639e5c7e2..951ce935760a1 100644
--- a/src/plugins/home/server/services/tutorials/lib/tutorials_registry_types.ts
+++ b/src/plugins/home/server/services/tutorials/lib/tutorials_registry_types.ts
@@ -94,17 +94,5 @@ export interface TutorialSchema {
savedObjectsInstallMsg: string;
}
export type TutorialProvider = (context: { [key: string]: unknown }) => TutorialSchema;
-export type TutorialContextFactory = (
- req: KibanaRequest<
- Readonly<{
- [x: string]: any;
- }>,
- Readonly<{
- [x: string]: any;
- }>,
- Readonly<{
- [x: string]: any;
- }>
- >
-) => { [key: string]: unknown };
+export type TutorialContextFactory = (req: KibanaRequest) => { [key: string]: unknown };
export type ScopedTutorialContextFactory = (...args: any[]) => any;
diff --git a/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts b/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts
index 771e50b22f66b..91a4fe89c1c57 100644
--- a/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts
+++ b/test/plugin_functional/plugins/core_plugin_b/server/plugin.ts
@@ -18,6 +18,7 @@
*/
import { Plugin, CoreSetup } from 'kibana/server';
+import { schema } from '@kbn/config-schema';
import { PluginARequestContext } from '../../core_plugin_a/server';
declare module 'kibana/server' {
@@ -34,6 +35,25 @@ export class CorePluginBPlugin implements Plugin {
const response = await context.pluginA.ping();
return res.ok({ body: `Pong via plugin A: ${response}` });
});
+
+ router.post(
+ {
+ path: '/core_plugin_b',
+ validate: {
+ query: schema.object({ id: schema.string() }),
+ body: ({ bar, baz } = {}, { ok, badRequest }) => {
+ if (typeof bar === 'string' && bar === baz) {
+ return ok({ bar, baz });
+ } else {
+ return badRequest(`bar: ${bar} !== baz: ${baz} or they are not string`);
+ }
+ },
+ },
+ },
+ async (context, req, res) => {
+ return res.ok({ body: `ID: ${req.query.id} - ${req.body.bar.toUpperCase()}` });
+ }
+ );
}
public start() {}
diff --git a/test/plugin_functional/test_suites/core_plugins/server_plugins.ts b/test/plugin_functional/test_suites/core_plugins/server_plugins.ts
index 3881af5642996..d437912c1b69a 100644
--- a/test/plugin_functional/test_suites/core_plugins/server_plugins.ts
+++ b/test/plugin_functional/test_suites/core_plugins/server_plugins.ts
@@ -29,5 +29,29 @@ export default function({ getService }: PluginFunctionalProviderContext) {
.expect(200)
.expect('Pong via plugin A: true');
});
+
+ it('extend request handler context with validation', async () => {
+ await supertest
+ .post('/core_plugin_b')
+ .set('kbn-xsrf', 'anything')
+ .query({ id: 'TEST' })
+ .send({ bar: 'hi!', baz: 'hi!' })
+ .expect(200)
+ .expect('ID: TEST - HI!');
+ });
+
+ it('extend request handler context with validation (400)', async () => {
+ await supertest
+ .post('/core_plugin_b')
+ .set('kbn-xsrf', 'anything')
+ .query({ id: 'TEST' })
+ .send({ bar: 'hi!', baz: 1234 })
+ .expect(400)
+ .expect({
+ error: 'Bad Request',
+ message: '[request body]: bar: hi! !== baz: 1234 or they are not string',
+ statusCode: 400,
+ });
+ });
});
}
diff --git a/x-pack/legacy/plugins/apm/server/routes/create_api/index.ts b/x-pack/legacy/plugins/apm/server/routes/create_api/index.ts
index 3e97d851acd29..c1a9838e90406 100644
--- a/x-pack/legacy/plugins/apm/server/routes/create_api/index.ts
+++ b/x-pack/legacy/plugins/apm/server/routes/create_api/index.ts
@@ -82,7 +82,7 @@ export function createApi() {
// if any validation is defined. Not having validation currently
// means we don't get the payload. See
// https://github.com/elastic/kibana/issues/50179
- body: schema.nullable(anyObject) as typeof anyObject,
+ body: schema.nullable(anyObject),
params: anyObject,
query: anyObject
}
diff --git a/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts b/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts
index 5b4ada31a6b0c..71f6a4db8c30d 100644
--- a/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts
+++ b/x-pack/legacy/plugins/infra/server/lib/adapters/framework/adapter_types.ts
@@ -6,7 +6,6 @@
import { SearchResponse, GenericParams } from 'elasticsearch';
import { Lifecycle } from 'hapi';
-import { ObjectType } from '@kbn/config-schema';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { RouteMethod, RouteConfig } from '../../../../../../../../src/core/server';
import { PluginSetupContract as FeaturesPluginSetup } from '../../../../../../../plugins/features/server';
@@ -166,11 +165,6 @@ export interface InfraTSVBSeries {
export type InfraTSVBDataPoint = [number, number];
-export type InfraRouteConfig<
- params extends ObjectType,
- query extends ObjectType,
- body extends ObjectType,
- method extends RouteMethod
-> = {
+export type InfraRouteConfig (
fn: RequestHandler
) => RequestHandler = fn => {
return async (context, request, response) => {
diff --git a/x-pack/plugins/security/server/authorization/register_privileges_with_cluster.test.ts b/x-pack/plugins/security/server/authorization/register_privileges_with_cluster.test.ts
index c18c071ca7675..dc406c17925dd 100644
--- a/x-pack/plugins/security/server/authorization/register_privileges_with_cluster.test.ts
+++ b/x-pack/plugins/security/server/authorization/register_privileges_with_cluster.test.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { IClusterClient, Logger } from '../../../../../src/core/server';
+import { IClusterClient, Logger } from 'kibana/server';
import { RawKibanaPrivileges } from '../../common/model';
import { registerPrivilegesWithCluster } from './register_privileges_with_cluster';
diff --git a/x-pack/plugins/security/server/routes/authentication/saml.ts b/x-pack/plugins/security/server/routes/authentication/saml.ts
index 06acf5283fe97..465ea61e12a4e 100644
--- a/x-pack/plugins/security/server/routes/authentication/saml.ts
+++ b/x-pack/plugins/security/server/routes/authentication/saml.ts
@@ -60,7 +60,9 @@ export function defineSAMLRoutes({ router, logger, authc, csp, basePath }: Route
router.get(
{
path: '/api/security/saml/start',
- validate: { query: schema.object({ redirectURLFragment: schema.string() }) },
+ validate: {
+ query: schema.object({ redirectURLFragment: schema.string() }),
+ },
options: { authRequired: false },
},
async (context, request, response) => {
diff --git a/x-pack/plugins/security/server/routes/authorization/roles/delete.ts b/x-pack/plugins/security/server/routes/authorization/roles/delete.ts
index de966d6f2a758..eb56143288747 100644
--- a/x-pack/plugins/security/server/routes/authorization/roles/delete.ts
+++ b/x-pack/plugins/security/server/routes/authorization/roles/delete.ts
@@ -13,7 +13,9 @@ export function defineDeleteRolesRoutes({ router, clusterClient }: RouteDefiniti
router.delete(
{
path: '/api/security/role/{name}',
- validate: { params: schema.object({ name: schema.string({ minLength: 1 }) }) },
+ validate: {
+ params: schema.object({ name: schema.string({ minLength: 1 }) }),
+ },
},
createLicensedRouteHandler(async (context, request, response) => {
try {
diff --git a/x-pack/plugins/security/server/routes/authorization/roles/get.ts b/x-pack/plugins/security/server/routes/authorization/roles/get.ts
index 8c158bee1a15e..bf1140e2e6fd1 100644
--- a/x-pack/plugins/security/server/routes/authorization/roles/get.ts
+++ b/x-pack/plugins/security/server/routes/authorization/roles/get.ts
@@ -14,7 +14,9 @@ export function defineGetRolesRoutes({ router, authz, clusterClient }: RouteDefi
router.get(
{
path: '/api/security/role/{name}',
- validate: { params: schema.object({ name: schema.string({ minLength: 1 }) }) },
+ validate: {
+ params: schema.object({ name: schema.string({ minLength: 1 }) }),
+ },
},
createLicensedRouteHandler(async (context, request, response) => {
try {
diff --git a/x-pack/plugins/security/server/routes/licensed_route_handler.ts b/x-pack/plugins/security/server/routes/licensed_route_handler.ts
index 1194e3d0a83cc..bc5a6a1139215 100644
--- a/x-pack/plugins/security/server/routes/licensed_route_handler.ts
+++ b/x-pack/plugins/security/server/routes/licensed_route_handler.ts
@@ -4,17 +4,10 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { RequestHandler } from 'src/core/server';
-import { ObjectType } from '@kbn/config-schema';
+import { RequestHandler } from 'kibana/server';
import { LICENSE_CHECK_STATE } from '../../../licensing/server';
-export const createLicensedRouteHandler = <
- P extends ObjectType
-) => {
+export const createLicensedRouteHandler = (handler: RequestHandler ) => {
const licensedRouteHandler: RequestHandler = (context, request, responseToolkit) => {
const { license } = context.licensing;
const licenseCheck = license.check('security', 'basic');
diff --git a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts
index 4f78828b14dc2..4d8d08a487e9a 100644
--- a/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts
+++ b/x-pack/plugins/spaces/server/routes/api/external/copy_to_space.test.ts
@@ -11,7 +11,7 @@ import {
mockRouteContext,
mockRouteContextWithInvalidLicense,
} from '../__fixtures__';
-import { CoreSetup, IRouter, kibanaResponseFactory } from 'src/core/server';
+import { CoreSetup, IRouter, kibanaResponseFactory, RouteValidatorConfig } from 'src/core/server';
import {
loggingServiceMock,
elasticsearchServiceMock,
@@ -22,10 +22,9 @@ import { SpacesService } from '../../../spaces_service';
import { SpacesAuditLogger } from '../../../lib/audit_logger';
import { SpacesClient } from '../../../lib/spaces_client';
import { initCopyToSpacesApi } from './copy_to_space';
-import { ObjectType } from '@kbn/config-schema';
-import { RouteSchemas } from 'src/core/server/http/router/route';
import { spacesConfig } from '../../../lib/__fixtures__';
import { securityMock } from '../../../../../security/server/mocks';
+import { ObjectType } from '@kbn/config-schema';
describe('copy to space', () => {
const spacesSavedObjects = createSpaces();
@@ -78,19 +77,11 @@ describe('copy to space', () => {
return {
copyToSpace: {
- routeValidation: ctsRouteDefinition.validate as RouteSchemas<
- ObjectType,
- ObjectType,
- ObjectType
- >,
+ routeValidation: ctsRouteDefinition.validate as RouteValidatorConfig<{}, {}, {}>,
routeHandler: ctsRouteHandler,
},
resolveConflicts: {
- routeValidation: resolveRouteDefinition.validate as RouteSchemas<
- ObjectType,
- ObjectType,
- ObjectType
- >,
+ routeValidation: resolveRouteDefinition.validate as RouteValidatorConfig<{}, {}, {}>,
routeHandler: resolveRouteHandler,
},
savedObjectsRepositoryMock,
@@ -150,7 +141,7 @@ describe('copy to space', () => {
const { copyToSpace } = await setup();
expect(() =>
- copyToSpace.routeValidation.body!.validate(payload)
+ (copyToSpace.routeValidation.body as ObjectType).validate(payload)
).toThrowErrorMatchingInlineSnapshot(`"[spaces]: duplicate space ids are not allowed"`);
});
@@ -163,7 +154,7 @@ describe('copy to space', () => {
const { copyToSpace } = await setup();
expect(() =>
- copyToSpace.routeValidation.body!.validate(payload)
+ (copyToSpace.routeValidation.body as ObjectType).validate(payload)
).toThrowErrorMatchingInlineSnapshot(
`"[spaces.1]: lower case, a-z, 0-9, \\"_\\", and \\"-\\" are allowed"`
);
@@ -181,7 +172,7 @@ describe('copy to space', () => {
const { copyToSpace } = await setup();
expect(() =>
- copyToSpace.routeValidation.body!.validate(payload)
+ (copyToSpace.routeValidation.body as ObjectType).validate(payload)
).toThrowErrorMatchingInlineSnapshot(`"[objects]: duplicate objects are not allowed"`);
});
@@ -322,7 +313,7 @@ describe('copy to space', () => {
const { resolveConflicts } = await setup();
expect(() =>
- resolveConflicts.routeValidation.body!.validate(payload)
+ (resolveConflicts.routeValidation.body as ObjectType).validate(payload)
).toThrowErrorMatchingInlineSnapshot(`"[objects]: duplicate objects are not allowed"`);
});
@@ -343,7 +334,7 @@ describe('copy to space', () => {
const { resolveConflicts } = await setup();
expect(() =>
- resolveConflicts.routeValidation.body!.validate(payload)
+ (resolveConflicts.routeValidation.body as ObjectType).validate(payload)
).toThrowErrorMatchingInlineSnapshot(
`"[retries.key(\\"invalid-space-id!@#$%^&*()\\")]: Invalid space id: invalid-space-id!@#$%^&*()"`
);
diff --git a/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts b/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts
index 86da3023c515e..28d5708a3873c 100644
--- a/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts
+++ b/x-pack/plugins/spaces/server/routes/api/external/delete.test.ts
@@ -12,7 +12,7 @@ import {
mockRouteContext,
mockRouteContextWithInvalidLicense,
} from '../__fixtures__';
-import { CoreSetup, IRouter, kibanaResponseFactory } from 'src/core/server';
+import { CoreSetup, IRouter, kibanaResponseFactory, RouteValidatorConfig } from 'src/core/server';
import {
loggingServiceMock,
elasticsearchServiceMock,
@@ -23,10 +23,9 @@ import { SpacesService } from '../../../spaces_service';
import { SpacesAuditLogger } from '../../../lib/audit_logger';
import { SpacesClient } from '../../../lib/spaces_client';
import { initDeleteSpacesApi } from './delete';
-import { RouteSchemas } from 'src/core/server/http/router/route';
-import { ObjectType } from '@kbn/config-schema';
import { spacesConfig } from '../../../lib/__fixtures__';
import { securityMock } from '../../../../../security/server/mocks';
+import { ObjectType } from '@kbn/config-schema';
describe('Spaces Public API', () => {
const spacesSavedObjects = createSpaces();
@@ -75,14 +74,16 @@ describe('Spaces Public API', () => {
const [routeDefinition, routeHandler] = router.delete.mock.calls[0];
return {
- routeValidation: routeDefinition.validate as RouteSchemas
-) => {
+export const createLicensedRouteHandler = (handler: RequestHandler ) => {
const licensedRouteHandler: RequestHandler = (context, request, responseToolkit) => {
const { license } = context.licensing;
const licenseCheck = license.check('spaces', 'basic');
, TypeOf, Method>, response: KibanaResponseFactory) => IKibanaResponse
string
| The endpoint \_within\_ the router path to register the route. |
-| [validate](./kibana-plugin-server.routeconfig.validate.md) | RouteSchemas<P, Q, B> | false
| A schema created with @kbn/config-schema
that every request will be validated against. |
+| [validate](./kibana-plugin-server.routeconfig.validate.md) | RouteValidatorFullConfig<P, Q, B> | false
| A schema created with @kbn/config-schema
that every request will be validated against. |
diff --git a/docs/development/core/server/kibana-plugin-server.routeconfig.validate.md b/docs/development/core/server/kibana-plugin-server.routeconfig.validate.md
index e1ec743ae71cc..23a72fc3c68b3 100644
--- a/docs/development/core/server/kibana-plugin-server.routeconfig.validate.md
+++ b/docs/development/core/server/kibana-plugin-server.routeconfig.validate.md
@@ -9,7 +9,7 @@ A schema created with `@kbn/config-schema` that every request will be validated
Signature:
```typescript
-validate: RouteSchemasB
| |
-| [params](./kibana-plugin-server.routeschemas.params.md) | P
| |
-| [query](./kibana-plugin-server.routeschemas.query.md) | Q
| |
-
diff --git a/docs/development/core/server/kibana-plugin-server.routeschemas.params.md b/docs/development/core/server/kibana-plugin-server.routeschemas.params.md
deleted file mode 100644
index 3dbf9fed94dc0..0000000000000
--- a/docs/development/core/server/kibana-plugin-server.routeschemas.params.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteSchemas](./kibana-plugin-server.routeschemas.md) > [params](./kibana-plugin-server.routeschemas.params.md)
-
-## RouteSchemas.params property
-
-Signature:
-
-```typescript
-params?: P;
-```
diff --git a/docs/development/core/server/kibana-plugin-server.routeschemas.query.md b/docs/development/core/server/kibana-plugin-server.routeschemas.query.md
deleted file mode 100644
index 5be5830cb4bc8..0000000000000
--- a/docs/development/core/server/kibana-plugin-server.routeschemas.query.md
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteSchemas](./kibana-plugin-server.routeschemas.md) > [query](./kibana-plugin-server.routeschemas.query.md)
-
-## RouteSchemas.query property
-
-Signature:
-
-```typescript
-query?: Q;
-```
diff --git a/docs/development/core/server/kibana-plugin-server.routevalidationerror._constructor_.md b/docs/development/core/server/kibana-plugin-server.routevalidationerror._constructor_.md
new file mode 100644
index 0000000000000..551e13faaf154
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.routevalidationerror._constructor_.md
@@ -0,0 +1,21 @@
+
+
+[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteValidationError](./kibana-plugin-server.routevalidationerror.md) > [(constructor)](./kibana-plugin-server.routevalidationerror._constructor_.md)
+
+## RouteValidationError.(constructor)
+
+Constructs a new instance of the `RouteValidationError` class
+
+Signature:
+
+```typescript
+constructor(error: Error | string, path?: string[]);
+```
+
+## Parameters
+
+| Parameter | Type | Description |
+| --- | --- | --- |
+| error | Error | string
| |
+| path | string[]
| |
+
diff --git a/docs/development/core/server/kibana-plugin-server.routevalidationerror.md b/docs/development/core/server/kibana-plugin-server.routevalidationerror.md
new file mode 100644
index 0000000000000..71bd72dca2eab
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.routevalidationerror.md
@@ -0,0 +1,20 @@
+
+
+[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteValidationError](./kibana-plugin-server.routevalidationerror.md)
+
+## RouteValidationError class
+
+Error to return when the validation is not successful.
+
+Signature:
+
+```typescript
+export declare class RouteValidationError extends SchemaTypeError
+```
+
+## Constructors
+
+| Constructor | Modifiers | Description |
+| --- | --- | --- |
+| [(constructor)(error, path)](./kibana-plugin-server.routevalidationerror._constructor_.md) | | Constructs a new instance of the RouteValidationError
class |
+
diff --git a/docs/development/core/server/kibana-plugin-server.routevalidationfunction.md b/docs/development/core/server/kibana-plugin-server.routevalidationfunction.md
new file mode 100644
index 0000000000000..34fa096aaae78
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.routevalidationfunction.md
@@ -0,0 +1,42 @@
+
+
+[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteValidationFunction](./kibana-plugin-server.routevalidationfunction.md)
+
+## RouteValidationFunction type
+
+The custom validation function if @kbn/config-schema is not a valid solution for your specific plugin requirements.
+
+Signature:
+
+```typescript
+export declare type RouteValidationFunction(error: Error | string, path?: string[]) => {
error: RouteValidationError;
}
| |
+| [ok](./kibana-plugin-server.routevalidationresultfactory.ok.md) | <T>(value: T) => {
value: T;
}
| |
+
diff --git a/docs/development/core/server/kibana-plugin-server.routevalidationresultfactory.ok.md b/docs/development/core/server/kibana-plugin-server.routevalidationresultfactory.ok.md
new file mode 100644
index 0000000000000..eca6a31bd547f
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.routevalidationresultfactory.ok.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteValidationResultFactory](./kibana-plugin-server.routevalidationresultfactory.md) > [ok](./kibana-plugin-server.routevalidationresultfactory.ok.md)
+
+## RouteValidationResultFactory.ok property
+
+Signature:
+
+```typescript
+ok: RouteValidationSpec<B>
| Validation logic for the body payload |
+| [params](./kibana-plugin-server.routevalidatorconfig.params.md) | RouteValidationSpec<P>
| Validation logic for the URL params |
+| [query](./kibana-plugin-server.routevalidatorconfig.query.md) | RouteValidationSpec<Q>
| Validation logic for the Query params |
+
diff --git a/docs/development/core/server/kibana-plugin-server.routevalidatorconfig.params.md b/docs/development/core/server/kibana-plugin-server.routevalidatorconfig.params.md
new file mode 100644
index 0000000000000..11de25ff3b19f
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.routevalidatorconfig.params.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteValidatorConfig](./kibana-plugin-server.routevalidatorconfig.md) > [params](./kibana-plugin-server.routevalidatorconfig.params.md)
+
+## RouteValidatorConfig.params property
+
+Validation logic for the URL params
+
+Signature:
+
+```typescript
+params?: RouteValidationSpec;
+```
diff --git a/docs/development/core/server/kibana-plugin-server.routevalidatorfullconfig.md b/docs/development/core/server/kibana-plugin-server.routevalidatorfullconfig.md
new file mode 100644
index 0000000000000..0f3785b954a3a
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.routevalidatorfullconfig.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteValidatorFullConfig](./kibana-plugin-server.routevalidatorfullconfig.md)
+
+## RouteValidatorFullConfig type
+
+Route validations config and options merged into one object
+
+Signature:
+
+```typescript
+export declare type RouteValidatorFullConfig
{
params?: boolean;
query?: boolean;
body?: boolean;
}
| Set the unsafe
config to avoid running some additional internal \*safe\* validations on top of your custom validation |
+
diff --git a/docs/development/core/server/kibana-plugin-server.routevalidatoroptions.unsafe.md b/docs/development/core/server/kibana-plugin-server.routevalidatoroptions.unsafe.md
new file mode 100644
index 0000000000000..0406a372c4e9d
--- /dev/null
+++ b/docs/development/core/server/kibana-plugin-server.routevalidatoroptions.unsafe.md
@@ -0,0 +1,17 @@
+
+
+[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [RouteValidatorOptions](./kibana-plugin-server.routevalidatoroptions.md) > [unsafe](./kibana-plugin-server.routevalidatoroptions.unsafe.md)
+
+## RouteValidatorOptions.unsafe property
+
+Set the `unsafe` config to avoid running some additional internal \*safe\* validations on top of your custom validation
+
+Signature:
+
+```typescript
+unsafe?: {
+ params?: boolean;
+ query?: boolean;
+ body?: boolean;
+ };
+```
diff --git a/packages/kbn-config-schema/src/index.ts b/packages/kbn-config-schema/src/index.ts
index 56b3096433c24..fc3e3c541846a 100644
--- a/packages/kbn-config-schema/src/index.ts
+++ b/packages/kbn-config-schema/src/index.ts
@@ -59,6 +59,7 @@ import {
export { ObjectType, TypeOf, Type };
export { ByteSizeValue } from './byte_size_value';
+export { SchemaTypeError, ValidationError } from './errors';
function any(options?: TypeOptions, TypeOf, RouteMethod>,
+ request: KibanaRequest
;
- body: TypeOf;
+ params: P;
+ query: Q;
+ body: B;
} {
- if (routeSchemas === undefined) {
- return {
- body: {},
- params: {},
- query: {},
- };
- }
-
- const params =
- routeSchemas.params === undefined
- ? {}
- : routeSchemas.params.validate(req.params, {}, 'request params');
-
- const query =
- routeSchemas.query === undefined
- ? {}
- : routeSchemas.query.validate(req.query, {}, 'request query');
-
- const body =
- routeSchemas.body === undefined
- ? {}
- : routeSchemas.body.validate(req.payload, {}, 'request body');
+ const params = routeValidator.getParams(req.params, 'request params');
+ const query = routeValidator.getQuery(req.query, 'request query');
+ const body = routeValidator.getBody(req.payload, 'request body');
return { query, params, body };
}
diff --git a/src/core/server/http/router/route.ts b/src/core/server/http/router/route.ts
index 129cf4c922ffd..4439a80b1eac7 100644
--- a/src/core/server/http/router/route.ts
+++ b/src/core/server/http/router/route.ts
@@ -17,8 +17,7 @@
* under the License.
*/
-import { ObjectType, Type } from '@kbn/config-schema';
-import { Stream } from 'stream';
+import { RouteValidatorFullConfig } from './validator';
/**
* The set of common HTTP methods supported by Kibana routing.
@@ -124,12 +123,7 @@ export interface RouteConfigOptions
, TypeOf, typeof request.method>;
+ let kibanaRequest: KibanaRequest
, TypeOf, Method>,
+ request: KibanaRequest
;
+ /**
+ * Validation logic for the body payload
+ * @public
+ */
+ body?: RouteValidationSpec;
+}
+
+/**
+ * Additional options for the RouteValidator class to modify its default behaviour.
+ *
+ * @public
+ */
+export interface RouteValidatorOptions {
+ /**
+ * Set the `unsafe` config to avoid running some additional internal *safe* validations on top of your custom validation
+ * @public
+ */
+ unsafe?: {
+ params?: boolean;
+ query?: boolean;
+ body?: boolean;
+ };
+}
+
+/**
+ * Route validations config and options merged into one object
+ * @public
+ */
+export type RouteValidatorFullConfig
{
+ return this.validate(this.config.query, this.options.unsafe?.query, data, namespace);
+ }
+
+ /**
+ * Get validated body
+ * @internal
+ */
+ public getBody(data: unknown, namespace?: string): Readonly {
+ return this.validate(this.config.body, this.options.unsafe?.body, data, namespace);
+ }
+
+ /**
+ * Has body validation
+ * @internal
+ */
+ public hasBody(): boolean {
+ return typeof this.config.body !== 'undefined';
+ }
+
+ private validate
, TypeOf, Method>, response: KibanaResponseFactory) => IKibanaResponse
;
+}
+
+// @public
+export type RouteValidatorFullConfig