From 0d18acea3ef0a9dc06b15e1269c08dfff28b1e13 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 18 Sep 2024 13:43:06 +0200 Subject: [PATCH 01/12] delete old README --- .../http/core-http-server/src/versioning/README.md | 11 ----------- 1 file changed, 11 deletions(-) delete mode 100644 packages/core/http/core-http-server/src/versioning/README.md diff --git a/packages/core/http/core-http-server/src/versioning/README.md b/packages/core/http/core-http-server/src/versioning/README.md deleted file mode 100644 index 60553d059ada9..0000000000000 --- a/packages/core/http/core-http-server/src/versioning/README.md +++ /dev/null @@ -1,11 +0,0 @@ -This folder contains types for sever-side HTTP versioning. - -## Experimental - -The types in this package are all experimental and may be subject to extensive changes. -Use this package as a reference for current thinking and as a starting point to -raise questions and discussion. - -## Versioning specification - -Currently the versioning spec is being designed. \ No newline at end of file From ede49dd44d6cd96d374baa763a3f0933faee6101 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 18 Sep 2024 13:54:02 +0200 Subject: [PATCH 02/12] added deprecation description to router, v1 --- packages/core/http/core-http-server/index.ts | 3 + .../http/core-http-server/src/router/index.ts | 3 + .../http/core-http-server/src/router/route.ts | 66 +++++++++++++++++-- 3 files changed, 67 insertions(+), 5 deletions(-) diff --git a/packages/core/http/core-http-server/index.ts b/packages/core/http/core-http-server/index.ts index 64387e5ca36d7..f4a96fe2d6b5a 100644 --- a/packages/core/http/core-http-server/index.ts +++ b/packages/core/http/core-http-server/index.ts @@ -107,6 +107,9 @@ export type { RouteValidatorFullConfigResponse, LazyValidator, RouteAccess, + RouteDeprecation, + RouteDeprecationDescription, + RouteInputDeprecation, } from './src/router'; export { validBodyOutput, diff --git a/packages/core/http/core-http-server/src/router/index.ts b/packages/core/http/core-http-server/src/router/index.ts index 89e9a345179b6..eb838e6536b82 100644 --- a/packages/core/http/core-http-server/src/router/index.ts +++ b/packages/core/http/core-http-server/src/router/index.ts @@ -53,6 +53,9 @@ export type { RouteContentType, SafeRouteMethod, RouteAccess, + RouteDeprecation, + RouteDeprecationDescription, + RouteInputDeprecation, } from './route'; export { validBodyOutput } from './route'; export type { diff --git a/packages/core/http/core-http-server/src/router/route.ts b/packages/core/http/core-http-server/src/router/route.ts index c47688b60d3cd..ebc9b20b06141 100644 --- a/packages/core/http/core-http-server/src/router/route.ts +++ b/packages/core/http/core-http-server/src/router/route.ts @@ -111,11 +111,68 @@ export interface RouteConfigOptionsBody { */ export type RouteAccess = 'public' | 'internal'; +/** + * @remark When providing a deprecation description like: "This API will be + * removed in version X.X.X., use Y instead" ensure that the string + * contains no dynamically computed parts. + */ +export type RouteDeprecationDescription = O extends object + ? { + [k in keyof O]: + | string + | { message: string; check: (v: O[k]) => boolean } + | RouteDeprecationDescription; + } + : never; + +export interface RouteInputDeprecation

{ + query?: RouteDeprecationDescription

; + params?: RouteDeprecationDescription; + body?: RouteDeprecationDescription; +} + +/** + * Description of deprecations for this HTTP API. + * + * @remark This will assist Kibana HTTP API users when upgrading to new versions + * of the Elastic stack (via Upgrade Assistant) and will be surfaced in documentation + * created from HTTP API introspection (like OAS). + * + * string - Provide a string to mark this route as deprecated along with a description like: + * "This route is deprecated and staged for removal by X.X.X. Use /another/cool/route instead" + * boolean - Set this to `true` to specify that this entire route is deprecated. + * + * It's also possible to provide deprecation messages about sub-parts of the route. Consider this + * example of a route deprecating an enum value from its request body: + * + * ```ts + * { + * body: { + * foo: { + * type: { check: (v) => v === "bar", message : "'bar' is deprecated. Use 'qux' or 'baz' instead." } + * } + * } + * } + * ``` + * + * @default false + * @public + */ +export type RouteDeprecation

= + | boolean + | string + | RouteInputDeprecation; + /** * Additional route options. * @public */ -export interface RouteConfigOptions { +export interface RouteConfigOptions< + Method extends RouteMethod, + P = unknown, + Q = unknown, + B = unknown +> { /** * Defines authentication mode for a route: * - true. A user has to have valid credentials to access a resource @@ -201,12 +258,11 @@ export interface RouteConfigOptions { description?: string; /** - * Setting this to `true` declares this route to be deprecated. Consumers SHOULD - * refrain from usage of this route. + * A description of this routes deprecations. * - * @remarks This will be surfaced in OAS documentation. + * @remarks This may be surfaced in OAS documentation. */ - deprecated?: boolean; + deprecated?: RouteDeprecation; /** * Release version or date that this route will be removed From 59e896b1cef0161f4f157cf901911f0a856adbf6 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 18 Sep 2024 13:54:29 +0200 Subject: [PATCH 03/12] added enhanced deprecations to versioned router --- .../http/core-http-server/src/versioning/types.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/core/http/core-http-server/src/versioning/types.ts b/packages/core/http/core-http-server/src/versioning/types.ts index c552abd251a1f..a9b2e066df4c3 100644 --- a/packages/core/http/core-http-server/src/versioning/types.ts +++ b/packages/core/http/core-http-server/src/versioning/types.ts @@ -19,12 +19,20 @@ import type { RequestHandlerContextBase, RouteValidationFunction, LazyValidator, + RouteInputDeprecation, } from '../..'; type RqCtx = RequestHandlerContextBase; export type { ApiVersion }; +/** + * It is better practice to provide a string that can be surfaced to end users + * guiding them to alternative routes. + * @public + */ +export type VersionedRouteDeprecation = boolean | string; + /** * Configuration for a versioned route * @public @@ -93,7 +101,7 @@ export type VersionedRouteConfig = Omit< * * @default false */ - deprecated?: boolean; + deprecated?: VersionedRouteDeprecation; /** * Release version or date that this route will be removed @@ -337,6 +345,11 @@ export interface AddVersionOpts { * @public */ validate: false | VersionedRouteValidation | (() => VersionedRouteValidation); // Provide a way to lazily load validation schemas + + /** + * A description of which parts of this route are deprecated. + */ + deprecated?: RouteInputDeprecation; } /** From 48fa2a13aecfbb68efdfe99fe76bf60be488891d Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Wed, 18 Sep 2024 15:12:46 +0200 Subject: [PATCH 04/12] oh boy --- packages/core/http/core-http-server/src/router/route.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core/http/core-http-server/src/router/route.ts b/packages/core/http/core-http-server/src/router/route.ts index ebc9b20b06141..a33df6a5aa6c1 100644 --- a/packages/core/http/core-http-server/src/router/route.ts +++ b/packages/core/http/core-http-server/src/router/route.ts @@ -116,14 +116,16 @@ export type RouteAccess = 'public' | 'internal'; * removed in version X.X.X., use Y instead" ensure that the string * contains no dynamically computed parts. */ -export type RouteDeprecationDescription = O extends object +export type RouteDeprecationDescription = O extends unknown[] + ? RouteDeprecationDescription + : O extends object ? { [k in keyof O]: | string | { message: string; check: (v: O[k]) => boolean } | RouteDeprecationDescription; } - : never; + : string | { message: string; check: (v: O) => boolean }; export interface RouteInputDeprecation

{ query?: RouteDeprecationDescription

; From 66264aef3d4ef9e94dc9544ab21079f493638898 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 19 Sep 2024 12:56:26 +0200 Subject: [PATCH 05/12] limit types of interfaces --- .../http/core-http-server/src/router/route.ts | 26 ++++++++++++------- packages/kbn-utility-types/index.ts | 6 +++++ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/packages/core/http/core-http-server/src/router/route.ts b/packages/core/http/core-http-server/src/router/route.ts index a33df6a5aa6c1..62c8669098636 100644 --- a/packages/core/http/core-http-server/src/router/route.ts +++ b/packages/core/http/core-http-server/src/router/route.ts @@ -7,6 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { RemoveIndexSignatures } from '@kbn/utility-types'; import type { RouteValidator } from './route_validator'; /** @@ -112,21 +113,26 @@ export interface RouteConfigOptionsBody { export type RouteAccess = 'public' | 'internal'; /** - * @remark When providing a deprecation description like: "This API will be - * removed in version X.X.X., use Y instead" ensure that the string - * contains no dynamically computed parts. + * @remark When providing a deprecation description like: "Use Y instead" ensure + * that the string contains no dynamically computed parts. + * + * @remark this is limited to interfaces with well-defined interfaces, Re */ export type RouteDeprecationDescription = O extends unknown[] - ? RouteDeprecationDescription + ? // Unwrap arrays + RouteDeprecationDescription + : O extends Map + ? // Map is not supported + never : O extends object ? { - [k in keyof O]: - | string - | { message: string; check: (v: O[k]) => boolean } - | RouteDeprecationDescription; + [K in keyof RemoveIndexSignatures]?: string | RouteDeprecationDescription; } - : string | { message: string; check: (v: O) => boolean }; + : string; +/** + * Declare route input deprecations. + */ export interface RouteInputDeprecation

{ query?: RouteDeprecationDescription

; params?: RouteDeprecationDescription; @@ -357,5 +363,5 @@ export interface RouteConfig { /** * Additional route options {@link RouteConfigOptions}. */ - options?: RouteConfigOptions; + options?: RouteConfigOptions; } diff --git a/packages/kbn-utility-types/index.ts b/packages/kbn-utility-types/index.ts index 78626bc7fdc55..6abdf28547128 100644 --- a/packages/kbn-utility-types/index.ts +++ b/packages/kbn-utility-types/index.ts @@ -172,3 +172,9 @@ export type RecursivePartial = { : RecursivePartial; }; type NonAny = number | boolean | string | symbol | null; + +export type RemoveIndexSignatures = { + // copy all attributes from the person interface + // and remove the index signature + [K in keyof O as string extends K ? never : number extends K ? never : K]: O[K]; +}; From 502d66f7b033fd3557055bbacb29e52b4ff14578 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 19 Sep 2024 16:15:56 +0200 Subject: [PATCH 06/12] wip idea --- .../route_deprecations/find_deprecations.ts | 30 +++++++++++++ .../src/route_deprecations/index.ts | 10 +++++ .../route_deprecations/route_deprecations.ts | 44 +++++++++++++++++++ 3 files changed, 84 insertions(+) create mode 100644 packages/core/root/core-root-server-internal/src/route_deprecations/find_deprecations.ts create mode 100644 packages/core/root/core-root-server-internal/src/route_deprecations/index.ts create mode 100644 packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts diff --git a/packages/core/root/core-root-server-internal/src/route_deprecations/find_deprecations.ts b/packages/core/root/core-root-server-internal/src/route_deprecations/find_deprecations.ts new file mode 100644 index 0000000000000..bba7f38bb6d83 --- /dev/null +++ b/packages/core/root/core-root-server-internal/src/route_deprecations/find_deprecations.ts @@ -0,0 +1,30 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { get } from 'lodash'; +import type { RouteInputDeprecationDescription } from '@kbn/core-http-server'; + +export function findInputDeprecations( + input: { query: unknown; body: unknown }, + deprecations: RouteInputDeprecationDescription[] +): string[] { + const messages: string[] = []; + for (const deprecation of deprecations) { + if (deprecation.type === 'renamed') { + if (!!get(input[deprecation.location], deprecation.old)) { + messages.push(`"${deprecation.old}" has been removed. Use "${deprecation.new}" instead.`); + } + } else if (deprecation.type === 'removed') { + if (!!get(input[deprecation.location], deprecation.path)) { + messages.push(`"${deprecation.path}" has been removed.`); + } + } + } + return messages; +} diff --git a/packages/core/root/core-root-server-internal/src/route_deprecations/index.ts b/packages/core/root/core-root-server-internal/src/route_deprecations/index.ts new file mode 100644 index 0000000000000..5729dff065a25 --- /dev/null +++ b/packages/core/root/core-root-server-internal/src/route_deprecations/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +export { createRouteDeprecationsHandler } from './route_deprecations'; diff --git a/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts b/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts new file mode 100644 index 0000000000000..ea725722a1528 --- /dev/null +++ b/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { OnPostAuthHandler } from '@kbn/core-http-server'; +import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-server-internal'; +import type { Logger } from '@kbn/logging'; + +import { findInputDeprecations } from './find_deprecations'; + +interface Dependencies { + log: Logger; + coreUsageData: InternalCoreUsageDataSetup; +} + +export function createRouteDeprecationsHandler({ coreUsageData }: Dependencies): OnPostAuthHandler { + return (req, res, toolkit) => { + const { + route: { + options: { deprecated }, + }, + } = req; + const messages = []; + if (typeof deprecated === 'boolean' && deprecated === true) { + // Log route level deprecation + } else if (typeof deprecated === 'string') { + // Log route level deprecation + message + } else if (typeof deprecated === 'object') { + messages.push(...findInputDeprecations(req, deprecated() /* TODO */)); + // Log route input level deprecation + message + } + for (const message of messages) { + coreUsageData.incrementUsageCounter({ + counterName: `[${req.route.method}]${req.route.path} ${message}`, + }); + } + return toolkit.next(); + }; +} From 37c3361426cfe93f227624df76d74cd1c8bf5e66 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Thu, 19 Sep 2024 16:16:07 +0200 Subject: [PATCH 07/12] wip idea 2 --- packages/core/http/core-http-server/index.ts | 3 +- .../http/core-http-server/src/router/index.ts | 3 +- .../http/core-http-server/src/router/route.ts | 46 +++++++++++-------- .../core-root-server-internal/src/server.ts | 4 ++ 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/packages/core/http/core-http-server/index.ts b/packages/core/http/core-http-server/index.ts index f4a96fe2d6b5a..c6bbeda56069c 100644 --- a/packages/core/http/core-http-server/index.ts +++ b/packages/core/http/core-http-server/index.ts @@ -108,8 +108,9 @@ export type { LazyValidator, RouteAccess, RouteDeprecation, - RouteDeprecationDescription, + RouteInputDeprecationDescription, RouteInputDeprecation, + RouteInputDeprecationFactory, } from './src/router'; export { validBodyOutput, diff --git a/packages/core/http/core-http-server/src/router/index.ts b/packages/core/http/core-http-server/src/router/index.ts index eb838e6536b82..7c959519858fb 100644 --- a/packages/core/http/core-http-server/src/router/index.ts +++ b/packages/core/http/core-http-server/src/router/index.ts @@ -54,8 +54,9 @@ export type { SafeRouteMethod, RouteAccess, RouteDeprecation, - RouteDeprecationDescription, + RouteInputDeprecationDescription, RouteInputDeprecation, + RouteInputDeprecationFactory, } from './route'; export { validBodyOutput } from './route'; export type { diff --git a/packages/core/http/core-http-server/src/router/route.ts b/packages/core/http/core-http-server/src/router/route.ts index 62c8669098636..dba2cfcbdcff5 100644 --- a/packages/core/http/core-http-server/src/router/route.ts +++ b/packages/core/http/core-http-server/src/router/route.ts @@ -112,31 +112,41 @@ export interface RouteConfigOptionsBody { */ export type RouteAccess = 'public' | 'internal'; +type InputDeprecationLocation = 'query' | 'body'; + +export interface RouteInputDeprecationRenamedDescription { + type: 'renamed'; + location: InputDeprecationLocation; + old: string; + new: string; +} + +export interface RouteInputDeprecationRemovedDescription { + type: 'removed'; + location: InputDeprecationLocation; + path: string; +} + +export type RouteInputDeprecationDescription = + | RouteInputDeprecationRenamedDescription + | RouteInputDeprecationRemovedDescription; + /** - * @remark When providing a deprecation description like: "Use Y instead" ensure - * that the string contains no dynamically computed parts. - * - * @remark this is limited to interfaces with well-defined interfaces, Re + * For now, this is only supports a small subset of possible deprecations. + * @public */ -export type RouteDeprecationDescription = O extends unknown[] - ? // Unwrap arrays - RouteDeprecationDescription - : O extends Map - ? // Map is not supported - never - : O extends object - ? { - [K in keyof RemoveIndexSignatures]?: string | RouteDeprecationDescription; - } - : string; +export type RouteInputDeprecationFactory = (factories: { + removed(path: string): RouteInputDeprecationRemovedDescription; + renamed(input: { oldPath: string; newPath: string }): RouteInputDeprecationRenamedDescription; +}) => RouteInputDeprecationDescription[]; /** * Declare route input deprecations. + * @public */ export interface RouteInputDeprecation

{ - query?: RouteDeprecationDescription

; - params?: RouteDeprecationDescription; - body?: RouteDeprecationDescription; + query?: RouteInputDeprecationFactory; + body?: RouteInputDeprecationFactory; } /** diff --git a/packages/core/root/core-root-server-internal/src/server.ts b/packages/core/root/core-root-server-internal/src/server.ts index 64cf2c936e3be..f04f182d3def3 100644 --- a/packages/core/root/core-root-server-internal/src/server.ts +++ b/packages/core/root/core-root-server-internal/src/server.ts @@ -519,6 +519,10 @@ export class Server { return new CoreRouteHandlerContext(this.coreStart!, req); } ); + + coreSetup.http.registerOnPostAuth((req, res, toolkit) => { + return toolkit.next(); + }); } public setupCoreConfig() { From 3f8abb845a118bc24719e5a85b0e27f4fd33c5ae Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 20 Sep 2024 10:02:38 +0200 Subject: [PATCH 08/12] clean up types --- .../http/core-http-server/src/router/route.ts | 22 +++++++++---------- packages/kbn-utility-types/index.ts | 6 ----- 2 files changed, 11 insertions(+), 17 deletions(-) diff --git a/packages/core/http/core-http-server/src/router/route.ts b/packages/core/http/core-http-server/src/router/route.ts index dba2cfcbdcff5..b78568e453164 100644 --- a/packages/core/http/core-http-server/src/router/route.ts +++ b/packages/core/http/core-http-server/src/router/route.ts @@ -7,7 +7,6 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import { RemoveIndexSignatures } from '@kbn/utility-types'; import type { RouteValidator } from './route_validator'; /** @@ -112,27 +111,31 @@ export interface RouteConfigOptionsBody { */ export type RouteAccess = 'public' | 'internal'; -type InputDeprecationLocation = 'query' | 'body'; +/** @public */ +export type InputDeprecationLocation = 'query' | 'body'; -export interface RouteInputDeprecationRenamedDescription { +/** @public */ +export interface RouteInputDeprecationRenamedDescription { type: 'renamed'; location: InputDeprecationLocation; old: string; new: string; } -export interface RouteInputDeprecationRemovedDescription { +/** @public */ +export interface RouteInputDeprecationRemovedDescription { type: 'removed'; location: InputDeprecationLocation; path: string; } +/** @public */ export type RouteInputDeprecationDescription = | RouteInputDeprecationRenamedDescription | RouteInputDeprecationRemovedDescription; /** - * For now, this is only supports a small subset of possible deprecations. + * Factory for supported types of route API deprecations. * @public */ export type RouteInputDeprecationFactory = (factories: { @@ -144,7 +147,7 @@ export type RouteInputDeprecationFactory = (factories: { * Declare route input deprecations. * @public */ -export interface RouteInputDeprecation

{ +export interface RouteInputDeprecation { query?: RouteInputDeprecationFactory; body?: RouteInputDeprecationFactory; } @@ -176,10 +179,7 @@ export interface RouteInputDeprecation

{ * @default false * @public */ -export type RouteDeprecation

= - | boolean - | string - | RouteInputDeprecation; +export type RouteDeprecation = boolean | string | RouteInputDeprecation; /** * Additional route options. @@ -280,7 +280,7 @@ export interface RouteConfigOptions< * * @remarks This may be surfaced in OAS documentation. */ - deprecated?: RouteDeprecation; + deprecated?: RouteDeprecation; /** * Release version or date that this route will be removed diff --git a/packages/kbn-utility-types/index.ts b/packages/kbn-utility-types/index.ts index 6abdf28547128..78626bc7fdc55 100644 --- a/packages/kbn-utility-types/index.ts +++ b/packages/kbn-utility-types/index.ts @@ -172,9 +172,3 @@ export type RecursivePartial = { : RecursivePartial; }; type NonAny = number | boolean | string | symbol | null; - -export type RemoveIndexSignatures = { - // copy all attributes from the person interface - // and remove the index signature - [K in keyof O as string extends K ? never : number extends K ? never : K]: O[K]; -}; From 8dc9aadfd12b676d42efd707e31aba51376262fb Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 20 Sep 2024 11:05:46 +0200 Subject: [PATCH 09/12] wip 3 --- .../core-http-router-server-internal/index.ts | 1 + .../src/deprecations.ts | 66 +++++++++++++++++++ packages/core/http/core-http-server/index.ts | 3 + .../http/core-http-server/src/router/index.ts | 5 +- .../http/core-http-server/src/router/route.ts | 10 +-- .../core-http-server/src/versioning/types.ts | 2 +- .../route_deprecations/find_deprecations.ts | 30 --------- .../route_deprecations/route_deprecations.ts | 22 ++++--- 8 files changed, 93 insertions(+), 46 deletions(-) create mode 100644 packages/core/http/core-http-router-server-internal/src/deprecations.ts delete mode 100644 packages/core/root/core-root-server-internal/src/route_deprecations/find_deprecations.ts diff --git a/packages/core/http/core-http-router-server-internal/index.ts b/packages/core/http/core-http-router-server-internal/index.ts index 6c684d5f8169c..c763932bf51da 100644 --- a/packages/core/http/core-http-router-server-internal/index.ts +++ b/packages/core/http/core-http-router-server-internal/index.ts @@ -27,3 +27,4 @@ export { isKibanaRequest, isRealRequest, ensureRawRequest, CoreKibanaRequest } f export { isSafeMethod } from './src/route'; export { HapiResponseAdapter } from './src/response_adapter'; export { kibanaResponseFactory, lifecycleResponseFactory, KibanaResponse } from './src/response'; +export { buildDeprecations } from './src/deprecations'; diff --git a/packages/core/http/core-http-router-server-internal/src/deprecations.ts b/packages/core/http/core-http-router-server-internal/src/deprecations.ts new file mode 100644 index 0000000000000..29378836bac04 --- /dev/null +++ b/packages/core/http/core-http-router-server-internal/src/deprecations.ts @@ -0,0 +1,66 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import { + RouteInputDeprecation, + RouteInputDeprecationLocation, + RouteInputDeprecationDescription, +} from '@kbn/core-http-server'; +import { get } from 'lodash'; + +interface BuilderCommonArgs { + location: RouteInputDeprecationLocation; +} + +export type RouteInputDeprecationInternalDescription = RouteInputDeprecationDescription & { + message: string; + check: (v: unknown) => boolean; +}; + +export const buildRename = + ({ location }: BuilderCommonArgs) => + (oldPath: string, newPath: string) => ({ + type: 'renamed' as const, + location, + message: `"${oldPath}" has been removed. Use "${newPath}" instead.`, + new: newPath, + old: oldPath, + check: (input: unknown) => Boolean(get(input, oldPath)), + }); + +export const buildRemove = + ({ location }: BuilderCommonArgs) => + (path: string) => ({ + type: 'removed' as const, + location, + message: `"${path}" has been removed.`, + path, + check: (input: unknown) => Boolean(get(input, path)), + }); + +export function buildDeprecations({ + body: bodyFactory, + query: queryFactory, +}: RouteInputDeprecation): RouteInputDeprecationInternalDescription[] { + const deprecations: RouteInputDeprecationInternalDescription[] = []; + for (const [factory, location] of [ + [bodyFactory, 'body'], + [queryFactory, 'query'], + ] as const) { + if (factory) { + deprecations.push( + ...(factory({ + rename: buildRename({ location }), + remove: buildRemove({ location }), + }) as RouteInputDeprecationInternalDescription[]) + ); + } + } + return deprecations; +} diff --git a/packages/core/http/core-http-server/index.ts b/packages/core/http/core-http-server/index.ts index c6bbeda56069c..8ef3f8c0a85de 100644 --- a/packages/core/http/core-http-server/index.ts +++ b/packages/core/http/core-http-server/index.ts @@ -111,6 +111,9 @@ export type { RouteInputDeprecationDescription, RouteInputDeprecation, RouteInputDeprecationFactory, + RouteInputDeprecationLocation, + RouteInputDeprecationRemovedDescription, + RouteInputDeprecationRenamedDescription, } from './src/router'; export { validBodyOutput, diff --git a/packages/core/http/core-http-server/src/router/index.ts b/packages/core/http/core-http-server/src/router/index.ts index 7c959519858fb..abb1b23b2c739 100644 --- a/packages/core/http/core-http-server/src/router/index.ts +++ b/packages/core/http/core-http-server/src/router/index.ts @@ -54,9 +54,12 @@ export type { SafeRouteMethod, RouteAccess, RouteDeprecation, - RouteInputDeprecationDescription, RouteInputDeprecation, RouteInputDeprecationFactory, + RouteInputDeprecationLocation, + RouteInputDeprecationDescription, + RouteInputDeprecationRemovedDescription, + RouteInputDeprecationRenamedDescription, } from './route'; export { validBodyOutput } from './route'; export type { diff --git a/packages/core/http/core-http-server/src/router/route.ts b/packages/core/http/core-http-server/src/router/route.ts index b78568e453164..4b9bdfb5e9891 100644 --- a/packages/core/http/core-http-server/src/router/route.ts +++ b/packages/core/http/core-http-server/src/router/route.ts @@ -112,12 +112,12 @@ export interface RouteConfigOptionsBody { export type RouteAccess = 'public' | 'internal'; /** @public */ -export type InputDeprecationLocation = 'query' | 'body'; +export type RouteInputDeprecationLocation = 'query' | 'body'; /** @public */ export interface RouteInputDeprecationRenamedDescription { type: 'renamed'; - location: InputDeprecationLocation; + location: RouteInputDeprecationLocation; old: string; new: string; } @@ -125,7 +125,7 @@ export interface RouteInputDeprecationRenamedDescription { /** @public */ export interface RouteInputDeprecationRemovedDescription { type: 'removed'; - location: InputDeprecationLocation; + location: RouteInputDeprecationLocation; path: string; } @@ -139,8 +139,8 @@ export type RouteInputDeprecationDescription = * @public */ export type RouteInputDeprecationFactory = (factories: { - removed(path: string): RouteInputDeprecationRemovedDescription; - renamed(input: { oldPath: string; newPath: string }): RouteInputDeprecationRenamedDescription; + remove(path: string): RouteInputDeprecationRemovedDescription; + rename(oldPath: string, newPath: string): RouteInputDeprecationRenamedDescription; }) => RouteInputDeprecationDescription[]; /** diff --git a/packages/core/http/core-http-server/src/versioning/types.ts b/packages/core/http/core-http-server/src/versioning/types.ts index a9b2e066df4c3..dd5d3dd7ba1ab 100644 --- a/packages/core/http/core-http-server/src/versioning/types.ts +++ b/packages/core/http/core-http-server/src/versioning/types.ts @@ -349,7 +349,7 @@ export interface AddVersionOpts { /** * A description of which parts of this route are deprecated. */ - deprecated?: RouteInputDeprecation; + deprecated?: RouteInputDeprecation; } /** diff --git a/packages/core/root/core-root-server-internal/src/route_deprecations/find_deprecations.ts b/packages/core/root/core-root-server-internal/src/route_deprecations/find_deprecations.ts deleted file mode 100644 index bba7f38bb6d83..0000000000000 --- a/packages/core/root/core-root-server-internal/src/route_deprecations/find_deprecations.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { get } from 'lodash'; -import type { RouteInputDeprecationDescription } from '@kbn/core-http-server'; - -export function findInputDeprecations( - input: { query: unknown; body: unknown }, - deprecations: RouteInputDeprecationDescription[] -): string[] { - const messages: string[] = []; - for (const deprecation of deprecations) { - if (deprecation.type === 'renamed') { - if (!!get(input[deprecation.location], deprecation.old)) { - messages.push(`"${deprecation.old}" has been removed. Use "${deprecation.new}" instead.`); - } - } else if (deprecation.type === 'removed') { - if (!!get(input[deprecation.location], deprecation.path)) { - messages.push(`"${deprecation.path}" has been removed.`); - } - } - } - return messages; -} diff --git a/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts b/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts index ea725722a1528..053d431dfb36e 100644 --- a/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts +++ b/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts @@ -11,9 +11,10 @@ import type { OnPostAuthHandler } from '@kbn/core-http-server'; import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-server-internal'; import type { Logger } from '@kbn/logging'; -import { findInputDeprecations } from './find_deprecations'; +import { buildDeprecations } from '@kbn/core-http-router-server-internal'; interface Dependencies { + logRouteApiDeprecations: boolean; log: Logger; coreUsageData: InternalCoreUsageDataSetup; } @@ -22,22 +23,25 @@ export function createRouteDeprecationsHandler({ coreUsageData }: Dependencies): return (req, res, toolkit) => { const { route: { - options: { deprecated }, + options: { deprecated: deprecatedInput }, }, } = req; const messages = []; - if (typeof deprecated === 'boolean' && deprecated === true) { + if (typeof deprecatedInput === 'boolean' && deprecatedInput === true) { // Log route level deprecation - } else if (typeof deprecated === 'string') { + messages.push(`${req.route.method} ${req.route.path} is deprecated.`); + } else if (typeof deprecatedInput === 'string') { // Log route level deprecation + message - } else if (typeof deprecated === 'object') { - messages.push(...findInputDeprecations(req, deprecated() /* TODO */)); + messages.push(deprecatedInput); + } else if (typeof deprecatedInput === 'object') { // Log route input level deprecation + message + const deprecations = buildDeprecations(deprecatedInput); + for (const { check, message, location } of deprecations) { + if (check(req[location])) messages.push(`${req.route.method} ${req.route.path} ${message}`); + } } for (const message of messages) { - coreUsageData.incrementUsageCounter({ - counterName: `[${req.route.method}]${req.route.path} ${message}`, - }); + coreUsageData.incrementUsageCounter({ counterName: message }); // TODO(jloleysens) figure this part out } return toolkit.next(); }; From 27131ade9567ce07ec6730d17edcde4e4daad996 Mon Sep 17 00:00:00 2001 From: Jean-Louis Leysens Date: Fri, 20 Sep 2024 12:49:49 +0200 Subject: [PATCH 10/12] wip 4 --- .../src/request.ts | 2 + .../src/router.ts | 41 ++++++++++++++++++- .../versioned_router/core_versioned_route.ts | 4 +- .../src/http_server.ts | 3 +- .../src/http_service.ts | 8 +++- .../core-http-server-internal/src/types.ts | 2 + .../core-http-server/src/router/request.ts | 3 +- .../route_deprecations/route_deprecations.ts | 12 +++--- .../core-root-server-internal/src/server.ts | 16 ++++++-- 9 files changed, 75 insertions(+), 16 deletions(-) diff --git a/packages/core/http/core-http-router-server-internal/src/request.ts b/packages/core/http/core-http-router-server-internal/src/request.ts index b7b23186def88..1ffacba7c567b 100644 --- a/packages/core/http/core-http-router-server-internal/src/request.ts +++ b/packages/core/http/core-http-router-server-internal/src/request.ts @@ -254,6 +254,8 @@ export class CoreKibanaRequest< xsrfRequired: ((request.route?.settings as RouteOptions)?.app as KibanaRouteOptions)?.xsrfRequired ?? true, // some places in LP call KibanaRequest.from(request) manually. remove fallback to true before v8 + deprecated: ((request.route?.settings as RouteOptions)?.app as KibanaRouteOptions) + ?.deprecated, access: this.getAccess(request), tags: request.route?.settings?.tags || [], timeout: { diff --git a/packages/core/http/core-http-router-server-internal/src/router.ts b/packages/core/http/core-http-router-server-internal/src/router.ts index a6f2ccc35f56b..9b261bdcb44d2 100644 --- a/packages/core/http/core-http-router-server-internal/src/router.ts +++ b/packages/core/http/core-http-router-server-internal/src/router.ts @@ -7,6 +7,7 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ +import { EventEmitter } from 'node:events'; import type { Request, ResponseToolkit } from '@hapi/hapi'; import apm from 'elastic-apm-node'; import { isConfigSchema } from '@kbn/config-schema'; @@ -142,7 +143,13 @@ export interface RouterOptions { /** @internal */ export interface InternalRegistrarOptions { + /** @default false */ isVersioned: boolean; + /** + * Whether this route should emit "route events" like postValidate + * @default true + */ + events: boolean; } /** @internal */ @@ -162,12 +169,18 @@ interface InternalGetRoutesOptions { excludeVersionedRoutes?: boolean; } +/** @internal */ +type RouterEvents = + /** Just before registered handlers are called */ + 'onPostValidate'; + /** * @internal */ export class Router implements IRouter { + private static ee = new EventEmitter(); public routes: Array> = []; public pluginId?: symbol; public get: InternalRegistrar<'get', Context>; @@ -188,7 +201,10 @@ export class Router( route: RouteConfig, handler: RequestHandler, - internalOptions: { isVersioned: boolean } = { isVersioned: false } + internalOptions: InternalRegistrarOptions = { + isVersioned: false, + events: true, + } ) => { route = prepareRouteConfigValidation(route); const routeSchemas = routeSchemasFromRouteConfig(route, method); @@ -200,6 +216,9 @@ export class Router void) { + Router.ee.on(event, cb); + } + + public static off(event: RouterEvents, cb: (req: CoreKibanaRequest) => void) { + Router.ee.off(event, cb); + } + public getRoutes({ excludeVersionedRoutes }: InternalGetRoutesOptions = {}) { if (excludeVersionedRoutes) { return this.routes.filter((route) => !route.isVersioned); @@ -246,15 +273,25 @@ export class Router { + const postValidate: RouterEvents = 'onPostValidate'; + Router.ee.emit(postValidate, request); + }; + private async handle({ routeSchemas, request, responseToolkit, + emit, handler, }: { request: Request; responseToolkit: ResponseToolkit; handler: RequestHandlerEnhanced; + emit?: { + onPostValidation: (req: KibanaRequest) => void; + }; routeSchemas?: RouteValidator; }) { let kibanaRequest: KibanaRequest; @@ -266,6 +303,8 @@ export class Router { + Router.on('onPostValidate', cb); + }, + externalUrl: new ExternalUrlConfig(config.externalUrl), createRouter: ( @@ -208,7 +214,7 @@ export class HttpService ) => this.requestHandlerContext!.registerContext(pluginOpaqueId, contextName, provider), }; - return this.internalSetup; + return this.internalSetup!; } // this method exists because we need the start contract to create the `CoreStart` used to start diff --git a/packages/core/http/core-http-server-internal/src/types.ts b/packages/core/http/core-http-server-internal/src/types.ts index 70dde23f035d0..d90598e85cdf8 100644 --- a/packages/core/http/core-http-server-internal/src/types.ts +++ b/packages/core/http/core-http-server-internal/src/types.ts @@ -17,6 +17,7 @@ import type { HttpServiceSetup, HttpServiceStart, } from '@kbn/core-http-server'; +import { CoreKibanaRequest } from '@kbn/core-http-router-server-internal'; import type { HttpServerSetup } from './http_server'; import type { ExternalUrlConfig } from './external_url'; import type { InternalStaticAssets } from './static_assets'; @@ -65,6 +66,7 @@ export interface InternalHttpServiceSetup contextName: ContextName, provider: IContextProvider ) => IContextContainer; + registerOnPostValidation(cb: (req: CoreKibanaRequest) => void): void; } /** @internal */ diff --git a/packages/core/http/core-http-server/src/router/request.ts b/packages/core/http/core-http-server/src/router/request.ts index 9080c1be48c8c..3a9f3d695acd2 100644 --- a/packages/core/http/core-http-server/src/router/request.ts +++ b/packages/core/http/core-http-server/src/router/request.ts @@ -13,13 +13,14 @@ import type { Observable } from 'rxjs'; import type { RecursiveReadonly } from '@kbn/utility-types'; import type { HttpProtocol } from '../http_contract'; import type { IKibanaSocket } from './socket'; -import type { RouteMethod, RouteConfigOptions } from './route'; +import type { RouteMethod, RouteConfigOptions, RouteDeprecation } from './route'; import type { Headers } from './headers'; /** * @public */ export interface KibanaRouteOptions extends RouteOptionsApp { + deprecated?: RouteDeprecation; xsrfRequired: boolean; access: 'internal' | 'public'; } diff --git a/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts b/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts index 053d431dfb36e..b1e2fa1af9070 100644 --- a/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts +++ b/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts @@ -7,26 +7,25 @@ * License v3.0 only", or the "Server Side Public License, v 1". */ -import type { OnPostAuthHandler } from '@kbn/core-http-server'; import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-server-internal'; import type { Logger } from '@kbn/logging'; -import { buildDeprecations } from '@kbn/core-http-router-server-internal'; +import { CoreKibanaRequest, buildDeprecations } from '@kbn/core-http-router-server-internal'; interface Dependencies { - logRouteApiDeprecations: boolean; + logRouteApiDeprecations: boolean; // TODO(jloleysens) use this log: Logger; coreUsageData: InternalCoreUsageDataSetup; } -export function createRouteDeprecationsHandler({ coreUsageData }: Dependencies): OnPostAuthHandler { - return (req, res, toolkit) => { +export function createRouteDeprecationsHandler({ coreUsageData }: Dependencies) { + return (req: CoreKibanaRequest) => { const { route: { options: { deprecated: deprecatedInput }, }, } = req; - const messages = []; + const messages: string[] = []; if (typeof deprecatedInput === 'boolean' && deprecatedInput === true) { // Log route level deprecation messages.push(`${req.route.method} ${req.route.path} is deprecated.`); @@ -43,6 +42,5 @@ export function createRouteDeprecationsHandler({ coreUsageData }: Dependencies): for (const message of messages) { coreUsageData.incrementUsageCounter({ counterName: message }); // TODO(jloleysens) figure this part out } - return toolkit.next(); }; } diff --git a/packages/core/root/core-root-server-internal/src/server.ts b/packages/core/root/core-root-server-internal/src/server.ts index f04f182d3def3..37b15bccb12ff 100644 --- a/packages/core/root/core-root-server-internal/src/server.ts +++ b/packages/core/root/core-root-server-internal/src/server.ts @@ -58,6 +58,7 @@ import { registerServiceConfig } from './register_service_config'; import { MIGRATION_EXCEPTION_CODE } from './constants'; import { coreConfig, type CoreConfigType } from './core_config'; import { registerRootEvents, reportKibanaStartedEvent, type UptimeSteps } from './events'; +import { createRouteDeprecationsHandler } from './route_deprecations'; const coreId = Symbol('core'); @@ -98,6 +99,7 @@ export class Server { #pluginsInitialized?: boolean; private coreStart?: InternalCoreStart; + private coreSetup?: InternalCoreSetup; private discoveredPlugins?: DiscoveredPlugins; private readonly logger: LoggerFactory; private nodeRoles?: NodeRoles; @@ -372,6 +374,7 @@ export class Server { this.#pluginsInitialized = pluginsSetup.initialized; this.registerCoreContext(coreSetup); + this.registerApiRouteDeprecationsLogger(coreSetup); await this.coreApp.setup(coreSetup, uiPlugins); setupTransaction.end(); @@ -519,10 +522,15 @@ export class Server { return new CoreRouteHandlerContext(this.coreStart!, req); } ); - - coreSetup.http.registerOnPostAuth((req, res, toolkit) => { - return toolkit.next(); - }); + } + private registerApiRouteDeprecationsLogger(coreSetup: InternalCoreSetup) { + coreSetup.http.registerOnPostValidation( + createRouteDeprecationsHandler({ + coreUsageData: coreSetup.coreUsageData, + log: this.logger.get('http.route-deprecations'), + logRouteApiDeprecations: true, + }) + ); } public setupCoreConfig() { From adb25aeaf4097f36322aeb04b5204ac3d72ff605 Mon Sep 17 00:00:00 2001 From: Bamieh Date: Mon, 23 Sep 2024 09:31:31 +0300 Subject: [PATCH 11/12] progress so far --- .../core-deprecations-common/src/types.ts | 16 +++- .../README.md | 87 +++++++++++++++++++ .../src/deprecations_factory.ts | 5 +- .../src/deprecations_service.ts | 1 + .../src/routes/get.ts | 3 + .../src/http_server.ts | 24 ++++- .../src/http_service.ts | 6 +- .../core-http-server-internal/src/types.ts | 1 + .../core-http-server/src/http_contract.ts | 2 + .../http/core-http-server/src/router/route.ts | 4 +- .../route_deprecations/route_deprecations.ts | 4 +- .../core-root-server-internal/src/server.ts | 38 ++++++++ .../src/internal_contract.ts | 3 + .../kibana_deprecations_table.tsx | 8 ++ .../upgrade_assistant/server/plugin.ts | 8 ++ 15 files changed, 198 insertions(+), 12 deletions(-) diff --git a/packages/core/deprecations/core-deprecations-common/src/types.ts b/packages/core/deprecations/core-deprecations-common/src/types.ts index bf9a4a673d721..6effe7323e25f 100644 --- a/packages/core/deprecations/core-deprecations-common/src/types.ts +++ b/packages/core/deprecations/core-deprecations-common/src/types.ts @@ -39,7 +39,7 @@ export interface BaseDeprecationDetails { * Predefined types are necessary to reduce having similar definitions with different keywords * across kibana deprecations. */ - deprecationType?: 'config' | 'feature'; + deprecationType?: 'config' | 'feature' | 'api'; /** (optional) link to the documentation for more details on the deprecation. */ documentationUrl?: string; /** (optional) specify the fix for this deprecation requires a full kibana restart. */ @@ -91,7 +91,19 @@ export interface FeatureDeprecationDetails extends BaseDeprecationDetails { /** * @public */ -export type DeprecationsDetails = ConfigDeprecationDetails | FeatureDeprecationDetails; +export interface ApiDeprecationDetails extends BaseDeprecationDetails { + routePath: string; + routeMethod: string; + deprecationType: 'api'; +} + +/** + * @public + */ +export type DeprecationsDetails = + | ConfigDeprecationDetails + | FeatureDeprecationDetails + | ApiDeprecationDetails; /** * @public diff --git a/packages/core/deprecations/core-deprecations-server-internal/README.md b/packages/core/deprecations/core-deprecations-server-internal/README.md index 8257dfb40ebbf..01b5b86014b47 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/README.md +++ b/packages/core/deprecations/core-deprecations-server-internal/README.md @@ -1,3 +1,90 @@ # @kbn/core-deprecations-server-internal This package contains the internal types and implementation of Core's server-side `deprecations` service. + + +/** Router: + * register Usage counters side core setup + * DOMIANID: CORE.ROUTER + * At router definition + * Call deprecations.registerDeprecation + * - group all renamed etc + + * GroupId: Method / path + * when route is called + if deprecation is triggered based on rename/ path/ remove + - increment counter: GroupId, domainId, type: 'count' + + +set: ['body.a'], +unset: ['body.a'], + +{ + "deprecations": [ + { + "configPath": "xpack.reporting.roles.enabled", + "title": "The \"xpack.reporting.roles\" setting is deprecated", + "level": "warning", + "message": "The default mechanism for Reporting privileges will work differently in future versions, which will affect the behavior of this cluster. Set \"xpack.reporting.roles.enabled\" to \"false\" to adopt the future behavior before upgrading.", + "correctiveActions": { + "manualSteps": [ + "Set \"xpack.reporting.roles.enabled\" to \"false\" in kibana.yml.", + "Remove \"xpack.reporting.roles.allow\" in kibana.yml, if present.", + "Go to Management > Security > Roles to create one or more roles that grant the Kibana application privilege for Reporting.", + "Grant Reporting privileges to users by assigning one of the new roles." + ], + api: { + path: 'some-path', + method: 'POST', + body: { + extra_param: 123, + }, + }, + }, + "deprecationType": "config", + "requireRestart": true, + "domainId": "xpack.reporting" + } + ] +} + + +domainId: 'routesDeprecations' +counterName: '{RouteAPIGroupingID}', +counterType: 'count', + +RouteAPIGroupingID +If Route level: method, route + +For fixed: +counterType: 'count:fixed', + +We count all deprecations + +{ + 'RouteAPIGroupingID': { + message: '', + deprecationDetails + } +} + + +Approach 1: +In memory: +1. Store in memory the deprecation details defined at routes setup + +3. enrich some text (last called etc) filter out diff 0 +4. send result + +SO and get rid of Usage counter + +interface UsageCountersParams { + /** The domainId used to create the Counter API */ + domainId: string; + /** The name of the counter. Optional, will return all counters in the same domainId that match the rest of filters if omitted */ + counterName?: string; + /** The 2. on UA api call we do matching between usage counters and the deprecation details in memorytype of counter. Optional, will return all counters in the same domainId that match the rest of filters if omitted */ + counterType?: string; + /** Namespace of the counter. Optional, counters of the 'default' namespace will be returned if omitted */ + namespace?: string; +} diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_factory.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_factory.ts index f6f5ebb0bfb81..132a7c24914a7 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_factory.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_factory.ts @@ -62,7 +62,10 @@ export class DeprecationsFactory { }) ); - return deprecationsInfo.flat(); + return [ + ...deprecationsInfo.flat(), + // ...apiDeprecations, + ]; }; private createDeprecationInfo = ( diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.ts index 4c8f564943ab1..3cb5481121d78 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.ts @@ -87,6 +87,7 @@ export class DeprecationsService if (!this.deprecationsFactory) { throw new Error('`setup` must be called before `start`'); } + return { asScopedToClient: this.createScopedDeprecations(), }; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/routes/get.ts b/packages/core/deprecations/core-deprecations-server-internal/src/routes/get.ts index ed3cd061b633b..8f24fa035e20b 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/routes/get.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/routes/get.ts @@ -15,6 +15,9 @@ export const registerGetRoute = (router: InternalDeprecationRouter) => { { path: '/', validate: false, + options: { + deprecated: true, + }, }, async (context, req, res) => { const deprecationsClient = (await context.core).deprecations.client; diff --git a/packages/core/http/core-http-server-internal/src/http_server.ts b/packages/core/http/core-http-server-internal/src/http_server.ts index 1df68f87c663e..5c58a195810dd 100644 --- a/packages/core/http/core-http-server-internal/src/http_server.ts +++ b/packages/core/http/core-http-server-internal/src/http_server.ts @@ -39,7 +39,7 @@ import type { } from '@kbn/core-http-server'; import { performance } from 'perf_hooks'; import { isBoom } from '@hapi/boom'; -import { identity } from 'lodash'; +import { identity, isFunction } from 'lodash'; import { IHttpEluMonitorConfig } from '@kbn/core-http-server/src/elu_monitor'; import { Env } from '@kbn/config'; import { CoreContext } from '@kbn/core-base-server-internal'; @@ -144,6 +144,8 @@ export interface HttpServerSetup { authRequestHeaders: IAuthHeadersStorage; auth: HttpAuth; getServerInfo: () => HttpServerInfo; + + getDeprecatedRoutes: HttpServiceSetup['getDeprecatedRoutes']; } /** @internal */ @@ -202,6 +204,25 @@ export class HttpServer { return this.server !== undefined && this.server.listener.listening; } + private getDeprecatedRoutes() { + const deprecatedRoutes: any[] = []; + + for (const router of this.registeredRouters) { + for (const route of router.getRoutes()) { + if (route.options.deprecated === true || isFunction(route.options.deprecated)) { + deprecatedRoutes.push({ + basePath: router.routerPath, + routePath: route.path, + routeOptions: route.options, + routeMethod: route.method, + }); + } + } + } + + return deprecatedRoutes; + } + private registerRouter(router: IRouter) { if (this.isListening()) { throw new Error('Routers can be registered only when HTTP server is stopped.'); @@ -281,6 +302,7 @@ export class HttpServer { return { registerRouter: this.registerRouter.bind(this), + getDeprecatedRoutes: this.getDeprecatedRoutes.bind(this), registerRouterAfterListening: this.registerRouterAfterListening.bind(this), registerStaticDir: this.registerStaticDir.bind(this), staticAssets, diff --git a/packages/core/http/core-http-server-internal/src/http_service.ts b/packages/core/http/core-http-server-internal/src/http_service.ts index 81c4d7352fbb2..2861652976fa3 100644 --- a/packages/core/http/core-http-server-internal/src/http_service.ts +++ b/packages/core/http/core-http-server-internal/src/http_service.ts @@ -10,8 +10,6 @@ import { Observable, Subscription, combineLatest, firstValueFrom, of, mergeMap } from 'rxjs'; import { map } from 'rxjs'; -import EventEmitter from 'node:events'; - import { pick, Semaphore } from '@kbn/std'; import { generateOpenApiDocument } from '@kbn/router-to-openapispec'; import { Logger } from '@kbn/logging'; @@ -188,6 +186,10 @@ export class HttpService Router.on('onPostValidate', cb); }, + getRegisteredDeprecatedApis: () => { + return serverContract.getDeprecatedRoutes(); + }, + externalUrl: new ExternalUrlConfig(config.externalUrl), createRouter: ( diff --git a/packages/core/http/core-http-server-internal/src/types.ts b/packages/core/http/core-http-server-internal/src/types.ts index d90598e85cdf8..bd4160d4d4b67 100644 --- a/packages/core/http/core-http-server-internal/src/types.ts +++ b/packages/core/http/core-http-server-internal/src/types.ts @@ -67,6 +67,7 @@ export interface InternalHttpServiceSetup provider: IContextProvider ) => IContextContainer; registerOnPostValidation(cb: (req: CoreKibanaRequest) => void): void; + getRegisteredDeprecatedApis: () => any[]; } /** @internal */ diff --git a/packages/core/http/core-http-server/src/http_contract.ts b/packages/core/http/core-http-server/src/http_contract.ts index 72eb70149f529..81871b502bee0 100644 --- a/packages/core/http/core-http-server/src/http_contract.ts +++ b/packages/core/http/core-http-server/src/http_contract.ts @@ -359,6 +359,8 @@ export interface HttpServiceSetup< * Provides common {@link HttpServerInfo | information} about the running http server. */ getServerInfo: () => HttpServerInfo; + + getDeprecatedRoutes: () => any[]; } /** @public */ diff --git a/packages/core/http/core-http-server/src/router/route.ts b/packages/core/http/core-http-server/src/router/route.ts index 4b9bdfb5e9891..5de6d590880e6 100644 --- a/packages/core/http/core-http-server/src/router/route.ts +++ b/packages/core/http/core-http-server/src/router/route.ts @@ -159,8 +159,6 @@ export interface RouteInputDeprecation { * of the Elastic stack (via Upgrade Assistant) and will be surfaced in documentation * created from HTTP API introspection (like OAS). * - * string - Provide a string to mark this route as deprecated along with a description like: - * "This route is deprecated and staged for removal by X.X.X. Use /another/cool/route instead" * boolean - Set this to `true` to specify that this entire route is deprecated. * * It's also possible to provide deprecation messages about sub-parts of the route. Consider this @@ -179,7 +177,7 @@ export interface RouteInputDeprecation { * @default false * @public */ -export type RouteDeprecation = boolean | string | RouteInputDeprecation; +export type RouteDeprecation = boolean | RouteInputDeprecation; /** * Additional route options. diff --git a/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts b/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts index b1e2fa1af9070..9582cb1678d14 100644 --- a/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts +++ b/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts @@ -29,9 +29,6 @@ export function createRouteDeprecationsHandler({ coreUsageData }: Dependencies) if (typeof deprecatedInput === 'boolean' && deprecatedInput === true) { // Log route level deprecation messages.push(`${req.route.method} ${req.route.path} is deprecated.`); - } else if (typeof deprecatedInput === 'string') { - // Log route level deprecation + message - messages.push(deprecatedInput); } else if (typeof deprecatedInput === 'object') { // Log route input level deprecation + message const deprecations = buildDeprecations(deprecatedInput); @@ -39,6 +36,7 @@ export function createRouteDeprecationsHandler({ coreUsageData }: Dependencies) if (check(req[location])) messages.push(`${req.route.method} ${req.route.path} ${message}`); } } + for (const message of messages) { coreUsageData.incrementUsageCounter({ counterName: message }); // TODO(jloleysens) figure this part out } diff --git a/packages/core/root/core-root-server-internal/src/server.ts b/packages/core/root/core-root-server-internal/src/server.ts index c91524502f221..71deaf1107558 100644 --- a/packages/core/root/core-root-server-internal/src/server.ts +++ b/packages/core/root/core-root-server-internal/src/server.ts @@ -534,6 +534,44 @@ export class Server { ); } private registerApiRouteDeprecationsLogger(coreSetup: InternalCoreSetup) { + const deprecationsRegistery = coreSetup.deprecations.getRegistry('core.http'); + deprecationsRegistery.registerDeprecations({ + getDeprecations: async () => { + const usageClient = coreSetup.coreUsageData.getClient(); + const deprecatedRoutes = coreSetup.http.getDeprecatedRoutes(); + const deprecatedStats = await usageClient.getDeprecatedApisStats(); + + return [ + { + routePath: '/api/chocolate_love', + routeMethod: 'GET', + title: `The Route "[GET] /api/chocolate_love" has deprected params`, + level: 'warning', + message: `Deprecated route [GET] /api/chocolate_love was called 34 times with deprecated params.\n + The last time the deprecation was triggered was on Fri Sep 20 2024 14:28:22.\n + This deprecation was previously marked as resolved but was called 3 times since it was marked on Fri Sep 13 2024 10:28:22.`, + documentationUrl: 'https://google.com', + correctiveActions: { + manualSteps: [ + 'The following query params are deprecated: dont_use,my_old_query_param', + 'Make sure you are not using any of these parameters when calling the API', + ], + api: { + path: 'some-path', + method: 'POST', + body: { + extra_param: 123, + }, + }, + }, + deprecationType: 'api', + requireRestart: false, + domainId: 'core.router', + }, + ]; + }, + }); + coreSetup.http.registerOnPostValidation( createRouteDeprecationsHandler({ coreUsageData: coreSetup.coreUsageData, diff --git a/packages/core/usage-data/core-usage-data-base-server-internal/src/internal_contract.ts b/packages/core/usage-data/core-usage-data-base-server-internal/src/internal_contract.ts index 4ed197817c277..17cea797156e2 100644 --- a/packages/core/usage-data/core-usage-data-base-server-internal/src/internal_contract.ts +++ b/packages/core/usage-data/core-usage-data-base-server-internal/src/internal_contract.ts @@ -23,4 +23,7 @@ export interface InternalCoreUsageDataSetup extends CoreUsageDataSetup { /** @internal {@link CoreIncrementUsageCounter} **/ incrementUsageCounter: CoreIncrementUsageCounter; + + /** @internal {@link CoreIncrementUsageCounter} **/ + incrementDeprecatedApiUsageCounter: CoreIncrementUsageCounter; } diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations_table.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations_table.tsx index 6a757d0cb2b0b..b115a8cf2e24b 100644 --- a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations_table.tsx +++ b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations_table.tsx @@ -56,6 +56,12 @@ const i18nTexts = { defaultMessage: 'Feature', } ), + routeDeprecationTypeCellLabel: i18n.translate( + 'xpack.upgradeAssistant.kibanaDeprecations.table.routeDeprecationTypeCellLabel', + { + defaultMessage: 'Route', + } + ), unknownDeprecationTypeCellLabel: i18n.translate( 'xpack.upgradeAssistant.kibanaDeprecations.table.unknownDeprecationTypeCellLabel', { @@ -135,6 +141,8 @@ export const KibanaDeprecationsTable: React.FunctionComponent = ({ return i18nTexts.configDeprecationTypeCellLabel; case 'feature': return i18nTexts.featureDeprecationTypeCellLabel; + case 'route': + return i18nTexts.routeDeprecationTypeCellLabel; case 'uncategorized': default: return i18nTexts.unknownDeprecationTypeCellLabel; diff --git a/x-pack/plugins/upgrade_assistant/server/plugin.ts b/x-pack/plugins/upgrade_assistant/server/plugin.ts index 3df9f7deced9d..7610e6f950680 100644 --- a/x-pack/plugins/upgrade_assistant/server/plugin.ts +++ b/x-pack/plugins/upgrade_assistant/server/plugin.ts @@ -103,6 +103,14 @@ export class UpgradeAssistantServerPlugin implements Plugin { ], }); + http.registerOnPreResponse(async (req, res, t) => { + if (req.route.options.deprecated) { + // console.log('route!') + } + + return t.next(); + }); + // We need to initialize the deprecation logs plugin so that we can // navigate from this app to the observability app using a source_id. logsShared?.logViews.defineInternalLogView(DEPRECATION_LOGS_SOURCE_ID, { From 5de5824ffd13f092877b79c885b5506a7ac1831b Mon Sep 17 00:00:00 2001 From: Bamieh Date: Wed, 25 Sep 2024 14:28:02 +0300 Subject: [PATCH 12/12] lets take a look --- .../src/deprecations_service.ts | 55 ++++----- .../src/register_core_deprecations.ts | 114 ++++++++++++++++++ .../src/routes/get.ts | 10 +- .../core-http-router-server-internal/index.ts | 1 - .../src/deprecations.ts | 66 ---------- .../src/http_server.ts | 5 +- .../http/core-http-server/src/router/route.ts | 30 +++-- .../route_deprecations/route_deprecations.ts | 24 ++-- .../core-root-server-internal/src/server.ts | 48 +------- .../index.ts | 1 + .../src/usage_stats_client.ts | 2 + .../src/core_usage_data_service.ts | 18 +++ .../src/core_usage_stats_client.ts | 24 ++++ 13 files changed, 232 insertions(+), 166 deletions(-) create mode 100644 packages/core/deprecations/core-deprecations-server-internal/src/register_core_deprecations.ts delete mode 100644 packages/core/http/core-http-router-server-internal/src/deprecations.ts diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.ts b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.ts index 3cb5481121d78..f4ade4b6c2d28 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/deprecations_service.ts @@ -19,9 +19,14 @@ import type { DeprecationRegistryProvider, DeprecationsClient, } from '@kbn/core-deprecations-server'; +import { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; import { DeprecationsFactory } from './deprecations_factory'; import { registerRoutes } from './routes'; import { config as deprecationConfig, DeprecationConfigType } from './deprecation_config'; +import { + registerApiDeprecationsInfo, + registerConfigDeprecationsInfo, +} from './register_core_deprecations'; export interface InternalDeprecationsServiceStart { /** @@ -40,6 +45,7 @@ export type InternalDeprecationsServiceSetup = DeprecationRegistryProvider; /** @internal */ export interface DeprecationsSetupDeps { http: InternalHttpServiceSetup; + coreUsageData: InternalCoreUsageDataSetup; } /** @internal */ @@ -55,7 +61,10 @@ export class DeprecationsService this.configService = coreContext.configService; } - public async setup({ http }: DeprecationsSetupDeps): Promise { + public async setup({ + http, + coreUsageData, + }: DeprecationsSetupDeps): Promise { this.logger.debug('Setting up Deprecations service'); const config = await firstValueFrom( @@ -70,7 +79,18 @@ export class DeprecationsService }); registerRoutes({ http }); - this.registerConfigDeprecationsInfo(this.deprecationsFactory); + + registerConfigDeprecationsInfo({ + deprecationsFactory: this.deprecationsFactory, + configService: this.configService, + }); + + registerApiDeprecationsInfo({ + deprecationsFactory: this.deprecationsFactory, + logger: this.logger, + http, + coreUsageData, + }); const deprecationsFactory = this.deprecationsFactory; return { @@ -108,35 +128,4 @@ export class DeprecationsService }; }; } - - private registerConfigDeprecationsInfo(deprecationsFactory: DeprecationsFactory) { - const handledDeprecatedConfigs = this.configService.getHandledDeprecatedConfigs(); - - for (const [domainId, deprecationsContexts] of handledDeprecatedConfigs) { - const deprecationsRegistry = deprecationsFactory.getRegistry(domainId); - deprecationsRegistry.registerDeprecations({ - getDeprecations: () => { - return deprecationsContexts.map( - ({ - configPath, - title = `${domainId} has a deprecated setting`, - level, - message, - correctiveActions, - documentationUrl, - }) => ({ - configPath, - title, - level, - message, - correctiveActions, - documentationUrl, - deprecationType: 'config', - requireRestart: true, - }) - ); - }, - }); - } - } } diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/register_core_deprecations.ts b/packages/core/deprecations/core-deprecations-server-internal/src/register_core_deprecations.ts new file mode 100644 index 0000000000000..6ef69bec45dfb --- /dev/null +++ b/packages/core/deprecations/core-deprecations-server-internal/src/register_core_deprecations.ts @@ -0,0 +1,114 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the "Elastic License + * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side + * Public License v 1"; you may not use this file except in compliance with, at + * your election, the "Elastic License 2.0", the "GNU Affero General Public + * License v3.0 only", or the "Server Side Public License, v 1". + */ + +import type { IConfigService } from '@kbn/config'; +import type { Logger } from '@kbn/logging'; +import { InternalHttpServiceSetup } from '@kbn/core-http-server-internal'; +import { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal'; +import { DeprecationsFactory } from './deprecations_factory'; + +interface RegisterConfigDeprecationsInfo { + deprecationsFactory: DeprecationsFactory; + configService: IConfigService; +} + +export const registerConfigDeprecationsInfo = ({ + deprecationsFactory, + configService, +}: RegisterConfigDeprecationsInfo) => { + const handledDeprecatedConfigs = configService.getHandledDeprecatedConfigs(); + + for (const [domainId, deprecationsContexts] of handledDeprecatedConfigs) { + const deprecationsRegistry = deprecationsFactory.getRegistry(domainId); + deprecationsRegistry.registerDeprecations({ + getDeprecations: () => { + return deprecationsContexts.map( + ({ + configPath, + title = `${domainId} has a deprecated setting`, + level, + message, + correctiveActions, + documentationUrl, + }) => ({ + configPath, + title, + level, + message, + correctiveActions, + documentationUrl, + deprecationType: 'config', + requireRestart: true, + }) + ); + }, + }); + } +}; + +export interface ApiDeprecationsServiceDeps { + logger: Logger; + deprecationsFactory: DeprecationsFactory; + http: InternalHttpServiceSetup; + coreUsageData: InternalCoreUsageDataSetup; +} + +export const registerApiDeprecationsInfo = ({ + deprecationsFactory, + http, + coreUsageData, +}: ApiDeprecationsServiceDeps): void => { + const deprecationsRegistery = deprecationsFactory.getRegistry('core.api_deprecations'); + + deprecationsRegistery.registerDeprecations({ + getDeprecations: async () => { + console.log('calling get!!'); + + const usageClient = coreUsageData.getClient(); + const deprecatedRoutes = http.getDeprecatedRoutes(); + const deprecatedStats = await usageClient.getDeprecatedApisStats(); + + console.log('deprecatedRoutes::', deprecatedRoutes); + console.log('deprecatedStats::', deprecatedStats); + + // Do the matching here + // Do the diff here + // write the messages here + + return [ + { + routePath: '/api/chocolate_love', + routeMethod: 'GET', + title: `The Route "[GET] /api/chocolate_love" has deprected params`, + level: 'warning', + message: `Deprecated route [GET] /api/chocolate_love was called 34 times with deprecated params.\n + The last time the deprecation was triggered was on Fri Sep 20 2024 14:28:22.\n + This deprecation was previously marked as resolved but was called 3 times since it was marked on Fri Sep 13 2024 10:28:22.`, + documentationUrl: 'https://google.com', + correctiveActions: { + manualSteps: [ + 'The following query params are deprecated: dont_use,my_old_query_param', + 'Make sure you are not using any of these parameters when calling the API', + ], + api: { + path: 'some-path', + method: 'POST', + body: { + extra_param: 123, + }, + }, + }, + deprecationType: 'api', + requireRestart: false, + domainId: 'core.router', + }, + ]; + }, + }); +}; diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/routes/get.ts b/packages/core/deprecations/core-deprecations-server-internal/src/routes/get.ts index 8f24fa035e20b..e987eb9e5e82b 100644 --- a/packages/core/deprecations/core-deprecations-server-internal/src/routes/get.ts +++ b/packages/core/deprecations/core-deprecations-server-internal/src/routes/get.ts @@ -16,7 +16,15 @@ export const registerGetRoute = (router: InternalDeprecationRouter) => { path: '/', validate: false, options: { - deprecated: true, + deprecated: { + guideLink: 'https://google.com', + severity: 'warning', + reason: { + type: 'migrated-api', + apiMethod: 'GET', + apiPath: '/api/core/deprecations', + }, + }, }, }, async (context, req, res) => { diff --git a/packages/core/http/core-http-router-server-internal/index.ts b/packages/core/http/core-http-router-server-internal/index.ts index c763932bf51da..6c684d5f8169c 100644 --- a/packages/core/http/core-http-router-server-internal/index.ts +++ b/packages/core/http/core-http-router-server-internal/index.ts @@ -27,4 +27,3 @@ export { isKibanaRequest, isRealRequest, ensureRawRequest, CoreKibanaRequest } f export { isSafeMethod } from './src/route'; export { HapiResponseAdapter } from './src/response_adapter'; export { kibanaResponseFactory, lifecycleResponseFactory, KibanaResponse } from './src/response'; -export { buildDeprecations } from './src/deprecations'; diff --git a/packages/core/http/core-http-router-server-internal/src/deprecations.ts b/packages/core/http/core-http-router-server-internal/src/deprecations.ts deleted file mode 100644 index 29378836bac04..0000000000000 --- a/packages/core/http/core-http-router-server-internal/src/deprecations.ts +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the "Elastic License - * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side - * Public License v 1"; you may not use this file except in compliance with, at - * your election, the "Elastic License 2.0", the "GNU Affero General Public - * License v3.0 only", or the "Server Side Public License, v 1". - */ - -import { - RouteInputDeprecation, - RouteInputDeprecationLocation, - RouteInputDeprecationDescription, -} from '@kbn/core-http-server'; -import { get } from 'lodash'; - -interface BuilderCommonArgs { - location: RouteInputDeprecationLocation; -} - -export type RouteInputDeprecationInternalDescription = RouteInputDeprecationDescription & { - message: string; - check: (v: unknown) => boolean; -}; - -export const buildRename = - ({ location }: BuilderCommonArgs) => - (oldPath: string, newPath: string) => ({ - type: 'renamed' as const, - location, - message: `"${oldPath}" has been removed. Use "${newPath}" instead.`, - new: newPath, - old: oldPath, - check: (input: unknown) => Boolean(get(input, oldPath)), - }); - -export const buildRemove = - ({ location }: BuilderCommonArgs) => - (path: string) => ({ - type: 'removed' as const, - location, - message: `"${path}" has been removed.`, - path, - check: (input: unknown) => Boolean(get(input, path)), - }); - -export function buildDeprecations({ - body: bodyFactory, - query: queryFactory, -}: RouteInputDeprecation): RouteInputDeprecationInternalDescription[] { - const deprecations: RouteInputDeprecationInternalDescription[] = []; - for (const [factory, location] of [ - [bodyFactory, 'body'], - [queryFactory, 'query'], - ] as const) { - if (factory) { - deprecations.push( - ...(factory({ - rename: buildRename({ location }), - remove: buildRemove({ location }), - }) as RouteInputDeprecationInternalDescription[]) - ); - } - } - return deprecations; -} diff --git a/packages/core/http/core-http-server-internal/src/http_server.ts b/packages/core/http/core-http-server-internal/src/http_server.ts index 5c58a195810dd..268778eb89aa0 100644 --- a/packages/core/http/core-http-server-internal/src/http_server.ts +++ b/packages/core/http/core-http-server-internal/src/http_server.ts @@ -39,7 +39,7 @@ import type { } from '@kbn/core-http-server'; import { performance } from 'perf_hooks'; import { isBoom } from '@hapi/boom'; -import { identity, isFunction } from 'lodash'; +import { identity, isObject } from 'lodash'; import { IHttpEluMonitorConfig } from '@kbn/core-http-server/src/elu_monitor'; import { Env } from '@kbn/config'; import { CoreContext } from '@kbn/core-base-server-internal'; @@ -209,9 +209,10 @@ export class HttpServer { for (const router of this.registeredRouters) { for (const route of router.getRoutes()) { - if (route.options.deprecated === true || isFunction(route.options.deprecated)) { + if (isObject(route.options.deprecated)) { deprecatedRoutes.push({ basePath: router.routerPath, + routeVerion: 'v1', // TODO(Bamieh): ADD this to route routePath: route.path, routeOptions: route.options, routeMethod: route.method, diff --git a/packages/core/http/core-http-server/src/router/route.ts b/packages/core/http/core-http-server/src/router/route.ts index 5de6d590880e6..840465d39a63e 100644 --- a/packages/core/http/core-http-server/src/router/route.ts +++ b/packages/core/http/core-http-server/src/router/route.ts @@ -143,15 +143,24 @@ export type RouteInputDeprecationFactory = (factories: { rename(oldPath: string, newPath: string): RouteInputDeprecationRenamedDescription; }) => RouteInputDeprecationDescription[]; -/** - * Declare route input deprecations. - * @public - */ -export interface RouteInputDeprecation { - query?: RouteInputDeprecationFactory; - body?: RouteInputDeprecationFactory; +interface NewRouteDeprecationType { + type: 'new-version'; + apiVersion: string; +} +interface RemovedApiDeprecationType { + type: 'removed'; +} +interface MigratedApiDeprecationType { + type: 'migrated-api'; + apiPath: string; + apiMethod: string; } +export type RouteInputDeprecationReason = + | NewRouteDeprecationType + | RemovedApiDeprecationType + | MigratedApiDeprecationType; + /** * Description of deprecations for this HTTP API. * @@ -177,7 +186,12 @@ export interface RouteInputDeprecation { * @default false * @public */ -export type RouteDeprecation = boolean | RouteInputDeprecation; +export interface RouteDeprecation { + severity: 'warning' | 'critical'; + /** new version is only for versioned router, removed is a special case */ + reason: RouteInputDeprecationReason; + guideLink: string; +} /** * Additional route options. diff --git a/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts b/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts index 9582cb1678d14..fdd28a11dc907 100644 --- a/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts +++ b/packages/core/root/core-root-server-internal/src/route_deprecations/route_deprecations.ts @@ -10,7 +10,8 @@ import type { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-server-internal'; import type { Logger } from '@kbn/logging'; -import { CoreKibanaRequest, buildDeprecations } from '@kbn/core-http-router-server-internal'; +import { CoreKibanaRequest } from '@kbn/core-http-router-server-internal'; +import { isObject } from 'lodash'; interface Dependencies { logRouteApiDeprecations: boolean; // TODO(jloleysens) use this @@ -25,20 +26,17 @@ export function createRouteDeprecationsHandler({ coreUsageData }: Dependencies) options: { deprecated: deprecatedInput }, }, } = req; - const messages: string[] = []; - if (typeof deprecatedInput === 'boolean' && deprecatedInput === true) { - // Log route level deprecation - messages.push(`${req.route.method} ${req.route.path} is deprecated.`); - } else if (typeof deprecatedInput === 'object') { - // Log route input level deprecation + message - const deprecations = buildDeprecations(deprecatedInput); - for (const { check, message, location } of deprecations) { - if (check(req[location])) messages.push(`${req.route.method} ${req.route.path} ${message}`); - } + + if (typeof deprecatedInput === 'boolean') { + console.log('INVALID DEPRECATION INPUT!!! GOT BOOLEAN'); } - for (const message of messages) { - coreUsageData.incrementUsageCounter({ counterName: message }); // TODO(jloleysens) figure this part out + if (isObject(deprecatedInput)) { + const routeVersion = 'v1'; + const counterName = `[${routeVersion}][${req.route.method}] ${req.route.path}`; + + console.log(`Incrementing deprecated route: ${req.route.path}`); + coreUsageData.incrementDeprecatedApiUsageCounter({ counterName }); } }; } diff --git a/packages/core/root/core-root-server-internal/src/server.ts b/packages/core/root/core-root-server-internal/src/server.ts index 71deaf1107558..6230b349b7b0f 100644 --- a/packages/core/root/core-root-server-internal/src/server.ts +++ b/packages/core/root/core-root-server-internal/src/server.ts @@ -278,10 +278,6 @@ export class Server { executionContext: executionContextSetup, }); - const deprecationsSetup = await this.deprecations.setup({ - http: httpSetup, - }); - // setup i18n prior to any other service, to have translations ready const i18nServiceSetup = await this.i18n.setup({ http: httpSetup, pluginPaths }); @@ -305,6 +301,11 @@ export class Server { changedDeprecatedConfigPath$: this.configService.getDeprecatedConfigPath$(), }); + const deprecationsSetup = await this.deprecations.setup({ + http: httpSetup, + coreUsageData: coreUsageDataSetup, + }); + const savedObjectsSetup = await this.savedObjects.setup({ http: httpSetup, elasticsearch: elasticsearchServiceSetup, @@ -381,6 +382,7 @@ export class Server { this.registerCoreContext(coreSetup); this.registerApiRouteDeprecationsLogger(coreSetup); + await this.coreApp.setup(coreSetup, uiPlugins); setupTransaction.end(); @@ -534,44 +536,6 @@ export class Server { ); } private registerApiRouteDeprecationsLogger(coreSetup: InternalCoreSetup) { - const deprecationsRegistery = coreSetup.deprecations.getRegistry('core.http'); - deprecationsRegistery.registerDeprecations({ - getDeprecations: async () => { - const usageClient = coreSetup.coreUsageData.getClient(); - const deprecatedRoutes = coreSetup.http.getDeprecatedRoutes(); - const deprecatedStats = await usageClient.getDeprecatedApisStats(); - - return [ - { - routePath: '/api/chocolate_love', - routeMethod: 'GET', - title: `The Route "[GET] /api/chocolate_love" has deprected params`, - level: 'warning', - message: `Deprecated route [GET] /api/chocolate_love was called 34 times with deprecated params.\n - The last time the deprecation was triggered was on Fri Sep 20 2024 14:28:22.\n - This deprecation was previously marked as resolved but was called 3 times since it was marked on Fri Sep 13 2024 10:28:22.`, - documentationUrl: 'https://google.com', - correctiveActions: { - manualSteps: [ - 'The following query params are deprecated: dont_use,my_old_query_param', - 'Make sure you are not using any of these parameters when calling the API', - ], - api: { - path: 'some-path', - method: 'POST', - body: { - extra_param: 123, - }, - }, - }, - deprecationType: 'api', - requireRestart: false, - domainId: 'core.router', - }, - ]; - }, - }); - coreSetup.http.registerOnPostValidation( createRouteDeprecationsHandler({ coreUsageData: coreSetup.coreUsageData, diff --git a/packages/core/usage-data/core-usage-data-base-server-internal/index.ts b/packages/core/usage-data/core-usage-data-base-server-internal/index.ts index aad2883b016a6..3e3651ad9e1c7 100644 --- a/packages/core/usage-data/core-usage-data-base-server-internal/index.ts +++ b/packages/core/usage-data/core-usage-data-base-server-internal/index.ts @@ -14,6 +14,7 @@ export { } from './src'; export type { InternalCoreUsageDataSetup, + IDeprecationsStatsClient, ICoreUsageStatsClient, BaseIncrementOptions, IncrementSavedObjectsImportOptions, diff --git a/packages/core/usage-data/core-usage-data-base-server-internal/src/usage_stats_client.ts b/packages/core/usage-data/core-usage-data-base-server-internal/src/usage_stats_client.ts index 649e972af2abc..106d450dc234e 100644 --- a/packages/core/usage-data/core-usage-data-base-server-internal/src/usage_stats_client.ts +++ b/packages/core/usage-data/core-usage-data-base-server-internal/src/usage_stats_client.ts @@ -38,6 +38,8 @@ export type IncrementSavedObjectsExportOptions = BaseIncrementOptions & { export interface ICoreUsageStatsClient { getUsageStats(): Promise; + getDeprecatedApisStats(): Promise; + incrementSavedObjectsBulkCreate(options: BaseIncrementOptions): Promise; incrementSavedObjectsBulkGet(options: BaseIncrementOptions): Promise; diff --git a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts index 2a10e06567d02..bcbdd3756a531 100644 --- a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts +++ b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_data_service.ts @@ -88,6 +88,7 @@ export class CoreUsageDataService private coreUsageStatsClient?: CoreUsageStatsClient; private deprecatedConfigPaths: ChangedDeprecatedPaths = { set: [], unset: [] }; private incrementUsageCounter: CoreIncrementUsageCounter = () => {}; // Initially set to noop + private incrementDeprecatedApiUsageCounter: CoreIncrementUsageCounter = () => {}; // Initially set to noop constructor(core: CoreContext) { this.logger = core.logger.get('core-usage-stats-service'); @@ -502,6 +503,9 @@ export class CoreUsageDataService const registerUsageCounter = (usageCounter: CoreUsageCounter) => { this.incrementUsageCounter = (params) => usageCounter.incrementCounter(params); }; + const registerDeprecatedApiUsageCounter = (usageCounter: CoreUsageCounter) => { + this.incrementDeprecatedApiUsageCounter = (params) => usageCounter.incrementCounter(params); + }; const incrementUsageCounter = (params: CoreIncrementCounterParams) => { try { @@ -513,6 +517,16 @@ export class CoreUsageDataService } }; + const incrementDeprecatedApiUsageCounter = (params: CoreIncrementCounterParams) => { + try { + this.incrementDeprecatedApiUsageCounter(params); + } catch (e) { + // Self-defense mechanism since the handler is externally registered + this.logger.debug('Failed to increase the usage counter'); + this.logger.debug(e); + } + }; + this.coreUsageStatsClient = new CoreUsageStatsClient({ debugLogger: (message: string) => this.logger.debug(message), basePath: http.basePath, @@ -524,8 +538,12 @@ export class CoreUsageDataService const contract: InternalCoreUsageDataSetup = { registerType, getClient: () => this.coreUsageStatsClient!, + // Core usage stats registerUsageCounter, incrementUsageCounter, + // api deprecations + registerDeprecatedApiUsageCounter, + incrementDeprecatedApiUsageCounter, }; return contract; diff --git a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.ts b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.ts index 67ab6d9b30c9c..f25b3fd7eb640 100644 --- a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.ts +++ b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.ts @@ -215,6 +215,30 @@ export class CoreUsageStatsClient implements ICoreUsageStatsClient { return coreUsageStats; } + public async getDeprecatedApisStats(): Promise { + this.debugLogger('getDeprecatedApisStats() called'); + const coreUsageStats: any[] = []; + try { + const repository = await this.repositoryPromise; + this.flush$.next(); + // const result = await repository.incrementCounter( + // CORE_USAGE_STATS_TYPE, + // CORE_USAGE_STATS_ID, + // ALL_COUNTER_FIELDS, + // { initialize: true } // set all counter fields to 0 if they don't exist + // ); + coreUsageStats.push({ + counterName: '[v1][GET] /api/chocolate_love', + count: 4, + type: 'count', + lastUpdated: new Date(), + }); + } catch (err) { + // do nothing + } + return coreUsageStats; + } + public async incrementSavedObjectsBulkCreate(options: BaseIncrementOptions) { await this.updateUsageStats([], BULK_CREATE_STATS_PREFIX, options); }