('/api/deprecations');
registerGetRoute(router);
+
+ registerApiDeprecationsPostValidationHandler({ http, coreUsageData });
+ registerMarkAsResolvedRoute(router, { coreUsageData });
}
diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/routes/post_validation_handler.ts b/packages/core/deprecations/core-deprecations-server-internal/src/routes/post_validation_handler.ts
new file mode 100644
index 0000000000000..b93c17af2f536
--- /dev/null
+++ b/packages/core/deprecations/core-deprecations-server-internal/src/routes/post_validation_handler.ts
@@ -0,0 +1,51 @@
+/*
+ * 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 { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-server-internal';
+import type { CoreKibanaRequest } from '@kbn/core-http-router-server-internal';
+import type { InternalHttpServiceSetup } from '@kbn/core-http-server-internal';
+import { isObject } from 'lodash';
+import { RouteDeprecationInfo } from '@kbn/core-http-server/src/router/route';
+import { buildApiDeprecationId } from '../deprecations';
+
+interface Dependencies {
+ coreUsageData: InternalCoreUsageDataSetup;
+ http: InternalHttpServiceSetup;
+}
+
+/**
+ * listens to http post validation events to increment deprecated api calls
+ * This will keep track of any called deprecated API.
+ */
+export const registerApiDeprecationsPostValidationHandler = ({
+ coreUsageData,
+ http,
+}: Dependencies) => {
+ http.registerOnPostValidation(createRouteDeprecationsHandler({ coreUsageData }));
+};
+
+export function createRouteDeprecationsHandler({
+ coreUsageData,
+}: {
+ coreUsageData: InternalCoreUsageDataSetup;
+}) {
+ return (req: CoreKibanaRequest, { deprecated }: { deprecated?: RouteDeprecationInfo }) => {
+ if (deprecated && isObject(deprecated) && req.route.routePath) {
+ const counterName = buildApiDeprecationId({
+ routeMethod: req.route.method,
+ routePath: req.route.routePath,
+ routeVersion: req.apiVersion,
+ });
+
+ const client = coreUsageData.getClient();
+ // no await we just fire it off.
+ void client.incrementDeprecatedApi(counterName, { resolved: false });
+ }
+ };
+}
diff --git a/packages/core/deprecations/core-deprecations-server-internal/src/routes/resolve_deprecated_api.ts b/packages/core/deprecations/core-deprecations-server-internal/src/routes/resolve_deprecated_api.ts
new file mode 100644
index 0000000000000..840bc5ac22d23
--- /dev/null
+++ b/packages/core/deprecations/core-deprecations-server-internal/src/routes/resolve_deprecated_api.ts
@@ -0,0 +1,52 @@
+/*
+ * 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 { schema } from '@kbn/config-schema';
+import { InternalCoreUsageDataSetup } from '@kbn/core-usage-data-base-server-internal';
+import type { InternalDeprecationRouter } from '../internal_types';
+import { buildApiDeprecationId } from '../deprecations';
+
+export const registerMarkAsResolvedRoute = (
+ router: InternalDeprecationRouter,
+ { coreUsageData }: { coreUsageData: InternalCoreUsageDataSetup }
+) => {
+ router.post(
+ {
+ path: '/mark_as_resolved',
+ validate: {
+ body: schema.object({
+ domainId: schema.string(),
+ routePath: schema.string(),
+ routeMethod: schema.oneOf([
+ schema.literal('post'),
+ schema.literal('put'),
+ schema.literal('delete'),
+ schema.literal('patch'),
+ schema.literal('get'),
+ schema.literal('options'),
+ ]),
+ routeVersion: schema.maybe(schema.string()),
+ incrementBy: schema.number(),
+ }),
+ },
+ },
+ async (_, req, res) => {
+ const usageClient = coreUsageData.getClient();
+ const { routeMethod, routePath, routeVersion, incrementBy } = req.body;
+ const counterName = buildApiDeprecationId({
+ routeMethod,
+ routePath,
+ routeVersion,
+ });
+
+ await usageClient.incrementDeprecatedApi(counterName, { resolved: true, incrementBy });
+ return res.ok();
+ }
+ );
+};
diff --git a/packages/core/deprecations/core-deprecations-server-internal/tsconfig.json b/packages/core/deprecations/core-deprecations-server-internal/tsconfig.json
index ba06a3e9ec2f7..02be6b7cb8198 100644
--- a/packages/core/deprecations/core-deprecations-server-internal/tsconfig.json
+++ b/packages/core/deprecations/core-deprecations-server-internal/tsconfig.json
@@ -33,6 +33,11 @@
"@kbn/core-elasticsearch-server-mocks",
"@kbn/core-http-server",
"@kbn/core-elasticsearch-client-server-mocks",
+ "@kbn/core-usage-data-base-server-internal",
+ "@kbn/core-usage-data-server",
+ "@kbn/core-usage-data-server-internal",
+ "@kbn/core-usage-data-server-mocks",
+ "@kbn/core-http-router-server-internal",
],
"exclude": [
"target/**/*",
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..6aa6ac117f533 100644
--- a/packages/core/http/core-http-router-server-internal/index.ts
+++ b/packages/core/http/core-http-router-server-internal/index.ts
@@ -13,16 +13,10 @@ export {
CoreVersionedRouter,
ALLOWED_PUBLIC_VERSION,
unwrapVersionedResponseBodyValidation,
- type VersionedRouterRoute,
type HandlerResolutionStrategy,
} from './src/versioned_router';
export { Router } from './src/router';
-export type {
- RouterOptions,
- InternalRegistrar,
- InternalRegistrarOptions,
- InternalRouterRoute,
-} from './src/router';
+export type { RouterOptions, InternalRegistrar, InternalRegistrarOptions } from './src/router';
export { isKibanaRequest, isRealRequest, ensureRawRequest, CoreKibanaRequest } from './src/request';
export { isSafeMethod } from './src/route';
export { HapiResponseAdapter } from './src/response_adapter';
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 286b900fc24f5..9f89f1a70bb47 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
@@ -143,6 +143,8 @@ export class CoreKibanaRequest<
public readonly rewrittenUrl?: URL;
/** {@inheritDoc KibanaRequest.httpVersion} */
public readonly httpVersion: string;
+ /** {@inheritDoc KibanaRequest.apiVersion} */
+ public readonly apiVersion: undefined;
/** {@inheritDoc KibanaRequest.protocol} */
public readonly protocol: HttpProtocol;
/** {@inheritDoc KibanaRequest.authzResult} */
@@ -185,6 +187,7 @@ export class CoreKibanaRequest<
});
this.httpVersion = isRealReq ? request.raw.req.httpVersion : '1.0';
+ this.apiVersion = undefined;
this.protocol = getProtocolFromHttpVersion(this.httpVersion);
this.route = deepFreeze(this.getRouteInfo(request));
@@ -216,6 +219,7 @@ export class CoreKibanaRequest<
},
route: this.route,
authzResult: this.authzResult,
+ apiVersion: this.apiVersion,
};
}
@@ -252,7 +256,14 @@ export class CoreKibanaRequest<
} = request.route?.settings?.payload || {};
// the socket is undefined when using @hapi/shot, or when a "fake request" is used
- const socketTimeout = isRealRawRequest(request) ? request.raw.req.socket?.timeout : undefined;
+ let socketTimeout: undefined | number;
+ let routePath: undefined | string;
+
+ if (isRealRawRequest(request)) {
+ socketTimeout = request.raw.req.socket?.timeout;
+ routePath = request.route.path;
+ }
+
const options = {
authRequired: this.getAuthRequired(request),
// TypeScript note: Casting to `RouterOptions` to fix the following error:
@@ -266,6 +277,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 || [],
security: this.getSecurity(request),
@@ -285,6 +298,7 @@ export class CoreKibanaRequest<
return {
path: request.path ?? '/',
+ routePath,
method,
options,
};
diff --git a/packages/core/http/core-http-router-server-internal/src/router.test.ts b/packages/core/http/core-http-router-server-internal/src/router.test.ts
index f611e3b6308fe..2c702fb3ef702 100644
--- a/packages/core/http/core-http-router-server-internal/src/router.test.ts
+++ b/packages/core/http/core-http-router-server-internal/src/router.test.ts
@@ -50,7 +50,11 @@ describe('Router', () => {
path: '/',
validate: { body: validation, query: validation, params: validation },
options: {
- deprecated: true,
+ deprecated: {
+ documentationUrl: 'https://fake-url.com',
+ reason: { type: 'remove' },
+ severity: 'warning',
+ },
discontinued: 'post test discontinued',
summary: 'post test summary',
description: 'post test description',
@@ -72,7 +76,11 @@ describe('Router', () => {
validationSchemas: { body: validation, query: validation, params: validation },
isVersioned: false,
options: {
- deprecated: true,
+ deprecated: {
+ documentationUrl: 'https://fake-url.com',
+ reason: { type: 'remove' },
+ severity: 'warning',
+ },
discontinued: 'post test discontinued',
summary: 'post test summary',
description: 'post test description',
@@ -93,7 +101,7 @@ describe('Router', () => {
validate: { body: validation, query: validation, params: validation },
},
(context, req, res) => res.ok(),
- { isVersioned: true }
+ { isVersioned: true, events: false }
);
router.get(
{
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 36f324236a4d2..e7ad85fcda33a 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';
@@ -32,6 +33,7 @@ import { isZod } from '@kbn/zod';
import { validBodyOutput, getRequestValidation } from '@kbn/core-http-server';
import type { RouteSecurityGetter } from '@kbn/core-http-server';
import type { DeepPartial } from '@kbn/utility-types';
+import { RouteDeprecationInfo } from '@kbn/core-http-server/src/router/route';
import { RouteValidator } from './validator';
import { ALLOWED_PUBLIC_VERSION, CoreVersionedRouter } from './versioned_router';
import { CoreKibanaRequest } from './request';
@@ -52,7 +54,7 @@ export type ContextEnhancer<
Context extends RequestHandlerContextBase
> = (handler: RequestHandler) => RequestHandlerEnhanced
;
-function getRouteFullPath(routerPath: string, routePath: string) {
+export function getRouteFullPath(routerPath: string, routePath: string) {
// If router's path ends with slash and route's path starts with slash,
// we should omit one of them to have a valid concatenated path.
const routePathStartIndex = routerPath.endsWith('/') && routePath.startsWith('/') ? 1 : 0;
@@ -147,7 +149,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 */
@@ -166,15 +174,9 @@ export type InternalRegistrar ReturnType>;
/** @internal */
-export interface InternalRouterRoute extends RouterRoute {
- readonly isVersioned: boolean;
-}
-
-/** @internal */
-interface InternalGetRoutesOptions {
- /** @default false */
- excludeVersionedRoutes?: boolean;
-}
+type RouterEvents =
+ /** Called after route validation, regardless of success or failure */
+ 'onPostValidate';
/**
* @internal
@@ -182,7 +184,8 @@ interface InternalGetRoutesOptions {
export class Router
implements IRouter
{
- public routes: Array> = [];
+ private static ee = new EventEmitter();
+ public routes: Array> = [];
public pluginId?: symbol;
public get: InternalRegistrar<'get', Context>;
public post: InternalRegistrar<'post', Context>;
@@ -202,25 +205,27 @@ export class Router(
route: InternalRouteConfig,
handler: RequestHandler
,
- { isVersioned }: InternalRegistrarOptions = { isVersioned: false }
+ { isVersioned, events }: InternalRegistrarOptions = { isVersioned: false, events: true }
) => {
route = prepareRouteConfigValidation(route);
const routeSchemas = routeSchemasFromRouteConfig(route, method);
- const isPublicUnversionedApi =
+ const isPublicUnversionedRoute =
!isVersioned &&
route.options?.access === 'public' &&
// We do not consider HTTP resource routes as APIs
route.options?.httpResource !== true;
this.routes.push({
- handler: async (req, responseToolkit) =>
- await this.handle({
+ handler: async (req, responseToolkit) => {
+ return await this.handle({
routeSchemas,
request: req,
responseToolkit,
- isPublicUnversionedApi,
+ isPublicUnversionedRoute,
handler: this.enhanceWithContext(handler),
- }),
+ emit: events ? { onPostValidation: this.emitPostValidate } : undefined,
+ });
+ },
method,
path: getRouteFullPath(this.routerPath, route.path),
options: validOptions(method, route),
@@ -229,6 +234,8 @@ export class Router, route.options),
validationSchemas: route.validate,
+ // @ts-ignore using isVersioned: false in the type instead of boolean
+ // for typeguarding between versioned and unversioned RouterRoute types
isVersioned,
});
};
@@ -240,7 +247,15 @@ export class Router void) {
+ Router.ee.on(event, cb);
+ }
+
+ public static off(event: RouterEvents, cb: (req: CoreKibanaRequest, ...args: any[]) => void) {
+ Router.ee.off(event, cb);
+ }
+
+ public getRoutes({ excludeVersionedRoutes }: { excludeVersionedRoutes?: boolean } = {}) {
if (excludeVersionedRoutes) {
return this.routes.filter((route) => !route.isVersioned);
}
@@ -269,16 +284,29 @@ export class Router {
+ const postValidate: RouterEvents = 'onPostValidate';
+ Router.ee.emit(postValidate, request, routeOptions);
+ };
+
private async handle({
routeSchemas,
request,
responseToolkit,
- isPublicUnversionedApi,
+ emit,
+ isPublicUnversionedRoute,
handler,
}: {
request: Request;
responseToolkit: ResponseToolkit;
- isPublicUnversionedApi: boolean;
+ emit?: {
+ onPostValidation: (req: KibanaRequest, reqOptions: any) => void;
+ };
+ isPublicUnversionedRoute: boolean;
handler: RequestHandlerEnhanced<
P,
Q,
@@ -300,18 +328,24 @@ export class Router {
expect(router.post).toHaveBeenCalledWith(
expect.objectContaining(expectedRouteConfig),
expect.any(Function),
- { isVersioned: true }
+ { isVersioned: true, events: false }
);
});
diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts
index e9a9e60de8193..45654696ba0cf 100644
--- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts
+++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_route.ts
@@ -18,7 +18,6 @@ import type {
KibanaRequest,
KibanaResponseFactory,
ApiVersion,
- AddVersionOpts,
VersionedRoute,
VersionedRouteConfig,
IKibanaResponse,
@@ -26,9 +25,10 @@ import type {
RouteSecurityGetter,
RouteSecurity,
RouteMethod,
+ VersionedRouterRoute,
} from '@kbn/core-http-server';
import type { Mutable } from 'utility-types';
-import type { HandlerResolutionStrategy, Method, VersionedRouterRoute } from './types';
+import type { HandlerResolutionStrategy, Method, Options } from './types';
import { validate } from './validate';
import {
@@ -46,8 +46,6 @@ import { prepareVersionedRouteValidation, unwrapVersionedResponseBodyValidation
import type { RequestLike } from './route_version_utils';
import { Router } from '../router';
-type Options = AddVersionOpts;
-
interface InternalVersionedRouteConfig extends VersionedRouteConfig {
isDev: boolean;
useVersionResolutionStrategyForInternalPaths: Map;
@@ -68,7 +66,7 @@ function extractValidationSchemaFromHandler(handler: VersionedRouterRoute['handl
}
export class CoreVersionedRoute implements VersionedRoute {
- private readonly handlers = new Map<
+ public readonly handlers = new Map<
ApiVersion,
{
fn: RequestHandler;
@@ -127,7 +125,7 @@ export class CoreVersionedRoute implements VersionedRoute {
security: this.getSecurity,
},
this.requestHandler,
- { isVersioned: true }
+ { isVersioned: true, events: false }
);
}
@@ -181,6 +179,7 @@ export class CoreVersionedRoute implements VersionedRoute {
}
const req = originalReq as Mutable;
const version = this.getVersion(req);
+ req.apiVersion = version;
if (!version) {
return res.badRequest({
@@ -221,6 +220,8 @@ export class CoreVersionedRoute implements VersionedRoute {
req.params = params;
req.query = query;
} catch (e) {
+ // Emit onPostValidation even if validation fails.
+ this.router.emitPostValidate(req, handler.options.options);
return res.badRequest({ body: e.message, headers: getVersionHeader(version) });
}
} else {
@@ -230,6 +231,8 @@ export class CoreVersionedRoute implements VersionedRoute {
req.query = {};
}
+ this.router.emitPostValidate(req, handler.options.options);
+
const response = await handler.fn(ctx, req, res);
if (this.isDev && validation?.response?.[response.status]?.body) {
@@ -280,7 +283,6 @@ export class CoreVersionedRoute implements VersionedRoute {
public addVersion(options: Options, handler: RequestHandler): VersionedRoute {
this.validateVersion(options.version);
options = prepareVersionedRouteValidation(options);
-
this.handlers.set(options.version, {
fn: handler,
options,
diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.test.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.test.ts
index d56de36ba9a29..a3ffffc0ef219 100644
--- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.test.ts
+++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.test.ts
@@ -36,7 +36,6 @@ describe('Versioned router', () => {
versionedRouter.get({
path: '/test/{id}',
access: 'internal',
- deprecated: true,
discontinued: 'x.y.z',
});
versionedRouter.post({
@@ -50,16 +49,17 @@ describe('Versioned router', () => {
Array [
Object {
"handlers": Array [],
+ "isVersioned": true,
"method": "get",
"options": Object {
"access": "internal",
- "deprecated": true,
"discontinued": "x.y.z",
},
"path": "/test/{id}",
},
Object {
"handlers": Array [],
+ "isVersioned": true,
"method": "post",
"options": Object {
"access": "internal",
@@ -70,6 +70,7 @@ describe('Versioned router', () => {
},
Object {
"handlers": Array [],
+ "isVersioned": true,
"method": "delete",
"options": Object {
"access": "internal",
diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts
index e9272e17ab18e..ef1f8255420ae 100644
--- a/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts
+++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/core_versioned_router.ts
@@ -7,11 +7,16 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
-import type { VersionedRouter, VersionedRoute, VersionedRouteConfig } from '@kbn/core-http-server';
+import type {
+ VersionedRouter,
+ VersionedRoute,
+ VersionedRouteConfig,
+ VersionedRouterRoute,
+} from '@kbn/core-http-server';
import { omit } from 'lodash';
import { CoreVersionedRoute } from './core_versioned_route';
-import type { HandlerResolutionStrategy, Method, VersionedRouterRoute } from './types';
-import type { Router } from '../router';
+import type { HandlerResolutionStrategy, Method } from './types';
+import { getRouteFullPath, type Router } from '../router';
/** @internal */
export interface VersionedRouterArgs {
@@ -98,10 +103,11 @@ export class CoreVersionedRouter implements VersionedRouter {
public getRoutes(): VersionedRouterRoute[] {
return [...this.routes].map((route) => {
return {
- path: route.path,
+ path: getRouteFullPath(this.router.routerPath, route.path),
method: route.method,
options: omit(route.options, 'path'),
handlers: route.getHandlers(),
+ isVersioned: true,
};
});
}
diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/index.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/index.ts
index e283fcc2a590f..14c08076faae0 100644
--- a/packages/core/http/core-http-router-server-internal/src/versioned_router/index.ts
+++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/index.ts
@@ -9,6 +9,6 @@
export { resolvers as versionHandlerResolvers } from './handler_resolvers';
export { CoreVersionedRouter } from './core_versioned_router';
-export type { HandlerResolutionStrategy, VersionedRouterRoute } from './types';
+export type { HandlerResolutionStrategy } from './types';
export { ALLOWED_PUBLIC_VERSION } from './route_version_utils';
export { unwrapVersionedResponseBodyValidation } from './util';
diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/mocks.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/mocks.ts
index 5a958fa9251f7..36a672ca6a9f7 100644
--- a/packages/core/http/core-http-router-server-internal/src/versioned_router/mocks.ts
+++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/mocks.ts
@@ -20,6 +20,7 @@ export function createRouter(opts: CreateMockRouterOptions = {}) {
put: jest.fn(),
getRoutes: jest.fn(),
handleLegacyErrors: jest.fn(),
+ emitPostValidate: jest.fn(),
patch: jest.fn(),
routerPath: '',
versioned: {} as any,
diff --git a/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts b/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts
index aec1f8b0cf0ab..bdcaae486cd9c 100644
--- a/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts
+++ b/packages/core/http/core-http-router-server-internal/src/versioned_router/types.ts
@@ -7,25 +7,12 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
-import type {
- AddVersionOpts,
- RequestHandler,
- RouteMethod,
- VersionedRouteConfig,
-} from '@kbn/core-http-server';
+import type { AddVersionOpts, RouteMethod } from '@kbn/core-http-server';
export type Method = Exclude;
/** @internal */
-export interface VersionedRouterRoute {
- method: string;
- path: string;
- options: Omit, 'path'>;
- handlers: Array<{
- fn: RequestHandler;
- options: AddVersionOpts;
- }>;
-}
+export type Options = AddVersionOpts;
/**
* Specifies resolution strategy to use if a request does not provide a version.
diff --git a/packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts b/packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts
index 987288cf372bd..fec80b06963a0 100644
--- a/packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts
+++ b/packages/core/http/core-http-router-server-mocks/src/versioned_router.mock.ts
@@ -14,6 +14,7 @@ import type {
AddVersionOpts,
RequestHandler,
KibanaResponseFactory,
+ VersionedRouterRoute,
} from '@kbn/core-http-server';
export type MockedVersionedRoute = jest.Mocked;
@@ -24,14 +25,16 @@ const createMockVersionedRoute = (): MockedVersionedRoute => {
return api;
};
+type VersionedRouterMethods = keyof Omit;
+
export type MockedVersionedRouter = jest.Mocked> & {
- getRoute: (method: keyof VersionedRouter, path: string) => RegisteredVersionedRoute;
+ getRoute: (method: VersionedRouterMethods, path: string) => RegisteredVersionedRoute;
};
const createMethodHandler = () => jest.fn((_) => createMockVersionedRoute());
-
+const createMockGetRoutes = () => jest.fn(() => [] as VersionedRouterRoute[]);
export const createVersionedRouterMock = (): MockedVersionedRouter => {
- const router: Omit = {
+ const router: Omit = {
delete: createMethodHandler(),
get: createMethodHandler(),
patch: createMethodHandler(),
@@ -42,6 +45,7 @@ export const createVersionedRouterMock = (): MockedVersionedRouter => {
return {
...router,
getRoute: getRoute.bind(null, router),
+ getRoutes: createMockGetRoutes(),
};
};
@@ -54,9 +58,10 @@ export interface RegisteredVersionedRoute {
};
};
}
+
const getRoute = (
- router: Omit,
- method: keyof VersionedRouter,
+ router: Omit,
+ method: VersionedRouterMethods,
path: string
): RegisteredVersionedRoute => {
if (!router[method].mock.calls.length) {
diff --git a/packages/core/http/core-http-server-internal/src/http_server.test.ts b/packages/core/http/core-http-server-internal/src/http_server.test.ts
index c374ff7ca2107..69e69f784e65e 100644
--- a/packages/core/http/core-http-server-internal/src/http_server.test.ts
+++ b/packages/core/http/core-http-server-internal/src/http_server.test.ts
@@ -906,6 +906,7 @@ test('exposes route details of incoming request to a route handler', async () =>
.expect(200, {
method: 'get',
path: '/',
+ routePath: '/',
options: {
authRequired: true,
xsrfRequired: false,
@@ -1088,6 +1089,7 @@ test('exposes route details of incoming request to a route handler (POST + paylo
.expect(200, {
method: 'post',
path: '/',
+ routePath: '/',
options: {
authRequired: true,
xsrfRequired: true,
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 46470bac7c504..1dee6a3286788 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
@@ -35,10 +35,12 @@ import type {
HttpServerInfo,
HttpAuth,
IAuthHeadersStorage,
+ RouterDeprecatedRouteDetails,
+ RouteMethod,
} from '@kbn/core-http-server';
import { performance } from 'perf_hooks';
import { isBoom } from '@hapi/boom';
-import { identity } 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';
@@ -140,6 +142,7 @@ export interface HttpServerSetup {
registerAuth: HttpServiceSetup['registerAuth'];
registerOnPostAuth: HttpServiceSetup['registerOnPostAuth'];
registerOnPreResponse: HttpServiceSetup['registerOnPreResponse'];
+ getDeprecatedRoutes: HttpServiceSetup['getDeprecatedRoutes'];
authRequestHeaders: IAuthHeadersStorage;
auth: HttpAuth;
getServerInfo: () => HttpServerInfo;
@@ -280,6 +283,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,
@@ -385,6 +389,45 @@ export class HttpServer {
}
}
+ private getDeprecatedRoutes(): RouterDeprecatedRouteDetails[] {
+ const deprecatedRoutes: RouterDeprecatedRouteDetails[] = [];
+
+ for (const router of this.registeredRouters) {
+ const allRouterRoutes = [
+ // exclude so we dont get double entries.
+ // we need to call the versioned getRoutes to grab the full version options details
+ router.getRoutes({ excludeVersionedRoutes: true }),
+ router.versioned.getRoutes(),
+ ].flat();
+
+ deprecatedRoutes.push(
+ ...allRouterRoutes
+ .flat()
+ .map((route) => {
+ if (route.isVersioned === true) {
+ return [...route.handlers.entries()].map(([_, { options }]) => {
+ const deprecated = options.options?.deprecated;
+ return { route, version: `${options.version}`, deprecated };
+ });
+ }
+ return { route, version: undefined, deprecated: route.options.deprecated };
+ })
+ .flat()
+ .filter(({ deprecated }) => isObject(deprecated))
+ .flatMap(({ route, deprecated, version }) => {
+ return {
+ routeDeprecationOptions: deprecated!,
+ routeMethod: route.method as RouteMethod,
+ routePath: route.path,
+ routeVersion: version,
+ };
+ })
+ );
+ }
+
+ return deprecatedRoutes;
+ }
+
private setupGracefulShutdownHandlers() {
this.registerOnPreRouting((request, response, toolkit) => {
if (this.stopping || this.stopped) {
@@ -693,12 +736,13 @@ export class HttpServer {
this.log.debug(`registering route handler for [${route.path}]`);
// Hapi does not allow payload validation to be specified for 'head' or 'get' requests
const validate = isSafeMethod(route.method) ? undefined : { payload: true };
- const { authRequired, tags, body = {}, timeout } = route.options;
+ const { authRequired, tags, body = {}, timeout, deprecated } = route.options;
const { accepts: allow, override, maxBytes, output, parse } = body;
const kibanaRouteOptions: KibanaRouteOptions = {
xsrfRequired: route.options.xsrfRequired ?? !isSafeMethod(route.method),
access: route.options.access ?? 'internal',
+ deprecated,
security: route.security,
};
// Log HTTP API target consumer.
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 3f803b06f15fd..af310a6792057 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
@@ -162,7 +162,7 @@ export class HttpService
return this.internalPreboot;
}
- public async setup(deps: SetupDeps) {
+ public async setup(deps: SetupDeps): Promise {
this.requestHandlerContext = deps.context.createContextContainer();
this.configSubscription = this.config$.subscribe(() => {
if (this.httpServer.isListening()) {
@@ -185,9 +185,11 @@ export class HttpService
this.internalSetup = {
...serverContract,
-
+ registerOnPostValidation: (cb) => {
+ Router.on('onPostValidate', cb);
+ },
+ getRegisteredDeprecatedApis: () => serverContract.getDeprecatedRoutes(),
externalUrl: new ExternalUrlConfig(config.externalUrl),
-
createRouter: (
path: string,
pluginId: PluginOpaqueId = this.coreContext.coreId
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..0706af9ad73a2 100644
--- a/packages/core/http/core-http-server-internal/src/types.ts
+++ b/packages/core/http/core-http-server-internal/src/types.ts
@@ -16,7 +16,10 @@ import type {
IContextContainer,
HttpServiceSetup,
HttpServiceStart,
+ RouterDeprecatedRouteDetails,
} from '@kbn/core-http-server';
+import { CoreKibanaRequest } from '@kbn/core-http-router-server-internal';
+import { RouteDeprecationInfo } from '@kbn/core-http-server/src/router/route';
import type { HttpServerSetup } from './http_server';
import type { ExternalUrlConfig } from './external_url';
import type { InternalStaticAssets } from './static_assets';
@@ -54,6 +57,9 @@ export interface InternalHttpServiceSetup
path: string,
plugin?: PluginOpaqueId
) => IRouter;
+ registerOnPostValidation(
+ cb: (req: CoreKibanaRequest, metadata: { deprecated: RouteDeprecationInfo }) => void
+ ): void;
registerRouterAfterListening: (router: IRouter) => void;
registerStaticDir: (path: string, dirPath: string) => void;
authRequestHeaders: IAuthHeadersStorage;
@@ -65,6 +71,7 @@ export interface InternalHttpServiceSetup
contextName: ContextName,
provider: IContextProvider
) => IContextContainer;
+ getRegisteredDeprecatedApis: () => RouterDeprecatedRouteDetails[];
}
/** @internal */
diff --git a/packages/core/http/core-http-server-mocks/src/http_service.mock.ts b/packages/core/http/core-http-server-mocks/src/http_service.mock.ts
index 4e803ee5f86a8..116db3648f120 100644
--- a/packages/core/http/core-http-server-mocks/src/http_service.mock.ts
+++ b/packages/core/http/core-http-server-mocks/src/http_service.mock.ts
@@ -171,6 +171,9 @@ const createInternalSetupContractMock = () => {
createCookieSessionStorageFactory: jest.fn(),
registerOnPreRouting: jest.fn(),
registerOnPreAuth: jest.fn(),
+ getDeprecatedRoutes: jest.fn(),
+ getRegisteredDeprecatedApis: jest.fn(),
+ registerOnPostValidation: jest.fn(),
registerAuth: jest.fn(),
registerOnPostAuth: jest.fn(),
registerRouteHandlerContext: jest.fn(),
@@ -207,6 +210,7 @@ const createSetupContractMock = <
createCookieSessionStorageFactory: internalMock.createCookieSessionStorageFactory,
registerOnPreRouting: internalMock.registerOnPreRouting,
registerOnPreAuth: jest.fn(),
+ getDeprecatedRoutes: jest.fn(),
registerAuth: internalMock.registerAuth,
registerOnPostAuth: internalMock.registerOnPostAuth,
registerOnPreResponse: internalMock.registerOnPreResponse,
diff --git a/packages/core/http/core-http-server/index.ts b/packages/core/http/core-http-server/index.ts
index 4ba653fbd534c..7fe125c6aa9a7 100644
--- a/packages/core/http/core-http-server/index.ts
+++ b/packages/core/http/core-http-server/index.ts
@@ -93,6 +93,7 @@ export type {
IRouter,
RouteRegistrar,
RouterRoute,
+ RouterDeprecatedRouteDetails,
IKibanaSocket,
KibanaErrorResponseFactory,
KibanaRedirectionResponseFactory,
@@ -170,6 +171,7 @@ export type {
VersionedRouter,
VersionedRouteCustomResponseBodyValidation,
VersionedResponseBodyValidation,
+ VersionedRouterRoute,
} from './src/versioning';
export type { IStaticAssets } from './src/static_assets';
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..e2f675bd8d0c0 100644
--- a/packages/core/http/core-http-server/src/http_contract.ts
+++ b/packages/core/http/core-http-server/src/http_contract.ts
@@ -12,6 +12,7 @@ import type {
IContextProvider,
IRouter,
RequestHandlerContextBase,
+ RouterDeprecatedRouteDetails,
} from './router';
import type {
AuthenticationHandler,
@@ -359,6 +360,14 @@ export interface HttpServiceSetup<
* Provides common {@link HttpServerInfo | information} about the running http server.
*/
getServerInfo: () => HttpServerInfo;
+
+ /**
+ * Provides a list of all registered deprecated routes {{@link RouterDeprecatedRouteDetails | information}}.
+ * The routers will be evaluated everytime this function gets called to
+ * accommodate for any late route registrations
+ * @returns {RouterDeprecatedRouteDetails[]}
+ */
+ getDeprecatedRoutes: () => RouterDeprecatedRouteDetails[];
}
/** @public */
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 c26212fa0de81..8384ed838d5fc 100644
--- a/packages/core/http/core-http-server/src/router/index.ts
+++ b/packages/core/http/core-http-server/src/router/index.ts
@@ -80,7 +80,7 @@ export type {
LazyValidator,
} from './route_validator';
export { RouteValidationError } from './route_validator';
-export type { IRouter, RouteRegistrar, RouterRoute } from './router';
+export type { IRouter, RouteRegistrar, RouterRoute, RouterDeprecatedRouteDetails } from './router';
export type { IKibanaSocket } from './socket';
export type {
KibanaErrorResponseFactory,
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 5cb84a21be0c3..066372faca1e4 100644
--- a/packages/core/http/core-http-server/src/router/request.ts
+++ b/packages/core/http/core-http-server/src/router/request.ts
@@ -13,7 +13,7 @@ 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, RouteSecurity } from './route';
+import type { RouteMethod, RouteConfigOptions, RouteSecurity, RouteDeprecationInfo } from './route';
import type { Headers } from './headers';
export type RouteSecurityGetter = (request: {
@@ -26,6 +26,7 @@ export type InternalRouteSecurity = RouteSecurity | RouteSecurityGetter;
* @public
*/
export interface KibanaRouteOptions extends RouteOptionsApp {
+ deprecated?: RouteDeprecationInfo;
xsrfRequired: boolean;
access: 'internal' | 'public';
security?: InternalRouteSecurity;
@@ -59,6 +60,7 @@ export interface KibanaRequestRoute {
path: string;
method: Method;
options: KibanaRequestRouteOptions;
+ routePath?: string;
}
/**
@@ -190,6 +192,11 @@ export interface KibanaRequest<
*/
readonly rewrittenUrl?: URL;
+ /**
+ * The versioned route API version of this request.
+ */
+ readonly apiVersion: string | undefined;
+
/**
* The path parameter of this request.
*/
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 ac723db924a5a..6696babce41e1 100644
--- a/packages/core/http/core-http-server/src/router/route.ts
+++ b/packages/core/http/core-http-server/src/router/route.ts
@@ -113,6 +113,43 @@ export type RouteAccess = 'public' | 'internal';
export type Privilege = string;
+/**
+ * Route Deprecation info
+ * This information 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).
+ */
+export interface RouteDeprecationInfo {
+ documentationUrl: string;
+ severity: 'warning' | 'critical';
+ reason: VersionBumpDeprecationType | RemovalApiDeprecationType | MigrationApiDeprecationType;
+}
+
+/**
+ * bump deprecation reason denotes a new version of the API is available
+ */
+interface VersionBumpDeprecationType {
+ type: 'bump';
+ newApiVersion: string;
+}
+
+/**
+ * remove deprecation reason denotes the API was fully removed with no replacement
+ */
+interface RemovalApiDeprecationType {
+ type: 'remove';
+}
+
+/**
+ * migrate deprecation reason denotes the API has been migrated to a different API path
+ * Please make sure that if you are only incrementing the version of the API to use 'bump' instead
+ */
+interface MigrationApiDeprecationType {
+ type: 'migrate';
+ newApiPath: string;
+ newApiMethod: string;
+}
+
/**
* A set of privileges that can be used to define complex authorization requirements.
*
@@ -277,12 +314,18 @@ export interface RouteConfigOptions {
description?: string;
/**
- * Setting this to `true` declares this route to be deprecated. Consumers SHOULD
- * refrain from usage of this route.
+ * Description of deprecations for this HTTP API.
*
- * @remarks This will be surfaced in OAS documentation.
+ * @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).
+ *
+ * Setting this object marks the route as deprecated.
+ *
+ * @remarks This may be surfaced in OAS documentation.
+ * @public
*/
- deprecated?: boolean;
+ deprecated?: RouteDeprecationInfo;
/**
* Whether this route should be treated as "invisible" and excluded from router
diff --git a/packages/core/http/core-http-server/src/router/router.ts b/packages/core/http/core-http-server/src/router/router.ts
index ba2b5eb906a93..d8b79bee13025 100644
--- a/packages/core/http/core-http-server/src/router/router.ts
+++ b/packages/core/http/core-http-server/src/router/router.ts
@@ -10,7 +10,7 @@
import type { Request, ResponseObject, ResponseToolkit } from '@hapi/hapi';
import type Boom from '@hapi/boom';
import type { VersionedRouter } from '../versioning';
-import type { RouteConfig, RouteMethod } from './route';
+import type { RouteConfig, RouteDeprecationInfo, RouteMethod } from './route';
import type { RequestHandler, RequestHandlerWrapper } from './request_handler';
import type { RequestHandlerContextBase } from './request_handler_context';
import type { RouteConfigOptions } from './route';
@@ -98,7 +98,7 @@ export interface IRouter RouterRoute[];
+ getRoutes: (options?: { excludeVersionedRoutes?: boolean }) => RouterRoute[];
/**
* An instance very similar to {@link IRouter} that can be used for versioning HTTP routes
@@ -139,4 +139,13 @@ export interface RouterRoute {
req: Request,
responseToolkit: ResponseToolkit
) => Promise>;
+ isVersioned: false;
+}
+
+/** @public */
+export interface RouterDeprecatedRouteDetails {
+ routeDeprecationOptions: RouteDeprecationInfo;
+ routeMethod: RouteMethod;
+ routePath: string;
+ routeVersion?: string;
}
diff --git a/packages/core/http/core-http-server/src/versioning/index.ts b/packages/core/http/core-http-server/src/versioning/index.ts
index 94b60bd105aac..8d8a664c769ac 100644
--- a/packages/core/http/core-http-server/src/versioning/index.ts
+++ b/packages/core/http/core-http-server/src/versioning/index.ts
@@ -19,4 +19,5 @@ export type {
VersionedRouter,
VersionedRouteCustomResponseBodyValidation,
VersionedResponseBodyValidation,
+ VersionedRouterRoute,
} from './types';
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 7998c9cc91fa9..63e1e37754803 100644
--- a/packages/core/http/core-http-server/src/versioning/types.ts
+++ b/packages/core/http/core-http-server/src/versioning/types.ts
@@ -20,7 +20,7 @@ import type {
RouteValidationFunction,
LazyValidator,
} from '../..';
-
+import type { RouteDeprecationInfo } from '../router/route';
type RqCtx = RequestHandlerContextBase;
export type { ApiVersion };
@@ -89,17 +89,9 @@ export type VersionedRouteConfig = Omit<
*/
description?: string;
- /**
- * Declares this operation to be deprecated. Consumers SHOULD refrain from usage
- * of this route. This will be surfaced in OAS documentation.
- *
- * @default false
- */
- deprecated?: boolean;
-
/**
* Release version or date that this route will be removed
- * Use with `deprecated: true`
+ * Use with `deprecated: {@link RouteDeprecationInfo}`
*
* @default undefined
*/
@@ -234,6 +226,11 @@ export interface VersionedRouter {
* @track-adoption
*/
delete: VersionedRouteRegistrar<'delete', Ctx>;
+
+ /**
+ * @public
+ */
+ getRoutes: () => VersionedRouterRoute[];
}
/** @public */
@@ -341,6 +338,10 @@ export interface AddVersionOpts {
validate: false | VersionedRouteValidation
| (() => VersionedRouteValidation
); // Provide a way to lazily load validation schemas
security?: Exclude['security'], undefined>;
+
+ options?: {
+ deprecated?: RouteDeprecationInfo;
+ };
}
/**
@@ -363,3 +364,11 @@ export interface VersionedRoute<
handler: (...params: Parameters>) => MaybePromise
): VersionedRoute;
}
+
+export interface VersionedRouterRoute {
+ method: string;
+ path: string;
+ options: Omit, 'path'>;
+ handlers: Array<{ fn: RequestHandler; options: AddVersionOpts
}>;
+ isVersioned: true;
+}
diff --git a/packages/core/http/core-http-server/tsconfig.json b/packages/core/http/core-http-server/tsconfig.json
index 64b2dacf2f292..50a0ce973eb4e 100644
--- a/packages/core/http/core-http-server/tsconfig.json
+++ b/packages/core/http/core-http-server/tsconfig.json
@@ -15,7 +15,7 @@
"@kbn/utility-types",
"@kbn/core-base-common",
"@kbn/core-http-common",
- "@kbn/zod"
+ "@kbn/zod",
],
"exclude": [
"target/**/*",
diff --git a/packages/core/lifecycle/core-lifecycle-server-mocks/src/core_setup.mock.ts b/packages/core/lifecycle/core-lifecycle-server-mocks/src/core_setup.mock.ts
index b50e9279d4721..30f5958bd92c5 100644
--- a/packages/core/lifecycle/core-lifecycle-server-mocks/src/core_setup.mock.ts
+++ b/packages/core/lifecycle/core-lifecycle-server-mocks/src/core_setup.mock.ts
@@ -76,6 +76,8 @@ export function createCoreSetupMock({
userProfile: userProfileServiceMock.createSetup(),
coreUsageData: {
registerUsageCounter: coreUsageDataServiceMock.createSetupContract().registerUsageCounter,
+ registerDeprecatedUsageFetch:
+ coreUsageDataServiceMock.createSetupContract().registerDeprecatedUsageFetch,
},
plugins: {
onSetup: jest.fn(),
diff --git a/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts b/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts
index 2fcdf384cb897..d7d40c9b792f7 100644
--- a/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts
+++ b/packages/core/plugins/core-plugins-server-internal/src/plugin_context.ts
@@ -225,6 +225,7 @@ export function createPluginSetupContext({
},
http: {
createCookieSessionStorageFactory: deps.http.createCookieSessionStorageFactory,
+ getDeprecatedRoutes: deps.http.getDeprecatedRoutes,
registerRouteHandlerContext: <
Context extends RequestHandlerContext,
ContextName extends keyof Omit
@@ -283,6 +284,7 @@ export function createPluginSetupContext({
deprecations: deps.deprecations.getRegistry(plugin.name),
coreUsageData: {
registerUsageCounter: deps.coreUsageData.registerUsageCounter,
+ registerDeprecatedUsageFetch: deps.coreUsageData.registerDeprecatedUsageFetch,
},
plugins: {
onSetup: (...dependencyNames) => runtimeResolver.onSetup(plugin.name, dependencyNames),
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 447db192c3048..5082a27930e87 100644
--- a/packages/core/root/core-root-server-internal/src/server.ts
+++ b/packages/core/root/core-root-server-internal/src/server.ts
@@ -276,10 +276,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 });
@@ -303,6 +299,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,
diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/mocks/internal_mocks.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/mocks/internal_mocks.ts
index 625c8ed77fb48..ff5fe86df1173 100644
--- a/packages/core/saved-objects/core-saved-objects-server-internal/src/mocks/internal_mocks.ts
+++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/mocks/internal_mocks.ts
@@ -36,6 +36,7 @@ export const createCoreUsageDataSetupMock = () => {
getClient: jest.fn(),
registerUsageCounter: jest.fn(),
incrementUsageCounter: jest.fn(),
+ registerDeprecatedUsageFetch: jest.fn(),
};
return setupContract;
};
diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts
index b90aa0226d71c..c9a8656b3f753 100644
--- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts
+++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_create.ts
@@ -38,6 +38,7 @@ export const registerBulkCreateRoute = (
summary: `Create saved objects`,
tags: ['oas-tag:saved objects'],
access,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
validate: {
diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_delete.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_delete.ts
index 0f33ddc384bed..65209a6072748 100644
--- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_delete.ts
+++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_delete.ts
@@ -38,6 +38,7 @@ export const registerBulkDeleteRoute = (
summary: `Delete saved objects`,
tags: ['oas-tag:saved objects'],
access,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
validate: {
diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_get.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_get.ts
index 95fd9f5eab10a..3f87ca12248ae 100644
--- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_get.ts
+++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_get.ts
@@ -38,6 +38,7 @@ export const registerBulkGetRoute = (
summary: `Get saved objects`,
tags: ['oas-tag:saved objects'],
access,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
validate: {
diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_resolve.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_resolve.ts
index d6b74131fb74d..8e19114e798e0 100644
--- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_resolve.ts
+++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_resolve.ts
@@ -38,6 +38,7 @@ export const registerBulkResolveRoute = (
summary: `Resolve saved objects`,
tags: ['oas-tag:saved objects'],
access,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
description: `Retrieve multiple Kibana saved objects by ID, using any legacy URL aliases if they exist.
Under certain circumstances, when Kibana is upgraded, saved object migrations may necessitate regenerating some object IDs to enable new features. When an object's ID is regenerated, a legacy URL alias is created for that object, preserving its old ID. In such a scenario, that object can be retrieved with the bulk resolve API using either its new ID or its old ID.`,
diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_update.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_update.ts
index 7a7ec340d98ca..825a5f95482c0 100644
--- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_update.ts
+++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/bulk_update.ts
@@ -38,6 +38,7 @@ export const registerBulkUpdateRoute = (
summary: `Update saved objects`,
tags: ['oas-tag:saved objects'],
access,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
validate: {
diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/create.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/create.ts
index c8bfd4c0feaf9..57f4a10ed9377 100644
--- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/create.ts
+++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/create.ts
@@ -38,6 +38,7 @@ export const registerCreateRoute = (
summary: `Create a saved object`,
tags: ['oas-tag:saved objects'],
access,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
validate: {
diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/delete.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/delete.ts
index 7ef8aac3fa1b1..69287821d8049 100644
--- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/delete.ts
+++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/delete.ts
@@ -38,6 +38,7 @@ export const registerDeleteRoute = (
summary: `Delete a saved object`,
tags: ['oas-tag:saved objects'],
access,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
validate: {
diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts
index ac3b0555a7694..884ba1ed5c423 100644
--- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts
+++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/find.ts
@@ -42,6 +42,7 @@ export const registerFindRoute = (
summary: `Search for saved objects`,
tags: ['oas-tag:saved objects'],
access,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
validate: {
diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/get.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/get.ts
index 9784ef1c79ff4..9fe3aa8ff20c7 100644
--- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/get.ts
+++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/get.ts
@@ -38,6 +38,7 @@ export const registerGetRoute = (
summary: `Get a saved object`,
tags: ['oas-tag:saved objects'],
access,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
validate: {
diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/resolve.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/resolve.ts
index 295acacc0ba0e..28a6c82e9ffdf 100644
--- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/resolve.ts
+++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/resolve.ts
@@ -34,6 +34,7 @@ export const registerResolveRoute = (
summary: `Resolve a saved object`,
tags: ['oas-tag:saved objects'],
access,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
description: `Retrieve a single Kibana saved object by ID, using any legacy URL alias if it exists.
Under certain circumstances, when Kibana is upgraded, saved object migrations may necessitate regenerating some object IDs to enable new features. When an object's ID is regenerated, a legacy URL alias is created for that object, preserving its old ID. In such a scenario, that object can be retrieved with the resolve API using either its new ID or its old ID.`,
diff --git a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/update.ts b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/update.ts
index 9eeea40a29b68..cfedc3ce03d2a 100644
--- a/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/update.ts
+++ b/packages/core/saved-objects/core-saved-objects-server-internal/src/routes/update.ts
@@ -39,6 +39,7 @@ export const registerUpdateRoute = (
summary: `Update a saved object`,
tags: ['oas-tag:saved objects'],
access,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
validate: {
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..044fb683fb69a 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
@@ -8,7 +8,7 @@
*/
import type { KibanaRequest } from '@kbn/core-http-server';
-import type { CoreUsageStats } from '@kbn/core-usage-data-server';
+import type { CoreUsageStats, CoreDeprecatedApiUsageStats } from '@kbn/core-usage-data-server';
/** @internal */
export interface BaseIncrementOptions {
@@ -38,6 +38,13 @@ export type IncrementSavedObjectsExportOptions = BaseIncrementOptions & {
export interface ICoreUsageStatsClient {
getUsageStats(): Promise;
+ getDeprecatedApiUsageStats(): Promise;
+
+ incrementDeprecatedApi(
+ counterName: string,
+ options: { resolved?: boolean; incrementBy?: number }
+ ): 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..7ac7cbb7fbb57 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
@@ -37,6 +37,7 @@ import type {
CoreConfigUsageData,
CoreIncrementCounterParams,
CoreUsageCounter,
+ DeprecatedApiUsageFetcher,
} from '@kbn/core-usage-data-server';
import {
CORE_USAGE_STATS_TYPE,
@@ -48,6 +49,7 @@ import {
type SavedObjectsServiceStart,
} from '@kbn/core-saved-objects-server';
+import { ISavedObjectsRepository } from '@kbn/core-saved-objects-api-server';
import { isConfigured } from './is_configured';
import { coreUsageStatsType } from './saved_objects';
import { CoreUsageStatsClient } from './core_usage_stats_client';
@@ -88,6 +90,7 @@ export class CoreUsageDataService
private coreUsageStatsClient?: CoreUsageStatsClient;
private deprecatedConfigPaths: ChangedDeprecatedPaths = { set: [], unset: [] };
private incrementUsageCounter: CoreIncrementUsageCounter = () => {}; // Initially set to noop
+ private deprecatedApiUsageFetcher: DeprecatedApiUsageFetcher = async () => []; // Initially set to noop
constructor(core: CoreContext) {
this.logger = core.logger.get('core-usage-stats-service');
@@ -513,12 +516,21 @@ export class CoreUsageDataService
}
};
+ const registerDeprecatedUsageFetch = (fetchFn: DeprecatedApiUsageFetcher) => {
+ this.deprecatedApiUsageFetcher = fetchFn;
+ };
+
+ const fetchDeprecatedUsageStats = (params: { soClient: ISavedObjectsRepository }) => {
+ return this.deprecatedApiUsageFetcher(params);
+ };
+
this.coreUsageStatsClient = new CoreUsageStatsClient({
debugLogger: (message: string) => this.logger.debug(message),
basePath: http.basePath,
repositoryPromise: internalRepositoryPromise,
stop$: this.stop$,
incrementUsageCounter,
+ fetchDeprecatedUsageStats,
});
const contract: InternalCoreUsageDataSetup = {
@@ -526,6 +538,7 @@ export class CoreUsageDataService
getClient: () => this.coreUsageStatsClient!,
registerUsageCounter,
incrementUsageCounter,
+ registerDeprecatedUsageFetch,
};
return contract;
diff --git a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.test.ts b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.test.ts
index 948332c71f59a..9702e4b512345 100644
--- a/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.test.ts
+++ b/packages/core/usage-data/core-usage-data-server-internal/src/core_usage_stats_client.test.ts
@@ -52,6 +52,7 @@ describe('CoreUsageStatsClient', () => {
debugLogger: debugLoggerMock,
basePath: basePathMock,
repositoryPromise: Promise.resolve(repositoryMock),
+ fetchDeprecatedUsageStats: jest.fn(),
stop$,
incrementUsageCounter: incrementUsageCounterMock,
});
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..69eba9e1abf23 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
@@ -37,6 +37,7 @@ import {
takeUntil,
tap,
} from 'rxjs';
+import type { DeprecatedApiUsageFetcher } from '@kbn/core-usage-data-server';
export const BULK_CREATE_STATS_PREFIX = 'apiCalls.savedObjectsBulkCreate';
export const BULK_GET_STATS_PREFIX = 'apiCalls.savedObjectsBulkGet';
@@ -108,6 +109,16 @@ export interface CoreUsageEvent {
types?: string[];
}
+/**
+ * Interface that models core events triggered by API deprecations. (e.g. SO HTTP API calls)
+ * @internal
+ */
+export interface CoreUsageDeprecatedApiEvent {
+ id: string;
+ resolved: boolean;
+ incrementBy: number;
+}
+
/** @internal */
export interface CoreUsageStatsClientParams {
debugLogger: (message: string) => void;
@@ -116,6 +127,7 @@ export interface CoreUsageStatsClientParams {
stop$: Observable;
incrementUsageCounter: (params: CoreIncrementCounterParams) => void;
bufferTimeMs?: number;
+ fetchDeprecatedUsageStats: DeprecatedApiUsageFetcher;
}
/** @internal */
@@ -126,6 +138,8 @@ export class CoreUsageStatsClient implements ICoreUsageStatsClient {
private readonly fieldsToIncrement$ = new Subject();
private readonly flush$ = new Subject();
private readonly coreUsageEvents$ = new Subject();
+ private readonly coreUsageDeprecatedApiCalls$ = new Subject();
+ private readonly fetchDeprecatedUsageStats: DeprecatedApiUsageFetcher;
constructor({
debugLogger,
@@ -134,10 +148,12 @@ export class CoreUsageStatsClient implements ICoreUsageStatsClient {
stop$,
incrementUsageCounter,
bufferTimeMs = DEFAULT_BUFFER_TIME_MS,
+ fetchDeprecatedUsageStats,
}: CoreUsageStatsClientParams) {
this.debugLogger = debugLogger;
this.basePath = basePath;
this.repositoryPromise = repositoryPromise;
+ this.fetchDeprecatedUsageStats = fetchDeprecatedUsageStats;
this.fieldsToIncrement$
.pipe(
takeUntil(stop$),
@@ -180,6 +196,28 @@ export class CoreUsageStatsClient implements ICoreUsageStatsClient {
)
.subscribe();
+ this.coreUsageDeprecatedApiCalls$
+ .pipe(
+ takeUntil(stop$),
+ tap(({ id, incrementBy, resolved }) => {
+ incrementUsageCounter({
+ counterName: id,
+ counterType: `deprecated_api_call:${resolved ? 'resolved' : 'total'}`,
+ incrementBy,
+ });
+
+ if (resolved) {
+ // increment number of times the marked_as_resolve has been called
+ incrementUsageCounter({
+ counterName: id,
+ counterType: 'deprecated_api_call:marked_as_resolved',
+ incrementBy: 1,
+ });
+ }
+ })
+ )
+ .subscribe();
+
this.coreUsageEvents$
.pipe(
takeUntil(stop$),
@@ -215,6 +253,20 @@ export class CoreUsageStatsClient implements ICoreUsageStatsClient {
return coreUsageStats;
}
+ public async incrementDeprecatedApi(
+ id: string,
+ { resolved = false, incrementBy = 1 }: { resolved: boolean; incrementBy: number }
+ ) {
+ const deprecatedField = resolved ? 'deprecated_api_calls_resolved' : 'deprecated_api_calls';
+ this.coreUsageDeprecatedApiCalls$.next({ id, resolved, incrementBy });
+ this.fieldsToIncrement$.next([`${deprecatedField}.total`]);
+ }
+
+ public async getDeprecatedApiUsageStats() {
+ const repository = await this.repositoryPromise;
+ return await this.fetchDeprecatedUsageStats({ soClient: repository });
+ }
+
public async incrementSavedObjectsBulkCreate(options: BaseIncrementOptions) {
await this.updateUsageStats([], BULK_CREATE_STATS_PREFIX, options);
}
diff --git a/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_data_service.mock.ts b/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_data_service.mock.ts
index c85aee50653d2..57628901c1a60 100644
--- a/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_data_service.mock.ts
+++ b/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_data_service.mock.ts
@@ -17,6 +17,7 @@ import { coreUsageStatsClientMock } from './core_usage_stats_client.mock';
const createSetupContractMock = (usageStatsClient = coreUsageStatsClientMock.create()) => {
const setupContract: jest.Mocked = {
registerType: jest.fn(),
+ registerDeprecatedUsageFetch: jest.fn(),
getClient: jest.fn().mockReturnValue(usageStatsClient),
registerUsageCounter: jest.fn(),
incrementUsageCounter: jest.fn(),
diff --git a/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_stats_client.mock.ts b/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_stats_client.mock.ts
index f6b11204ca040..8896e9066ef78 100644
--- a/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_stats_client.mock.ts
+++ b/packages/core/usage-data/core-usage-data-server-mocks/src/core_usage_stats_client.mock.ts
@@ -12,6 +12,7 @@ import type { ICoreUsageStatsClient } from '@kbn/core-usage-data-base-server-int
const createUsageStatsClientMock = () =>
({
getUsageStats: jest.fn().mockResolvedValue({}),
+ getDeprecatedApiUsageStats: jest.fn().mockResolvedValue([]),
incrementSavedObjectsBulkCreate: jest.fn().mockResolvedValue(null),
incrementSavedObjectsBulkGet: jest.fn().mockResolvedValue(null),
incrementSavedObjectsBulkResolve: jest.fn().mockResolvedValue(null),
diff --git a/packages/core/usage-data/core-usage-data-server/index.ts b/packages/core/usage-data/core-usage-data-server/index.ts
index 77fb0b1066750..45ed369ca8381 100644
--- a/packages/core/usage-data/core-usage-data-server/index.ts
+++ b/packages/core/usage-data/core-usage-data-server/index.ts
@@ -19,4 +19,6 @@ export type {
CoreConfigUsageData,
CoreServicesUsageData,
CoreUsageStats,
+ CoreDeprecatedApiUsageStats,
+ DeprecatedApiUsageFetcher,
} from './src';
diff --git a/packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts b/packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts
index 36409a097129c..39df9d30d19c9 100644
--- a/packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts
+++ b/packages/core/usage-data/core-usage-data-server/src/core_usage_stats.ts
@@ -146,3 +146,16 @@ export interface CoreUsageStats {
'savedObjectsRepository.resolvedOutcome.notFound'?: number;
'savedObjectsRepository.resolvedOutcome.total'?: number;
}
+
+/**
+ * @public
+ *
+ * CoreDeprecatedApiUsageStats are collected over time while Kibana is running.
+ */
+export interface CoreDeprecatedApiUsageStats {
+ apiId: string;
+ totalMarkedAsResolved: number;
+ markedAsResolvedLastCalledAt: string;
+ apiTotalCalls: number;
+ apiLastCalledAt: string;
+}
diff --git a/packages/core/usage-data/core-usage-data-server/src/index.ts b/packages/core/usage-data/core-usage-data-server/src/index.ts
index 01cd52adbe986..05d0f02500053 100644
--- a/packages/core/usage-data/core-usage-data-server/src/index.ts
+++ b/packages/core/usage-data/core-usage-data-server/src/index.ts
@@ -12,11 +12,12 @@ export type {
CoreEnvironmentUsageData,
CoreConfigUsageData,
} from './core_usage_data';
-export type { CoreUsageStats } from './core_usage_stats';
+export type { CoreUsageStats, CoreDeprecatedApiUsageStats } from './core_usage_stats';
export type {
CoreUsageDataSetup,
CoreUsageCounter,
CoreIncrementUsageCounter,
CoreIncrementCounterParams,
+ DeprecatedApiUsageFetcher,
} from './setup_contract';
export type { CoreUsageData, ConfigUsageData, CoreUsageDataStart } from './start_contract';
diff --git a/packages/core/usage-data/core-usage-data-server/src/setup_contract.ts b/packages/core/usage-data/core-usage-data-server/src/setup_contract.ts
index bd87563792e6d..30ed7edb6ce1d 100644
--- a/packages/core/usage-data/core-usage-data-server/src/setup_contract.ts
+++ b/packages/core/usage-data/core-usage-data-server/src/setup_contract.ts
@@ -7,12 +7,12 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
+import type { ISavedObjectsRepository } from '@kbn/core-saved-objects-api-server';
+import type { CoreDeprecatedApiUsageStats } from './core_usage_stats';
+
/**
* Internal API for registering the Usage Tracker used for Core's usage data payload.
*
- * @note This API should never be used to drive application logic and is only
- * intended for telemetry purposes.
- *
* @public
*/
export interface CoreUsageDataSetup {
@@ -21,6 +21,7 @@ export interface CoreUsageDataSetup {
* when tracking events.
*/
registerUsageCounter: (usageCounter: CoreUsageCounter) => void;
+ registerDeprecatedUsageFetch: (fetchFn: DeprecatedApiUsageFetcher) => void;
}
/**
@@ -49,3 +50,11 @@ export interface CoreIncrementCounterParams {
* Method to call whenever an event occurs, so the counter can be increased.
*/
export type CoreIncrementUsageCounter = (params: CoreIncrementCounterParams) => void;
+
+/**
+ * @public
+ * Registers the deprecated API fetcher to be called to grab all the deprecated API usage details.
+ */
+export type DeprecatedApiUsageFetcher = (params: {
+ soClient: ISavedObjectsRepository;
+}) => Promise;
diff --git a/packages/core/usage-data/core-usage-data-server/tsconfig.json b/packages/core/usage-data/core-usage-data-server/tsconfig.json
index 77d0aa6ade3b1..83abb04d150ac 100644
--- a/packages/core/usage-data/core-usage-data-server/tsconfig.json
+++ b/packages/core/usage-data/core-usage-data-server/tsconfig.json
@@ -12,5 +12,8 @@
],
"exclude": [
"target/**/*",
+ ],
+ "kbn_references": [
+ "@kbn/core-saved-objects-api-server",
]
}
diff --git a/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap b/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap
index 818c0502ad774..fef935624ae64 100644
--- a/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap
+++ b/packages/kbn-router-to-openapispec/src/__snapshots__/generate_oas.test.ts.snap
@@ -572,6 +572,7 @@ OK response oas-test-version-2",
},
"/no-xsrf/{id}/{path*}": Object {
"post": Object {
+ "deprecated": true,
"operationId": "%2Fno-xsrf%2F%7Bid%7D%2F%7Bpath*%7D#1",
"parameters": Array [
Object {
diff --git a/packages/kbn-router-to-openapispec/src/generate_oas.test.ts b/packages/kbn-router-to-openapispec/src/generate_oas.test.ts
index c3532312d3088..25b786ac7c2c7 100644
--- a/packages/kbn-router-to-openapispec/src/generate_oas.test.ts
+++ b/packages/kbn-router-to-openapispec/src/generate_oas.test.ts
@@ -189,6 +189,7 @@ describe('generateOpenApiDocument', () => {
versionedRouters: { testVersionedRouter: { routes: [{}] } },
bodySchema: createSharedZodSchema(),
});
+
expect(
generateOpenApiDocument(
{
@@ -240,6 +241,7 @@ describe('generateOpenApiDocument', () => {
{
method: 'get',
path: '/test',
+ isVersioned: true,
options: { access: 'public' },
handlers: [
{
diff --git a/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts b/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts
index 898f234cdc310..a39afb6357bfc 100644
--- a/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts
+++ b/packages/kbn-router-to-openapispec/src/generate_oas.test.util.ts
@@ -10,6 +10,7 @@
import type { ZodType } from '@kbn/zod';
import { schema, Type } from '@kbn/config-schema';
import type { CoreVersionedRouter, Router } from '@kbn/core-http-router-server-internal';
+import type { RouterRoute, VersionedRouterRoute } from '@kbn/core-http-server';
import { createLargeSchema } from './oas_converter/kbn_config_schema/lib.test.util';
type RoutesMeta = ReturnType[number];
@@ -27,7 +28,7 @@ export const createVersionedRouter = (args: { routes: VersionedRoutesMeta[] }) =
} as unknown as CoreVersionedRouter;
};
-export const getRouterDefaults = (bodySchema?: RuntimeSchema) => ({
+export const getRouterDefaults = (bodySchema?: RuntimeSchema): RouterRoute => ({
isVersioned: false,
path: '/foo/{id}/{path*}',
method: 'get',
@@ -57,22 +58,29 @@ export const getRouterDefaults = (bodySchema?: RuntimeSchema) => ({
handler: jest.fn(),
});
-export const getVersionedRouterDefaults = (bodySchema?: RuntimeSchema) => ({
+export const getVersionedRouterDefaults = (bodySchema?: RuntimeSchema): VersionedRouterRoute => ({
method: 'get',
path: '/bar',
options: {
summary: 'versioned route',
access: 'public',
- deprecated: true,
discontinued: 'route discontinued version or date',
options: {
tags: ['ignore-me', 'oas-tag:versioned'],
},
},
+ isVersioned: true,
handlers: [
{
fn: jest.fn(),
options: {
+ options: {
+ deprecated: {
+ documentationUrl: 'https://fake-url',
+ reason: { type: 'remove' },
+ severity: 'critical',
+ },
+ },
validate: {
request: {
body:
diff --git a/packages/kbn-router-to-openapispec/src/process_router.ts b/packages/kbn-router-to-openapispec/src/process_router.ts
index 1884b6dddf863..f0d37fd208b7b 100644
--- a/packages/kbn-router-to-openapispec/src/process_router.ts
+++ b/packages/kbn-router-to-openapispec/src/process_router.ts
@@ -63,11 +63,12 @@ export const processRouter = (
parameters.push(...pathObjects, ...queryObjects);
}
+ const hasDeprecations = !!route.options.deprecated;
const operation: CustomOperationObject = {
summary: route.options.summary ?? '',
tags: route.options.tags ? extractTags(route.options.tags) : [],
...(route.options.description ? { description: route.options.description } : {}),
- ...(route.options.deprecated ? { deprecated: route.options.deprecated } : {}),
+ ...(hasDeprecations ? { deprecated: true } : {}),
...(route.options.discontinued ? { 'x-discontinued': route.options.discontinued } : {}),
requestBody: !!validationSchemas?.body
? {
diff --git a/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts b/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts
index 6452c2cf3c2cc..f9f4f4898c1d0 100644
--- a/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts
+++ b/packages/kbn-router-to-openapispec/src/process_versioned_router.test.ts
@@ -8,10 +8,7 @@
*/
import { schema } from '@kbn/config-schema';
-import type {
- CoreVersionedRouter,
- VersionedRouterRoute,
-} from '@kbn/core-http-router-server-internal';
+import type { CoreVersionedRouter } from '@kbn/core-http-router-server-internal';
import { get } from 'lodash';
import { OasConverter } from './oas_converter';
import { createOperationIdCounter } from './operation_id_counter';
@@ -20,6 +17,7 @@ import {
extractVersionedResponses,
extractVersionedRequestBodies,
} from './process_versioned_router';
+import { VersionedRouterRoute } from '@kbn/core-http-server';
let oasConverter: OasConverter;
beforeEach(() => {
@@ -151,6 +149,7 @@ describe('processVersionedRouter', () => {
const createTestRoute: () => VersionedRouterRoute = () => ({
path: '/foo',
method: 'get',
+ isVersioned: true,
options: {
access: 'public',
deprecated: true,
diff --git a/packages/kbn-router-to-openapispec/src/process_versioned_router.ts b/packages/kbn-router-to-openapispec/src/process_versioned_router.ts
index 5446f1409760d..7eee0d20c11d2 100644
--- a/packages/kbn-router-to-openapispec/src/process_versioned_router.ts
+++ b/packages/kbn-router-to-openapispec/src/process_versioned_router.ts
@@ -10,10 +10,9 @@
import {
type CoreVersionedRouter,
versionHandlerResolvers,
- VersionedRouterRoute,
unwrapVersionedResponseBodyValidation,
} from '@kbn/core-http-router-server-internal';
-import type { RouteMethod } from '@kbn/core-http-server';
+import type { RouteMethod, VersionedRouterRoute } from '@kbn/core-http-server';
import type { OpenAPIV3 } from 'openapi-types';
import type { GenerateOpenApiDocumentOptionsFilters } from './generate_oas';
import type { OasConverter } from './oas_converter';
@@ -94,11 +93,13 @@ export const processVersionedRouter = (
const hasBody = Boolean(extractValidationSchemaFromVersionedHandler(handler)?.request?.body);
const contentType = extractContentType(route.options.options?.body);
const hasVersionFilter = Boolean(filters?.version);
+ // If any handler is deprecated we show deprecated: true in the spec
+ const hasDeprecations = route.handlers.some(({ options }) => !!options.options?.deprecated);
const operation: OpenAPIV3.OperationObject = {
summary: route.options.summary ?? '',
tags: route.options.options?.tags ? extractTags(route.options.options.tags) : [],
...(route.options.description ? { description: route.options.description } : {}),
- ...(route.options.deprecated ? { deprecated: route.options.deprecated } : {}),
+ ...(hasDeprecations ? { deprecated: true } : {}),
...(route.options.discontinued ? { 'x-discontinued': route.options.discontinued } : {}),
requestBody: hasBody
? {
diff --git a/src/core/server/integration_tests/http/request_representation.test.ts b/src/core/server/integration_tests/http/request_representation.test.ts
index f180a3a49ce0f..82300eceec774 100644
--- a/src/core/server/integration_tests/http/request_representation.test.ts
+++ b/src/core/server/integration_tests/http/request_representation.test.ts
@@ -87,6 +87,7 @@ describe('request logging', () => {
route: {
method: 'get',
path: '/',
+ routePath: '/',
options: expect.any(Object),
},
uuid: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
@@ -116,10 +117,12 @@ describe('request logging', () => {
auth: { isAuthenticated: false },
route: {
path: '/',
+ routePath: '/',
method: 'get',
options: {
authRequired: true,
xsrfRequired: false,
+ deprecated: undefined,
access: 'internal',
tags: [],
security: undefined,
@@ -127,7 +130,8 @@ describe('request logging', () => {
body: undefined
}
},
- authzResult: undefined
+ authzResult: undefined,
+ apiVersion: undefined
}"
`);
});
diff --git a/src/plugins/kibana_usage_collection/server/collectors/common/counters.test.ts b/src/plugins/kibana_usage_collection/server/collectors/common/counters.test.ts
index 6a64e6b597e6a..33e9b68f59c4a 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/common/counters.test.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/common/counters.test.ts
@@ -118,7 +118,6 @@ describe('createCounterFetcher', () => {
dailyEvents,
})
);
- // @ts-expect-error incomplete mock implementation
const { dailyEvents } = await fetch({ soClient: soClientMock });
expect(dailyEvents).toHaveLength(5);
const intersectingEntry = dailyEvents.find(
diff --git a/src/plugins/kibana_usage_collection/server/collectors/common/counters.ts b/src/plugins/kibana_usage_collection/server/collectors/common/counters.ts
index 9300ddcb959aa..df0ca67a85198 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/common/counters.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/common/counters.ts
@@ -30,7 +30,7 @@ export function createCounterFetcher(
filter: string,
transform: (counters: CounterEvent[]) => T
) {
- return async ({ soClient }: CollectorFetchContext) => {
+ return async ({ soClient }: Pick) => {
const finder = soClient.createPointInTimeFinder({
type: USAGE_COUNTERS_SAVED_OBJECT_TYPE,
namespaces: ['*'],
diff --git a/src/plugins/kibana_usage_collection/server/collectors/core/fetch_deprecated_api_counters.ts b/src/plugins/kibana_usage_collection/server/collectors/core/fetch_deprecated_api_counters.ts
new file mode 100644
index 0000000000000..1c90b23e40b16
--- /dev/null
+++ b/src/plugins/kibana_usage_collection/server/collectors/core/fetch_deprecated_api_counters.ts
@@ -0,0 +1,69 @@
+/*
+ * 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 { Logger } from '@kbn/logging';
+import type { CoreDeprecatedApiUsageStats } from '@kbn/core-usage-data-server';
+import { USAGE_COUNTERS_SAVED_OBJECT_TYPE } from '@kbn/usage-collection-plugin/server';
+
+import { createCounterFetcher, type CounterEvent } from '../common/counters';
+
+const DEPRECATED_API_COUNTERS_FILTER = `${USAGE_COUNTERS_SAVED_OBJECT_TYPE}.attributes.counterType: deprecated_api_call\\:*`;
+
+const mergeCounter = (counter: CounterEvent, acc?: CoreDeprecatedApiUsageStats) => {
+ if (acc && acc?.apiId !== counter.counterName) {
+ throw new Error(
+ `Failed to merge mismatching counterNames: ${acc.apiId} with ${counter.counterName}`
+ );
+ }
+ const isMarkedCounter = counter.counterType.endsWith(':marked_as_resolved');
+
+ const finalCounter = {
+ apiId: counter.counterName,
+ apiTotalCalls: 0,
+ apiLastCalledAt: 'unknown',
+ totalMarkedAsResolved: 0,
+ markedAsResolvedLastCalledAt: 'unknown',
+ ...(acc || {}),
+ };
+
+ if (isMarkedCounter) {
+ return finalCounter;
+ }
+
+ const isResolvedCounter = counter.counterType.endsWith(':resolved');
+ const totalKey = isResolvedCounter ? 'totalMarkedAsResolved' : 'apiTotalCalls';
+ const lastUpdatedKey = isResolvedCounter ? 'markedAsResolvedLastCalledAt' : 'apiLastCalledAt';
+
+ const newPayload = {
+ [totalKey]: (finalCounter[totalKey] || 0) + counter.total,
+ [lastUpdatedKey]: counter.lastUpdatedAt,
+ };
+
+ return {
+ ...finalCounter,
+ ...newPayload,
+ };
+};
+
+function mergeCounters(counters: CounterEvent[]): CoreDeprecatedApiUsageStats[] {
+ const mergedCounters = counters.reduce((acc, counter) => {
+ const { counterName } = counter;
+ const existingCounter = acc[counterName];
+
+ acc[counterName] = mergeCounter(counter, existingCounter);
+
+ return acc;
+ }, {} as Record);
+
+ return Object.values(mergedCounters);
+}
+
+export const fetchDeprecatedApiCounterStats = (logger: Logger) => {
+ return createCounterFetcher(logger, DEPRECATED_API_COUNTERS_FILTER, mergeCounters);
+};
diff --git a/src/plugins/kibana_usage_collection/server/collectors/core/index.ts b/src/plugins/kibana_usage_collection/server/collectors/core/index.ts
index 0e0f783b0f847..e298560893ccb 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/core/index.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/core/index.ts
@@ -8,3 +8,4 @@
*/
export { registerCoreUsageCollector } from './core_usage_collector';
+export { fetchDeprecatedApiCounterStats } from './fetch_deprecated_api_counters';
diff --git a/src/plugins/kibana_usage_collection/server/collectors/index.ts b/src/plugins/kibana_usage_collection/server/collectors/index.ts
index ef5287324ee59..c22fb3b5697f8 100644
--- a/src/plugins/kibana_usage_collection/server/collectors/index.ts
+++ b/src/plugins/kibana_usage_collection/server/collectors/index.ts
@@ -17,7 +17,7 @@ export {
export { registerOpsStatsCollector } from './ops_stats';
export { registerCloudProviderUsageCollector } from './cloud';
export { registerCspCollector } from './csp';
-export { registerCoreUsageCollector } from './core';
+export { registerCoreUsageCollector, fetchDeprecatedApiCounterStats } from './core';
export { registerLocalizationUsageCollector } from './localization';
export { registerConfigUsageCollector } from './config_usage';
export { registerUiCountersUsageCollector } from './ui_counters';
diff --git a/src/plugins/kibana_usage_collection/server/plugin.ts b/src/plugins/kibana_usage_collection/server/plugin.ts
index bbfc010c0e065..48fb1c6ff7b9b 100644
--- a/src/plugins/kibana_usage_collection/server/plugin.ts
+++ b/src/plugins/kibana_usage_collection/server/plugin.ts
@@ -43,6 +43,7 @@ import {
registerUsageCountersUsageCollector,
registerSavedObjectsCountUsageCollector,
registerEventLoopDelaysCollector,
+ fetchDeprecatedApiCounterStats,
} from './collectors';
interface KibanaUsageCollectionPluginsDepsSetup {
@@ -74,6 +75,10 @@ export class KibanaUsageCollectionPlugin implements Plugin {
registerEbtCounters(coreSetup.analytics, usageCollection);
this.eventLoopUsageCounter = usageCollection.createUsageCounter('eventLoop');
coreSetup.coreUsageData.registerUsageCounter(usageCollection.createUsageCounter('core'));
+ const deprecatedUsageFetch = fetchDeprecatedApiCounterStats(
+ this.logger.get('deprecated-api-usage')
+ );
+ coreSetup.coreUsageData.registerDeprecatedUsageFetch(deprecatedUsageFetch);
this.registerUsageCollectors(
usageCollection,
coreSetup,
diff --git a/test/plugin_functional/test_suites/core/deprecations.ts b/test/plugin_functional/test_suites/core/deprecations.ts
index 081209e6c7dce..b41adc7ffefa5 100644
--- a/test/plugin_functional/test_suites/core/deprecations.ts
+++ b/test/plugin_functional/test_suites/core/deprecations.ts
@@ -151,7 +151,7 @@ export default function ({ getService, getPageObjects }: PluginFunctionalProvide
});
expect(resolveResult).to.eql({
- reason: 'This deprecation cannot be resolved automatically.',
+ reason: 'This deprecation cannot be resolved automatically or marked as resolved.',
status: 'fail',
});
});
diff --git a/x-pack/plugins/actions/server/routes/legacy/create.ts b/x-pack/plugins/actions/server/routes/legacy/create.ts
index 6da0d25c2e59c..f667a9e003a77 100644
--- a/x-pack/plugins/actions/server/routes/legacy/create.ts
+++ b/x-pack/plugins/actions/server/routes/legacy/create.ts
@@ -38,6 +38,7 @@ export const createActionRoute = (
access: 'public',
summary: `Create a connector`,
tags: ['oas-tag:connectors'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
validate: {
diff --git a/x-pack/plugins/actions/server/routes/legacy/delete.ts b/x-pack/plugins/actions/server/routes/legacy/delete.ts
index 2204095e03801..c7e1e985cc6f0 100644
--- a/x-pack/plugins/actions/server/routes/legacy/delete.ts
+++ b/x-pack/plugins/actions/server/routes/legacy/delete.ts
@@ -32,6 +32,7 @@ export const deleteActionRoute = (
summary: `Delete a connector`,
description: 'WARNING: When you delete a connector, it cannot be recovered.',
tags: ['oas-tag:connectors'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
validate: {
diff --git a/x-pack/plugins/actions/server/routes/legacy/execute.ts b/x-pack/plugins/actions/server/routes/legacy/execute.ts
index 8083f884c1186..71b04262075d4 100644
--- a/x-pack/plugins/actions/server/routes/legacy/execute.ts
+++ b/x-pack/plugins/actions/server/routes/legacy/execute.ts
@@ -37,6 +37,7 @@ export const executeActionRoute = (
options: {
access: 'public',
summary: `Run a connector`,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
tags: ['oas-tag:connectors'],
},
diff --git a/x-pack/plugins/actions/server/routes/legacy/get.ts b/x-pack/plugins/actions/server/routes/legacy/get.ts
index 2adf6413b9248..571849ccaf478 100644
--- a/x-pack/plugins/actions/server/routes/legacy/get.ts
+++ b/x-pack/plugins/actions/server/routes/legacy/get.ts
@@ -31,6 +31,7 @@ export const getActionRoute = (
options: {
access: 'public',
summary: `Get connector information`,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
tags: ['oas-tag:connectors'],
},
diff --git a/x-pack/plugins/actions/server/routes/legacy/get_all.ts b/x-pack/plugins/actions/server/routes/legacy/get_all.ts
index 04ba20f4fb3c8..f0a17acb96691 100644
--- a/x-pack/plugins/actions/server/routes/legacy/get_all.ts
+++ b/x-pack/plugins/actions/server/routes/legacy/get_all.ts
@@ -23,6 +23,7 @@ export const getAllActionRoute = (
options: {
access: 'public',
summary: `Get all connectors`,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
tags: ['oas-tag:connectors'],
},
diff --git a/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts b/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts
index f42cd7479286c..cc3e9c23f240d 100644
--- a/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts
+++ b/x-pack/plugins/actions/server/routes/legacy/list_action_types.ts
@@ -27,6 +27,7 @@ export const listActionTypesRoute = (
options: {
access: 'public',
summary: `Get connector types`,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
tags: ['oas-tag:connectors'],
},
diff --git a/x-pack/plugins/actions/server/routes/legacy/update.ts b/x-pack/plugins/actions/server/routes/legacy/update.ts
index 81106c2cdc73b..0bf1ec7ece55d 100644
--- a/x-pack/plugins/actions/server/routes/legacy/update.ts
+++ b/x-pack/plugins/actions/server/routes/legacy/update.ts
@@ -37,6 +37,7 @@ export const updateActionRoute = (
options: {
access: 'public',
summary: `Update a connector`,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
tags: ['oas-tag:connectors'],
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/create.ts b/x-pack/plugins/alerting/server/routes/legacy/create.ts
index d8505fdc8453d..333877b7df49e 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/create.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/create.ts
@@ -65,6 +65,7 @@ export const createAlertRoute = ({
access: isServerless ? 'internal' : 'public',
summary: 'Create an alert',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/delete.ts b/x-pack/plugins/alerting/server/routes/legacy/delete.ts
index f931af10ccbbf..2b63de9e4ee73 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/delete.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/delete.ts
@@ -33,6 +33,7 @@ export const deleteAlertRoute = (
access: isServerless ? 'internal' : 'public',
summary: 'Delete an alert',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/disable.ts b/x-pack/plugins/alerting/server/routes/legacy/disable.ts
index 486bef89dd197..0c6f3cf062a0c 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/disable.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/disable.ts
@@ -34,6 +34,7 @@ export const disableAlertRoute = (
access: isServerless ? 'internal' : 'public',
summary: 'Disable an alert',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/enable.ts b/x-pack/plugins/alerting/server/routes/legacy/enable.ts
index c5076b3de1a54..d52eaa784f670 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/enable.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/enable.ts
@@ -35,6 +35,7 @@ export const enableAlertRoute = (
access: isServerless ? 'internal' : 'public',
summary: 'Enable an alert',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/find.ts b/x-pack/plugins/alerting/server/routes/legacy/find.ts
index ad85f3c7333b0..fa309ae51f2e4 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/find.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/find.ts
@@ -79,6 +79,7 @@ export const findAlertRoute = (
tags: ['oas-tag:alerting'],
description:
'Gets a paginated set of alerts. Alert `params` are stored as a flattened field type and analyzed as keywords. As alerts change in Kibana, the results on each page of the response also change. Use the find API for traditional paginated results, but avoid using it to export large amounts of data.',
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/get.ts b/x-pack/plugins/alerting/server/routes/legacy/get.ts
index 8437b888f7c0f..e5eff52bf02d6 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/get.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/get.ts
@@ -33,6 +33,7 @@ export const getAlertRoute = (
access: isServerless ? 'internal' : 'public',
summary: 'Get an alert',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts
index fd1fae64b538f..58a75dd68dce7 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_instance_summary.ts
@@ -44,6 +44,7 @@ export const getAlertInstanceSummaryRoute = (
access: isServerless ? 'internal' : 'public',
summary: 'Get an alert summary',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts
index 5943ab7203599..e952ef8719667 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/get_alert_state.ts
@@ -33,6 +33,7 @@ export const getAlertStateRoute = (
access: isServerless ? 'internal' : 'public',
summary: 'Get the state of an alert',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/health.ts b/x-pack/plugins/alerting/server/routes/legacy/health.ts
index 90bfda371932a..8f67767941fd2 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/health.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/health.ts
@@ -29,6 +29,7 @@ export function healthRoute(
access: isServerless ? 'internal' : 'public',
summary: 'Get the alerting framework health',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts b/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts
index 6668ff219ade0..35d6a7efeeee3 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/list_alert_types.ts
@@ -25,6 +25,7 @@ export const listAlertTypesRoute = (
access: isServerless ? 'internal' : 'public',
summary: 'Get the alert types',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts b/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts
index eaa989dc8fb6a..5c4fc1542ef5b 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/mute_all.ts
@@ -34,6 +34,7 @@ export const muteAllAlertRoute = (
access: isServerless ? 'internal' : 'public',
summary: 'Mute all alert instances',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts b/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts
index c309dd36b7744..ab0b52d41de29 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/mute_instance.ts
@@ -37,6 +37,7 @@ export const muteAlertInstanceRoute = (
access: isServerless ? 'internal' : 'public',
summary: 'Mute an alert',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts b/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts
index 70dfd65e33c79..0681e7d2cf01e 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/unmute_all.ts
@@ -34,6 +34,7 @@ export const unmuteAllAlertRoute = (
access: isServerless ? 'internal' : 'public',
summary: 'Unmute all alert instances',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts b/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts
index 7990539d6c20d..1101a2b5092e7 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/unmute_instance.ts
@@ -35,6 +35,7 @@ export const unmuteAlertInstanceRoute = (
access: isServerless ? 'internal' : 'public',
summary: 'Unmute an alert',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/update.ts b/x-pack/plugins/alerting/server/routes/legacy/update.ts
index b65579e17b087..01adeb5c634dc 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/update.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/update.ts
@@ -61,6 +61,7 @@ export const updateAlertRoute = (
access: isServerless ? 'internal' : 'public',
summary: 'Update an alert',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts b/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts
index 06c466333967c..30c51d3cdcf5c 100644
--- a/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts
+++ b/x-pack/plugins/alerting/server/routes/legacy/update_api_key.ts
@@ -35,6 +35,7 @@ export const updateApiKeyRoute = (
access: isServerless ? 'internal' : 'public',
summary: 'Update the API key for an alert',
tags: ['oas-tag:alerting'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
},
diff --git a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts
index a33b638f5310e..6e8ac79bffec9 100644
--- a/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts
+++ b/x-pack/plugins/cases/server/routes/api/comments/get_all_comment.ts
@@ -31,6 +31,7 @@ export const getAllCommentsRoute = createCasesRoute({
summary: `Gets all case comments`,
tags: ['oas-tag:cases'],
// description: 'You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases with the comments you\'re seeking.',
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
handler: async ({ context, request, response }) => {
diff --git a/x-pack/plugins/cases/server/routes/api/stats/get_status.ts b/x-pack/plugins/cases/server/routes/api/stats/get_status.ts
index 07d02c0b6713f..dce369e4a0f45 100644
--- a/x-pack/plugins/cases/server/routes/api/stats/get_status.ts
+++ b/x-pack/plugins/cases/server/routes/api/stats/get_status.ts
@@ -26,6 +26,7 @@ export const getStatusRoute: CaseRoute = createCasesRoute({
description:
'Returns the number of cases that are open, closed, and in progress in the default space.',
// You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the cases you're seeking.
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
handler: async ({ context, request, response }) => {
diff --git a/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts
index 5edc7a261b3c4..17fe0dcdb9012 100644
--- a/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts
+++ b/x-pack/plugins/cases/server/routes/api/user_actions/get_all_user_actions.ts
@@ -30,6 +30,7 @@ export const getUserActionsRoute = createCasesRoute({
description: `Returns all user activity for a case.`,
// You must have `read` privileges for the **Cases** feature in the **Management**, **Observability**, or **Security** section of the Kibana feature privileges, depending on the owner of the case you're seeking.
tags: ['oas-tag:cases'],
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
},
handler: async ({ context, request, response }) => {
diff --git a/x-pack/plugins/fleet/server/routes/agent/index.ts b/x-pack/plugins/fleet/server/routes/agent/index.ts
index f3b6e28a90f37..fc45869dc1219 100644
--- a/x-pack/plugins/fleet/server/routes/agent/index.ts
+++ b/x-pack/plugins/fleet/server/routes/agent/index.ts
@@ -397,6 +397,7 @@ export const registerAPIRoutes = (router: FleetAuthzRouter, config: FleetConfigT
fleetAuthz: {
fleet: { allAgents: true },
},
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
})
.addVersion(
@@ -618,6 +619,7 @@ export const registerAPIRoutes = (router: FleetAuthzRouter, config: FleetConfigT
fleetAuthz: {
fleet: { readAgents: true },
},
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
})
.addVersion(
diff --git a/x-pack/plugins/fleet/server/routes/app/index.ts b/x-pack/plugins/fleet/server/routes/app/index.ts
index ea5d7be8156d5..c0b7dbcfa1743 100644
--- a/x-pack/plugins/fleet/server/routes/app/index.ts
+++ b/x-pack/plugins/fleet/server/routes/app/index.ts
@@ -293,6 +293,7 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType
fleet: { allAgents: true },
},
description: `Create a service token`,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
})
.addVersion(
diff --git a/x-pack/plugins/fleet/server/routes/enrollment_api_key/index.ts b/x-pack/plugins/fleet/server/routes/enrollment_api_key/index.ts
index a93f63822e5b4..bcf4448420919 100644
--- a/x-pack/plugins/fleet/server/routes/enrollment_api_key/index.ts
+++ b/x-pack/plugins/fleet/server/routes/enrollment_api_key/index.ts
@@ -161,6 +161,7 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
fleetAuthz: {
fleet: { readEnrollmentTokens: true },
},
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
})
.addVersion(
@@ -177,6 +178,7 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
fleetAuthz: {
fleet: { allAgents: true },
},
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
})
.addVersion(
@@ -193,6 +195,7 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
fleetAuthz: {
fleet: { readEnrollmentTokens: true },
},
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
})
.addVersion(
@@ -209,6 +212,7 @@ export const registerRoutes = (router: FleetAuthzRouter) => {
fleetAuthz: {
fleet: { allAgents: true },
},
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
})
.addVersion(
diff --git a/x-pack/plugins/fleet/server/routes/epm/index.ts b/x-pack/plugins/fleet/server/routes/epm/index.ts
index c7eddee19b9e0..0e3c5e76eb825 100644
--- a/x-pack/plugins/fleet/server/routes/epm/index.ts
+++ b/x-pack/plugins/fleet/server/routes/epm/index.ts
@@ -659,6 +659,7 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType
fleetAuthz,
getRouteRequiredAuthz('get', EPM_API_ROUTES.INFO_PATTERN_DEPRECATED)
).granted,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
})
.addVersion(
@@ -687,6 +688,7 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType
fleetAuthz: {
integrations: { writePackageSettings: true },
},
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
})
.addVersion(
@@ -713,6 +715,7 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType
.post({
path: EPM_API_ROUTES.INSTALL_FROM_REGISTRY_PATTERN_DEPRECATED,
fleetAuthz: INSTALL_PACKAGES_AUTHZ,
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
})
.addVersion(
@@ -741,6 +744,7 @@ export const registerRoutes = (router: FleetAuthzRouter, config: FleetConfigType
fleetAuthz: {
integrations: { removePackages: true },
},
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
})
.addVersion(
diff --git a/x-pack/plugins/ml/server/routes/system.ts b/x-pack/plugins/ml/server/routes/system.ts
index 0804a8dc02348..bf4fa3161f5b9 100644
--- a/x-pack/plugins/ml/server/routes/system.ts
+++ b/x-pack/plugins/ml/server/routes/system.ts
@@ -202,8 +202,9 @@ export function systemRoutes(
options: {
tags: ['access:ml:canGetJobs'],
},
- deprecated: true,
summary: 'ES Search wrapper',
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
+ deprecated: true,
})
.addVersion(
{
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts
index 9603e9e9a6d48..2f6e46d1d7727 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/metadata/index.ts
@@ -95,6 +95,7 @@ export function registerEndpointRoutes(
access: 'public',
path: METADATA_TRANSFORMS_STATUS_ROUTE,
options: { authRequired: true, tags: ['access:securitySolution'] },
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
})
.addVersion(
diff --git a/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts b/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts
index a4853d9772ad7..677fb004ee862 100644
--- a/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts
+++ b/x-pack/plugins/security_solution/server/endpoint/routes/suggestions/index.ts
@@ -43,6 +43,7 @@ export function registerEndpointSuggestionsRoutes(
access: 'public',
path: SUGGESTIONS_ROUTE,
options: { authRequired: true, tags: ['access:securitySolution'] },
+ // @ts-expect-error TODO(https://github.com/elastic/kibana/issues/196095): Replace {RouteDeprecationInfo}
deprecated: true,
})
.addVersion(
diff --git a/x-pack/plugins/upgrade_assistant/README.md b/x-pack/plugins/upgrade_assistant/README.md
index 30b403f6d6230..2acac8e3e734d 100644
--- a/x-pack/plugins/upgrade_assistant/README.md
+++ b/x-pack/plugins/upgrade_assistant/README.md
@@ -17,7 +17,7 @@ When we want to enable ML model snapshot deprecation warnings again we need to c
* Migrating system indices (`featureSet.migrateSystemIndices`): Migrating system indices should only be enabled for major version upgrades. This config hides the second step from the UA UI for migrating system indices.
* Reindex corrective actions (`featureSet.reindexCorrectiveActions`): Deprecations with reindexing corrective actions are only enabled for major version upgrades. Currently, the reindex actions include some logic that is specific to the [8.0 upgrade](https://github.com/elastic/kibana/blob/main/x-pack/plugins/upgrade_assistant/server/lib/reindexing/index_settings.ts). End users could get into a bad situation if this is enabled before this logic is fixed.
-### Deprecations
+## Deprecations
There are three sources of deprecation information:
@@ -275,6 +275,47 @@ PUT .reporting-*/_settings
}
```
+#### Kibana API deprecations:
+Run kibana locally with the test example plugin that has deprecated routes
+```
+yarn start --plugin-path=examples/routing_example --plugin-path=examples/developer_examples
+```
+
+The following comprehensive deprecated routes examples are registered inside the folder: `examples/routing_example/server/routes/deprecated_routes`
+
+Run them in the console to trigger the deprecation condition so they show up in the UA:
+
+```
+# Versioned routes: Version 1 is deprecated
+GET kbn:/api/routing_example/d/versioned?apiVersion=1
+GET kbn:/api/routing_example/d/versioned?apiVersion=2
+
+# Non-versioned routes
+GET kbn:/api/routing_example/d/removed_route
+POST kbn:/api/routing_example/d/migrated_route
+{}
+```
+
+1. You can also mark as deprecated in the UA to remove the deprecation from the list.
+2. Check the telemetry response to see the reported data about the deprecated route.
+3. Calling version 2 of the API does not do anything since it is not deprecated unlike version `1` (`GET kbn:/api/routing_example/d/versioned?apiVersion=2`)
+4. Internally you can see the deprecations counters from the dev console by running the following:
+```
+GET .kibana_usage_counters/_search
+{
+ "query": {
+ "bool": {
+ "should": [
+ {"match": { "usage-counter.counterType": "deprecated_api_call:total"}},
+ {"match": { "usage-counter.counterType": "deprecated_api_call:resolved"}},
+ {"match": { "usage-counter.counterType": "deprecated_api_call:marked_as_resolved"}}
+ ]
+ }
+ }
+}
+
+```
+
For a complete list of Kibana deprecations, refer to the [8.0 Kibana deprecations meta issue](https://github.com/elastic/kibana/issues/109166).
### Errors
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/deprecation_details_flyout.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/deprecation_details_flyout.tsx
index a6ea9a26c9bb8..beb4c7c0c678c 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/deprecation_details_flyout.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/deprecation_details_flyout.tsx
@@ -50,6 +50,12 @@ const i18nTexts = {
defaultMessage: 'Quick resolve',
}
),
+ markAsResolvedButtonLabel: i18n.translate(
+ 'xpack.upgradeAssistant.kibanaDeprecations.flyout.quickResolveButtonLabel',
+ {
+ defaultMessage: 'Mark as Resolved',
+ }
+ ),
retryQuickResolveButtonLabel: i18n.translate(
'xpack.upgradeAssistant.kibanaDeprecations.flyout.retryQuickResolveButtonLabel',
{
@@ -97,7 +103,15 @@ const i18nTexts = {
),
};
-const getQuickResolveButtonLabel = (deprecationResolutionState?: DeprecationResolutionState) => {
+interface AvailableCorrectiveActions {
+ api: boolean;
+ manual: boolean;
+ markAsResolved: boolean;
+}
+const getQuickResolveButtonLabel = (
+ deprecationResolutionState: DeprecationResolutionState | undefined,
+ avilableCorrectiveActions: AvailableCorrectiveActions
+) => {
if (deprecationResolutionState?.resolveDeprecationStatus === 'in_progress') {
return i18nTexts.quickResolveInProgressButtonLabel;
}
@@ -110,7 +124,13 @@ const getQuickResolveButtonLabel = (deprecationResolutionState?: DeprecationReso
return i18nTexts.retryQuickResolveButtonLabel;
}
- return i18nTexts.quickResolveButtonLabel;
+ if (avilableCorrectiveActions.api) {
+ return i18nTexts.quickResolveButtonLabel;
+ }
+
+ if (avilableCorrectiveActions.markAsResolved) {
+ return i18nTexts.markAsResolvedButtonLabel;
+ }
};
export const DeprecationDetailsFlyout = ({
@@ -120,9 +140,19 @@ export const DeprecationDetailsFlyout = ({
deprecationResolutionState,
}: DeprecationDetailsFlyoutProps) => {
const { documentationUrl, message, correctiveActions, title } = deprecation;
+ const messages = Array.isArray(message) ? message : [message];
+
const isCurrent = deprecationResolutionState?.id === deprecation.id;
+ const avilableCorrectiveActions: AvailableCorrectiveActions = {
+ api: !!correctiveActions.api,
+ manual: correctiveActions.manualSteps && correctiveActions.manualSteps.length > 0,
+ markAsResolved: !!correctiveActions.mark_as_resolved_api,
+ };
const isResolved = isCurrent && deprecationResolutionState?.resolveDeprecationStatus === 'ok';
+ const hasResolveButton =
+ avilableCorrectiveActions.api || avilableCorrectiveActions.markAsResolved;
+
const onResolveDeprecation = useCallback(() => {
uiMetricService.trackUiMetric(METRIC_TYPE.CLICK, UIM_KIBANA_QUICK_RESOLVE_CLICK);
resolveDeprecation(deprecation);
@@ -155,7 +185,11 @@ export const DeprecationDetailsFlyout = ({
)}
- {message}
+ {messages.map((m, i) => (
+
+ {m}
+
+ ))}
{documentationUrl && (
@@ -221,7 +255,7 @@ export const DeprecationDetailsFlyout = ({
{/* Only show the "Quick resolve" button if deprecation supports it and deprecation is not yet resolved */}
- {correctiveActions.api && !isResolved && (
+ {hasResolveButton && !isResolved && (
- {getQuickResolveButtonLabel(deprecationResolutionState)}
+ {getQuickResolveButtonLabel(deprecationResolutionState, avilableCorrectiveActions)}
)}
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx
index d76f1afa9e612..0d433a59ee2d9 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/kibana_deprecations.tsx
@@ -126,9 +126,9 @@ export const KibanaDeprecationsList = ({
const [flyoutContent, setFlyoutContent] = useState(
undefined
);
- const [deprecationResolutionState, setDeprecationResolutionState] = useState<
- DeprecationResolutionState | undefined
- >(undefined);
+ const [deprecationResolutionStates, setDeprecationResolutionStates] = useState<
+ Record
+ >({});
const {
services: {
@@ -194,17 +194,25 @@ export const KibanaDeprecationsList = ({
const resolveDeprecation = useCallback(
async (deprecationDetails: KibanaDeprecationDetails) => {
- setDeprecationResolutionState({
- id: deprecationDetails.id,
- resolveDeprecationStatus: 'in_progress',
+ setDeprecationResolutionStates((states) => {
+ states[deprecationDetails.id] = {
+ id: deprecationDetails.id,
+ resolveDeprecationStatus: 'in_progress',
+ };
+
+ return states;
});
const response = await deprecations.resolveDeprecation(deprecationDetails);
- setDeprecationResolutionState({
- id: deprecationDetails.id,
- resolveDeprecationStatus: response.status,
- resolveDeprecationError: response.status === 'fail' ? response.reason : undefined,
+ setDeprecationResolutionStates((states) => {
+ states[deprecationDetails.id] = {
+ id: deprecationDetails.id,
+ resolveDeprecationStatus: response.status,
+ resolveDeprecationError: response.status === 'fail' ? response.reason : undefined,
+ };
+
+ return states;
});
closeFlyout();
@@ -221,10 +229,7 @@ export const KibanaDeprecationsList = ({
deprecation: flyoutContent,
closeFlyout,
resolveDeprecation,
- deprecationResolutionState:
- deprecationResolutionState && flyoutContent.id === deprecationResolutionState.id
- ? deprecationResolutionState
- : undefined,
+ deprecationResolutionState: deprecationResolutionStates[flyoutContent.id],
},
flyoutProps: {
onClose: closeFlyout,
@@ -236,7 +241,7 @@ export const KibanaDeprecationsList = ({
}, [
addContentToGlobalFlyout,
closeFlyout,
- deprecationResolutionState,
+ deprecationResolutionStates,
flyoutContent,
resolveDeprecation,
]);
@@ -310,7 +315,7 @@ export const KibanaDeprecationsList = ({
deprecations={kibanaDeprecations}
reload={getAllDeprecations}
toggleFlyout={toggleFlyout}
- deprecationResolutionState={deprecationResolutionState}
+ deprecationResolutionStates={deprecationResolutionStates}
/>
);
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..8d223dedca490 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',
}
),
+ apiDeprecationTypeCellLabel: i18n.translate(
+ 'xpack.upgradeAssistant.kibanaDeprecations.table.apiDeprecationTypeCellLabel',
+ {
+ defaultMessage: 'API',
+ }
+ ),
unknownDeprecationTypeCellLabel: i18n.translate(
'xpack.upgradeAssistant.kibanaDeprecations.table.unknownDeprecationTypeCellLabel',
{
@@ -86,14 +92,14 @@ interface Props {
deprecations?: KibanaDeprecationDetails[];
reload: () => void;
toggleFlyout: (newFlyoutContent?: KibanaDeprecationDetails) => void;
- deprecationResolutionState?: DeprecationResolutionState;
+ deprecationResolutionStates: Record;
}
export const KibanaDeprecationsTable: React.FunctionComponent = ({
deprecations,
reload,
toggleFlyout,
- deprecationResolutionState,
+ deprecationResolutionStates,
}) => {
const columns: Array> = [
{
@@ -135,6 +141,8 @@ export const KibanaDeprecationsTable: React.FunctionComponent = ({
return i18nTexts.configDeprecationTypeCellLabel;
case 'feature':
return i18nTexts.featureDeprecationTypeCellLabel;
+ case 'api':
+ return i18nTexts.apiDeprecationTypeCellLabel;
case 'uncategorized':
default:
return i18nTexts.unknownDeprecationTypeCellLabel;
@@ -155,7 +163,8 @@ export const KibanaDeprecationsTable: React.FunctionComponent = ({
);
},
@@ -191,6 +200,10 @@ export const KibanaDeprecationsTable: React.FunctionComponent = ({
value: 'feature',
name: i18nTexts.featureDeprecationTypeCellLabel,
},
+ {
+ value: 'api',
+ name: i18nTexts.apiDeprecationTypeCellLabel,
+ },
{
value: 'uncategorized',
name: i18nTexts.unknownDeprecationTypeCellLabel,
diff --git a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/resolution_table_cell.tsx b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/resolution_table_cell.tsx
index 373c9e7b43f52..502c31ae90744 100644
--- a/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/resolution_table_cell.tsx
+++ b/x-pack/plugins/upgrade_assistant/public/application/components/kibana_deprecations/resolution_table_cell.tsx
@@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n';
import type { DeprecationResolutionState } from './kibana_deprecations';
-const i18nTexts = {
+const manualI18nTexts = {
manualCellLabel: i18n.translate(
'xpack.upgradeAssistant.kibanaDeprecations.table.manualCellLabel',
{
@@ -32,31 +32,34 @@ const i18nTexts = {
defaultMessage: 'This issue needs to be resolved manually.',
}
),
- automatedCellLabel: i18n.translate(
+};
+
+const automatedI18nTexts = {
+ resolutionTypeCellLabel: i18n.translate(
'xpack.upgradeAssistant.kibanaDeprecations.table.automatedCellLabel',
{
defaultMessage: 'Automated',
}
),
- automationInProgressCellLabel: i18n.translate(
+ resolutionProgressCellLabel: i18n.translate(
'xpack.upgradeAssistant.kibanaDeprecations.table.automationInProgressCellLabel',
{
defaultMessage: 'Resolution in progress…',
}
),
- automationCompleteCellLabel: i18n.translate(
+ resolutionCompleteCellLabel: i18n.translate(
'xpack.upgradeAssistant.kibanaDeprecations.table.automationCompleteCellLabel',
{
defaultMessage: 'Resolved',
}
),
- automationFailedCellLabel: i18n.translate(
+ resolutionFailedCellLabel: i18n.translate(
'xpack.upgradeAssistant.kibanaDeprecations.table.automationFailedCellLabel',
{
defaultMessage: 'Resolution failed',
}
),
- automatedCellTooltipLabel: i18n.translate(
+ resolutionCellTooltipLabel: i18n.translate(
'xpack.upgradeAssistant.kibanaDeprecations.table.automatedCellTooltipLabel',
{
defaultMessage: 'This issue can be resolved automatically.',
@@ -64,18 +67,56 @@ const i18nTexts = {
),
};
+const markAsResolvedI18nTexts = {
+ resolutionTypeCellLabel: i18n.translate(
+ 'xpack.upgradeAssistant.kibanaDeprecations.table.markAsResolvedCellLabel',
+ {
+ defaultMessage: 'Mark as resolved',
+ }
+ ),
+ resolutionProgressCellLabel: i18n.translate(
+ 'xpack.upgradeAssistant.kibanaDeprecations.table.markAsResolvedInProgressCellLabel',
+ {
+ defaultMessage: 'Marking as resolved…',
+ }
+ ),
+ resolutionCompleteCellLabel: i18n.translate(
+ 'xpack.upgradeAssistant.kibanaDeprecations.table.markAsResolvedCompleteCellLabel',
+ {
+ defaultMessage: 'Marked as resolved',
+ }
+ ),
+ resolutionFailedCellLabel: i18n.translate(
+ 'xpack.upgradeAssistant.kibanaDeprecations.table.markAsResolvedFailedCellLabel',
+ {
+ defaultMessage: 'Failed to mark as resolved',
+ }
+ ),
+ resolutionCellTooltipLabel: i18n.translate(
+ 'xpack.upgradeAssistant.kibanaDeprecations.table.markAsResolvedCellTooltipLabel',
+ {
+ defaultMessage: 'This issue can be marked as resolved.',
+ }
+ ),
+};
+
interface Props {
deprecationId: string;
isAutomated: boolean;
+ canBeMarkedAsResolved: boolean;
deprecationResolutionState?: DeprecationResolutionState;
}
export const ResolutionTableCell: React.FunctionComponent = ({
deprecationId,
isAutomated,
+ canBeMarkedAsResolved,
deprecationResolutionState,
}) => {
- if (isAutomated) {
+ if (isAutomated || canBeMarkedAsResolved) {
+ const resolutionI18nTexts = isAutomated ? automatedI18nTexts : markAsResolvedI18nTexts;
+ const euiIconType = isAutomated ? 'indexSettings' : 'clickLeft';
+
if (deprecationResolutionState?.id === deprecationId) {
const { resolveDeprecationStatus } = deprecationResolutionState;
@@ -87,7 +128,7 @@ export const ResolutionTableCell: React.FunctionComponent = ({
- {i18nTexts.automationInProgressCellLabel}
+ {resolutionI18nTexts.resolutionProgressCellLabel}
);
@@ -98,7 +139,7 @@ export const ResolutionTableCell: React.FunctionComponent = ({
- {i18nTexts.automationFailedCellLabel}
+ {resolutionI18nTexts.resolutionFailedCellLabel}
);
@@ -110,7 +151,7 @@ export const ResolutionTableCell: React.FunctionComponent = ({
- {i18nTexts.automationCompleteCellLabel}
+ {resolutionI18nTexts.resolutionCompleteCellLabel}
);
@@ -118,13 +159,13 @@ export const ResolutionTableCell: React.FunctionComponent = ({
}
return (
-
+
-
+
- {i18nTexts.automatedCellLabel}
+ {resolutionI18nTexts.resolutionTypeCellLabel}
@@ -134,11 +175,11 @@ export const ResolutionTableCell: React.FunctionComponent = ({
return (
- {i18nTexts.manualCellLabel}
+ {manualI18nTexts.manualCellLabel}
);
diff --git a/x-pack/test/tsconfig.json b/x-pack/test/tsconfig.json
index 03fbddf161f00..2ba14ceb1218c 100644
--- a/x-pack/test/tsconfig.json
+++ b/x-pack/test/tsconfig.json
@@ -185,6 +185,8 @@
"@kbn/cloud-security-posture-common",
"@kbn/saved-objects-management-plugin",
"@kbn/alerting-types",
- "@kbn/ai-assistant-common"
+ "@kbn/ai-assistant-common",
+ "@kbn/core-deprecations-common",
+ "@kbn/usage-collection-plugin"
]
}
diff --git a/x-pack/test/upgrade_assistant_integration/config.js b/x-pack/test/upgrade_assistant_integration/config.ts
similarity index 70%
rename from x-pack/test/upgrade_assistant_integration/config.js
rename to x-pack/test/upgrade_assistant_integration/config.ts
index 9529e4bc568d3..0794f4d0b9ada 100644
--- a/x-pack/test/upgrade_assistant_integration/config.js
+++ b/x-pack/test/upgrade_assistant_integration/config.ts
@@ -6,8 +6,10 @@
*/
import { commonFunctionalServices } from '@kbn/ftr-common-functional-services';
+import { FtrConfigProviderContext } from '@kbn/test';
+import path from 'node:path';
-export default async function ({ readConfigFile }) {
+export default async function ({ readConfigFile }: FtrConfigProviderContext) {
// Read the Kibana API integration tests config file so that we can utilize its services.
const kibanaAPITestsConfig = await readConfigFile(
require.resolve('@kbn/test-suites-src/api_integration/config')
@@ -26,7 +28,14 @@ export default async function ({ readConfigFile }) {
junit: {
reportName: 'X-Pack Upgrade Assistant Integration Tests',
},
- kbnTestServer: xPackFunctionalTestsConfig.get('kbnTestServer'),
+ kbnTestServer: {
+ ...xPackFunctionalTestsConfig.get('kbnTestServer'),
+ serverArgs: [
+ ...xPackFunctionalTestsConfig.get('kbnTestServer.serverArgs'),
+ `--plugin-path=${path.resolve(__dirname, '../../../examples/routing_example')}`,
+ `--plugin-path=${path.resolve(__dirname, '../../../examples/developer_examples')}`,
+ ],
+ },
esTestCluster: {
...xPackFunctionalTestsConfig.get('esTestCluster'),
// this archive can not be loaded into 8.0+
diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts
new file mode 100644
index 0000000000000..f146bf38f5f26
--- /dev/null
+++ b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/api_deprecations.ts
@@ -0,0 +1,162 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import expect from '@kbn/expect';
+import { expect as expectExpect } from 'expect';
+import type { DomainDeprecationDetails } from '@kbn/core-deprecations-common';
+import { ApiDeprecationDetails } from '@kbn/core-deprecations-common/src/types';
+import { setTimeout as setTimeoutAsync } from 'timers/promises';
+import { UsageCountersSavedObject } from '@kbn/usage-collection-plugin/server';
+import _ from 'lodash';
+import { FtrProviderContext } from '../../common/ftr_provider_context';
+
+interface DomainApiDeprecationDetails extends ApiDeprecationDetails {
+ domainId: string;
+}
+
+const getApiDeprecations = (allDeprecations: DomainDeprecationDetails[]) => {
+ return allDeprecations.filter(
+ (deprecation) => deprecation.deprecationType === 'api'
+ ) as unknown as DomainApiDeprecationDetails[];
+};
+
+export default function ({ getService }: FtrProviderContext) {
+ const supertest = getService('supertest');
+ const esArchiver = getService('esArchiver');
+ const retry = getService('retry');
+ const es = getService('es');
+
+ describe('Kibana API Deprecations', () => {
+ before(async () => {
+ // await kibanaServer.savedObjects.cleanStandardList();
+ await esArchiver.emptyKibanaIndex();
+ });
+ it('returns does not return api deprecations if the routes are not called', async () => {
+ const { deprecations } = (await supertest.get(`/api/deprecations/`).expect(200)).body;
+ const apiDeprecations = getApiDeprecations(deprecations);
+ expect(apiDeprecations.length).to.equal(0);
+ });
+
+ it('returns deprecated APIs when the api is called', async () => {
+ await supertest.get(`/api/routing_example/d/removed_route`).expect(200);
+
+ // sleep a little until the usage counter is synced into ES
+ await setTimeoutAsync(3000);
+ await retry.tryForTime(
+ 15 * 1000,
+ async () => {
+ const { deprecations } = (await supertest.get(`/api/deprecations/`).expect(200)).body;
+ const apiDeprecations = getApiDeprecations(deprecations);
+ expect(apiDeprecations.length).to.equal(1);
+
+ expectExpect(apiDeprecations[0].correctiveActions.mark_as_resolved_api).toEqual({
+ routePath: '/api/routing_example/d/removed_route',
+ routeMethod: 'get',
+ apiTotalCalls: 1,
+ totalMarkedAsResolved: 0,
+ timestamp: expectExpect.any(String),
+ });
+
+ expectExpect(apiDeprecations[0].domainId).toEqual('core.api_deprecations');
+ expectExpect(apiDeprecations[0].apiId).toEqual(
+ 'unversioned|get|/api/routing_example/d/removed_route'
+ );
+ expectExpect(apiDeprecations[0].title).toEqual(
+ 'The "GET /api/routing_example/d/removed_route" route is removed'
+ );
+ },
+ undefined,
+ 2000
+ );
+ });
+
+ it('no longer returns deprecated API when it is marked as resolved', async () => {
+ await supertest
+ .post(`/api/deprecations/mark_as_resolved`)
+ .set('kbn-xsrf', 'xxx')
+ .send({
+ domainId: 'core.api_deprecations',
+ routePath: '/api/routing_example/d/removed_route',
+ routeMethod: 'get',
+ incrementBy: 1,
+ })
+ .expect(200);
+
+ // sleep a little until the usage counter is synced into ES
+ await setTimeoutAsync(5000);
+ await retry.tryForTime(15 * 1000, async () => {
+ const { deprecations } = (await supertest.get(`/api/deprecations/`).expect(200)).body;
+ const apiDeprecations = getApiDeprecations(deprecations);
+ expect(apiDeprecations.length).to.equal(0);
+ });
+ });
+
+ it('returns deprecated API when it is called again after resolved, but with a different message', async () => {
+ await supertest.get(`/api/routing_example/d/removed_route`).expect(200);
+
+ // sleep a little until the usage counter is synced into ES
+ await setTimeoutAsync(3000);
+ await retry.tryForTime(
+ 15 * 1000,
+ async () => {
+ const { deprecations } = (await supertest.get(`/api/deprecations/`).expect(200)).body;
+ const apiDeprecations = getApiDeprecations(deprecations);
+ expect(apiDeprecations.length).to.equal(1);
+
+ expectExpect(apiDeprecations[0].correctiveActions.mark_as_resolved_api).toEqual({
+ routePath: '/api/routing_example/d/removed_route',
+ routeMethod: 'get',
+ apiTotalCalls: 2,
+ totalMarkedAsResolved: 1,
+ timestamp: expectExpect.any(String),
+ });
+ },
+ undefined,
+ 2000
+ );
+ });
+
+ it('keeps track of all counters via saved objects and core usage counters', async () => {
+ const should = ['total', 'resolved', 'marked_as_resolved'].map((type) => ({
+ match: { 'usage-counter.counterType': `deprecated_api_call:${type}` },
+ }));
+
+ const { hits } = await es.search<{ 'usage-counter': UsageCountersSavedObject }>({
+ index: '.kibana_usage_counters',
+ body: {
+ query: { bool: { should } },
+ },
+ });
+
+ expect(hits.hits.length).to.equal(3);
+ const counters = hits.hits.map((hit) => hit._source!['usage-counter']).sort();
+ expectExpect(_.sortBy(counters, 'counterType')).toEqual([
+ {
+ count: 1,
+ counterName: 'unversioned|get|/api/routing_example/d/removed_route',
+ counterType: 'deprecated_api_call:marked_as_resolved',
+ domainId: 'core',
+ source: 'server',
+ },
+ {
+ count: 1,
+ counterName: 'unversioned|get|/api/routing_example/d/removed_route',
+ counterType: 'deprecated_api_call:resolved',
+ domainId: 'core',
+ source: 'server',
+ },
+ {
+ count: 2,
+ counterName: 'unversioned|get|/api/routing_example/d/removed_route',
+ counterType: 'deprecated_api_call:total',
+ domainId: 'core',
+ source: 'server',
+ },
+ ]);
+ });
+ });
+}
diff --git a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.ts
similarity index 64%
rename from x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js
rename to x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.ts
index eb09d24b79b6a..2aaddc7d6f669 100644
--- a/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.js
+++ b/x-pack/test/upgrade_assistant_integration/upgrade_assistant/index.ts
@@ -5,8 +5,11 @@
* 2.0.
*/
-export default function ({ loadTestFile }) {
+import { FtrProviderContext } from '../../common/ftr_provider_context';
+
+export default function ({ loadTestFile }: FtrProviderContext) {
describe('upgrade assistant', function () {
loadTestFile(require.resolve('./reindexing'));
+ loadTestFile(require.resolve('./api_deprecations'));
});
}
From ae0ac7410de3cfc311d3750d957a81e2a5785d33 Mon Sep 17 00:00:00 2001
From: Krzysztof Kowalczyk
Date: Tue, 22 Oct 2024 19:42:20 +0200
Subject: [PATCH 021/146] [Reporting] Add searchSourceStart.create error
handling (#197238)
## Summary
This PR adds a new type of error (`ReportingSavedObjectNotFoundError`)
which gets thrown when passed in saved object doesn't eixst.
This produces a log like this:
```
[2024-10-22T15:09:26.768+02:00][ERROR][plugins.reporting.runTask] Error: ReportingError(code: reporting_saved_object_not_found) "Error: Saved object [index-pattern/ff959d40-b880-11e8-a6d9-e546fe2bba5f] not found"
```
Closes: #191548
Closes: #196620
---------
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
---
packages/kbn-generate-csv/src/generate_csv.ts | 19 ++++++++++++++++++-
packages/kbn-generate-csv/tsconfig.json | 1 +
packages/kbn-reporting/common/errors.ts | 7 +++++++
3 files changed, 26 insertions(+), 1 deletion(-)
diff --git a/packages/kbn-generate-csv/src/generate_csv.ts b/packages/kbn-generate-csv/src/generate_csv.ts
index cafc6600f51a3..5ed92df84c581 100644
--- a/packages/kbn-generate-csv/src/generate_csv.ts
+++ b/packages/kbn-generate-csv/src/generate_csv.ts
@@ -26,10 +26,12 @@ import {
byteSizeValueToNumber,
CancellationToken,
ReportingError,
+ ReportingSavedObjectNotFoundError,
} from '@kbn/reporting-common';
import type { TaskInstanceFields, TaskRunResult } from '@kbn/reporting-common/types';
import type { ReportingConfigType } from '@kbn/reporting-server';
+import { TaskErrorSource, createTaskRunError } from '@kbn/task-manager-plugin/server';
import { CONTENT_TYPE_CSV } from '../constants';
import type { JobParamsCSV } from '../types';
import { getExportSettings, type CsvExportSettings } from './lib/get_export_settings';
@@ -235,6 +237,21 @@ export class CsvGenerator {
public async generateData(): Promise {
const logger = this.logger;
+
+ const createSearchSource = async () => {
+ try {
+ const source = await this.dependencies.searchSourceStart.create(this.job.searchSource);
+ return source;
+ } catch (err) {
+ // Saved object not found
+ if (err?.output?.statusCode === 404) {
+ const reportingError = new ReportingSavedObjectNotFoundError(err);
+ throw createTaskRunError(reportingError, TaskErrorSource.USER);
+ }
+ throw err;
+ }
+ };
+
const [settings, searchSource] = await Promise.all([
getExportSettings(
this.clients.uiSettings,
@@ -243,7 +260,7 @@ export class CsvGenerator {
this.job.browserTimezone,
logger
),
- this.dependencies.searchSourceStart.create(this.job.searchSource),
+ createSearchSource(),
]);
const { startedAt, retryAt } = this.taskInstanceFields;
diff --git a/packages/kbn-generate-csv/tsconfig.json b/packages/kbn-generate-csv/tsconfig.json
index b57990c20eb4a..4216438b6689a 100644
--- a/packages/kbn-generate-csv/tsconfig.json
+++ b/packages/kbn-generate-csv/tsconfig.json
@@ -30,5 +30,6 @@
"@kbn/es-types",
"@kbn/data-views-plugin",
"@kbn/search-types",
+ "@kbn/task-manager-plugin",
]
}
diff --git a/packages/kbn-reporting/common/errors.ts b/packages/kbn-reporting/common/errors.ts
index 9f45a1b6ae1d5..45a299115bf1b 100644
--- a/packages/kbn-reporting/common/errors.ts
+++ b/packages/kbn-reporting/common/errors.ts
@@ -152,3 +152,10 @@ export class VisualReportingSoftDisabledError extends ReportingError {
});
}
}
+
+export class ReportingSavedObjectNotFoundError extends ReportingError {
+ static code = 'reporting_saved_object_not_found_error' as const;
+ public get code(): string {
+ return ReportingSavedObjectNotFoundError.code;
+ }
+}
From c25599ee983c65357a9d63eb843cec101c27f1ea Mon Sep 17 00:00:00 2001
From: Saarika Bhasi <55930906+saarikabhasi@users.noreply.github.com>
Date: Tue, 22 Oct 2024 13:59:28 -0400
Subject: [PATCH 022/146] [Search] Enable semantic_text feature in es3
(#197140)
## Summary
Semantic text feature was disabled by default due to ML node unable to
scale down. With the relevant
[PR](https://github.com/elastic/elasticsearch/pull/114323) & subsequent
[fix](https://github.com/elastic/elasticsearch/pull/115189) merged, ML
node now auto scales when there is no activity. Therefore enabling
semantic_text feature in es3.
### Testing instructions
* start serverless instance
* visit index management index details page -> mappings
* Click Add field
* Confirm `semantic_text` is shown in the field type form
---
config/serverless.es.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config/serverless.es.yml b/config/serverless.es.yml
index eb3af92c89963..b3b953e5316ac 100644
--- a/config/serverless.es.yml
+++ b/config/serverless.es.yml
@@ -120,4 +120,4 @@ xpack.searchInferenceEndpoints.ui.enabled: false
xpack.search.notebooks.catalog.url: https://elastic-enterprise-search.s3.us-east-2.amazonaws.com/serverless/catalog.json
# Semantic text UI
-xpack.index_management.dev.enableSemanticText: false
+xpack.index_management.dev.enableSemanticText: true
From 848c9f48dc101744c8fae69951916dc511dfdc8a Mon Sep 17 00:00:00 2001
From: Vadim Kibana <82822460+vadimkibana@users.noreply.github.com>
Date: Tue, 22 Oct 2024 20:05:09 +0200
Subject: [PATCH 023/146] [ES|QL] Normalize multiplication by one when
pretty-printing (#197182)
## Summary
Partially addresses https://github.com/elastic/kibana/issues/189258
This change will likely supercede
https://github.com/elastic/kibana/pull/196817
When parsing, currently ES|QL parsers adds extraneous multiply by 1 or
multiply by -1 nodes when parsing arithmetic unary expressions.
For example, `-(x)` is parsed as `-1 * x`.
This change, reverts these when pretty-printing using the
`BasicPrettyPrinter`: `-1 * x` is pretty printed as `-x`.
### Checklist
Delete any items that are not applicable to this PR.
- [x] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
### For maintainers
- [x] This was checked for breaking API changes and was [labeled
appropriately](https://www.elastic.co/guide/en/kibana/master/contributing.html#_add_your_labels)
---
packages/kbn-esql-ast/src/ast/helpers.ts | 27 ++++++-
.../__tests__/basic_pretty_printer.test.ts | 62 ++++++++++++++-
.../src/pretty_print/basic_pretty_printer.ts | 79 ++++++++++++++++++-
.../kbn-esql-ast/src/pretty_print/helpers.ts | 2 +-
packages/kbn-esql-ast/src/visitor/utils.ts | 4 +
5 files changed, 169 insertions(+), 5 deletions(-)
diff --git a/packages/kbn-esql-ast/src/ast/helpers.ts b/packages/kbn-esql-ast/src/ast/helpers.ts
index 9ca49dcb38822..74a7b5c0991e8 100644
--- a/packages/kbn-esql-ast/src/ast/helpers.ts
+++ b/packages/kbn-esql-ast/src/ast/helpers.ts
@@ -7,11 +7,22 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
-import { ESQLAstNode, ESQLBinaryExpression, ESQLFunction } from '../types';
+import type {
+ ESQLAstNode,
+ ESQLBinaryExpression,
+ ESQLColumn,
+ ESQLFunction,
+ ESQLIntegerLiteral,
+ ESQLLiteral,
+ ESQLProperNode,
+} from '../types';
import { BinaryExpressionGroup } from './constants';
+export const isProperNode = (node: unknown): node is ESQLProperNode =>
+ !!node && typeof node === 'object' && !Array.isArray(node);
+
export const isFunctionExpression = (node: unknown): node is ESQLFunction =>
- !!node && typeof node === 'object' && !Array.isArray(node) && (node as any).type === 'function';
+ isProperNode(node) && node.type === 'function';
/**
* Returns true if the given node is a binary expression, i.e. an operator
@@ -28,6 +39,18 @@ export const isFunctionExpression = (node: unknown): node is ESQLFunction =>
export const isBinaryExpression = (node: unknown): node is ESQLBinaryExpression =>
isFunctionExpression(node) && node.subtype === 'binary-expression';
+export const isLiteral = (node: unknown): node is ESQLLiteral =>
+ isProperNode(node) && node.type === 'literal';
+
+export const isIntegerLiteral = (node: unknown): node is ESQLIntegerLiteral =>
+ isLiteral(node) && node.literalType === 'integer';
+
+export const isDoubleLiteral = (node: unknown): node is ESQLIntegerLiteral =>
+ isLiteral(node) && node.literalType === 'double';
+
+export const isColumn = (node: unknown): node is ESQLColumn =>
+ isProperNode(node) && node.type === 'column';
+
/**
* Returns the group of a binary expression:
*
diff --git a/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts b/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts
index 20db9e729f094..9e21c45f75b4b 100644
--- a/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts
+++ b/packages/kbn-esql-ast/src/pretty_print/__tests__/basic_pretty_printer.test.ts
@@ -16,7 +16,7 @@ const reprint = (src: string) => {
const { root } = parse(src);
const text = BasicPrettyPrinter.print(root);
- // console.log(JSON.stringify(ast, null, 2));
+ // console.log(JSON.stringify(root, null, 2));
return { text };
};
@@ -194,6 +194,66 @@ describe('single line query', () => {
expect(text).toBe('ROW NOT a');
});
+
+ test('negative numbers', () => {
+ const { text } = reprint('ROW -1');
+
+ expect(text).toBe('ROW -1');
+ });
+
+ test('negative numbers in brackets', () => {
+ const { text } = reprint('ROW -(1)');
+
+ expect(text).toBe('ROW -1');
+ });
+
+ test('negative column names', () => {
+ const { text } = reprint('ROW -col');
+
+ expect(text).toBe('ROW -col');
+ });
+
+ test('plus unary expression', () => {
+ const { text } = reprint('ROW +(23)');
+
+ expect(text).toBe('ROW 23');
+ });
+
+ test('chained multiple unary expressions', () => {
+ const { text } = reprint('ROW ----+-+(23)');
+
+ expect(text).toBe('ROW -23');
+ });
+
+ test('before another expression', () => {
+ const { text } = reprint('ROW ----+-+(1 + 1)');
+
+ expect(text).toBe('ROW -(1 + 1)');
+ });
+
+ test('negative one from the right side', () => {
+ const { text } = reprint('ROW 2 * -1');
+
+ expect(text).toBe('ROW -2');
+ });
+
+ test('two minuses is plus', () => {
+ const { text } = reprint('ROW --123');
+
+ expect(text).toBe('ROW 123');
+ });
+
+ test('two minuses is plus (float)', () => {
+ const { text } = reprint('ROW --1.23');
+
+ expect(text).toBe('ROW 1.23');
+ });
+
+ test('two minuses is plus (with brackets)', () => {
+ const { text } = reprint('ROW --(123)');
+
+ expect(text).toBe('ROW 123');
+ });
});
describe('postfix unary expression', () => {
diff --git a/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts b/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts
index ec744c65f636e..2f1e3439cd3a3 100644
--- a/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts
+++ b/packages/kbn-esql-ast/src/pretty_print/basic_pretty_printer.ts
@@ -7,9 +7,18 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
-import { binaryExpressionGroup } from '../ast/helpers';
+import {
+ binaryExpressionGroup,
+ isBinaryExpression,
+ isColumn,
+ isDoubleLiteral,
+ isIntegerLiteral,
+ isLiteral,
+ isProperNode,
+} from '../ast/helpers';
import { ESQLAstBaseItem, ESQLAstCommand, ESQLAstQueryExpression } from '../types';
import { ESQLAstExpressionNode, Visitor } from '../visitor';
+import { resolveItem } from '../visitor/utils';
import { LeafPrinter } from './leaf_printer';
export interface BasicPrettyPrinterOptions {
@@ -152,6 +161,62 @@ export class BasicPrettyPrinter {
return formatted;
}
+ protected simplifyMultiplicationByOne(
+ node: ESQLAstExpressionNode,
+ minusCount: number = 0
+ ): string | undefined {
+ if (isBinaryExpression(node) && node.name === '*') {
+ let [left, right] = node.args;
+ left = resolveItem(left);
+ right = resolveItem(right);
+
+ if (isProperNode(left) && isProperNode(right)) {
+ if (!!left.formatting || !!right.formatting) {
+ return undefined;
+ }
+ if (isIntegerLiteral(left)) {
+ if (left.value === 1) {
+ return this.simplifyMultiplicationByOne(right, minusCount);
+ } else if (left.value === -1) {
+ return this.simplifyMultiplicationByOne(right, minusCount + 1);
+ }
+ }
+ if (isIntegerLiteral(right)) {
+ if (right.value === 1) {
+ return this.simplifyMultiplicationByOne(left, minusCount);
+ } else if (right.value === -1) {
+ return this.simplifyMultiplicationByOne(left, minusCount + 1);
+ }
+ }
+ return undefined;
+ } else {
+ return undefined;
+ }
+ }
+
+ const isNegative = minusCount % 2 === 1;
+
+ if (isNegative && (isIntegerLiteral(node) || isDoubleLiteral(node)) && node.value < 0) {
+ return BasicPrettyPrinter.expression(
+ {
+ ...node,
+ value: Math.abs(node.value),
+ },
+ this.opts
+ );
+ }
+
+ let expression = BasicPrettyPrinter.expression(node, this.opts);
+ const sign = isNegative ? '-' : '';
+ const needsBrackets = !!sign && !isColumn(node) && !isLiteral(node);
+
+ if (needsBrackets) {
+ expression = `(${expression})`;
+ }
+
+ return sign ? `${sign}${expression}` : expression;
+ }
+
protected readonly visitor: Visitor = new Visitor()
.on('visitExpression', (ctx) => {
return '';
@@ -237,6 +302,18 @@ export class BasicPrettyPrinter {
const groupLeft = binaryExpressionGroup(left);
const groupRight = binaryExpressionGroup(right);
+ if (
+ node.name === '*' &&
+ ((isIntegerLiteral(left) && Math.abs(left.value) === 1) ||
+ (isIntegerLiteral(right) && Math.abs(right.value) === 1))
+ ) {
+ const formatted = this.simplifyMultiplicationByOne(node);
+
+ if (formatted) {
+ return formatted;
+ }
+ }
+
let leftFormatted = ctx.visitArgument(0);
let rightFormatted = ctx.visitArgument(1);
diff --git a/packages/kbn-esql-ast/src/pretty_print/helpers.ts b/packages/kbn-esql-ast/src/pretty_print/helpers.ts
index f9d9daac84e7a..1b4a75a119cb2 100644
--- a/packages/kbn-esql-ast/src/pretty_print/helpers.ts
+++ b/packages/kbn-esql-ast/src/pretty_print/helpers.ts
@@ -7,7 +7,7 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/
-import { ESQLAstBaseItem, ESQLProperNode } from '../types';
+import type { ESQLAstBaseItem, ESQLProperNode } from '../types';
import { Walker } from '../walker';
export interface QueryPrettyPrintStats {
diff --git a/packages/kbn-esql-ast/src/visitor/utils.ts b/packages/kbn-esql-ast/src/visitor/utils.ts
index 0dc95b73cf9d7..da8544ef46c90 100644
--- a/packages/kbn-esql-ast/src/visitor/utils.ts
+++ b/packages/kbn-esql-ast/src/visitor/utils.ts
@@ -36,6 +36,10 @@ export const firstItem = (items: ESQLAstItem[]): ESQLSingleAstItem | undefined =
}
};
+export const resolveItem = (items: ESQLAstItem | ESQLAstItem[]): ESQLAstItem => {
+ return Array.isArray(items) ? resolveItem(items[0]) : items;
+};
+
/**
* Returns the last normalized "single item" from the "item" list.
*
From 6a764e726569ed2ec5cd1bccd113b20212a8fdab Mon Sep 17 00:00:00 2001
From: Samiul Monir <150824886+Samiul-TheSoccerFan@users.noreply.github.com>
Date: Tue, 22 Oct 2024 14:17:15 -0400
Subject: [PATCH 024/146] [Search: Inference Management UI] Fixing design
issues and Removing Duplicate Service Name (#196431)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
## Summary
This PR includes:
- Addressed the comments from
https://github.com/elastic/kibana/pull/193642
- Fix CSS to display usage items of inference endpoints
![Screenshot 2024-10-22 at 9 50
47 AM](https://github.com/user-attachments/assets/02cd6323-f9aa-4af6-9a4a-c410ca38b2d9)
- Aligned header buttons with title
![Screenshot 2024-10-22 at 9 51
18 AM](https://github.com/user-attachments/assets/98cea438-634d-4cfe-b55a-a07bf56ecc8c)
- removed duplicate "Elasticsearch" from Service dropdown
![Screenshot 2024-10-22 at 9 51
54 AM](https://github.com/user-attachments/assets/e4eb0d91-9440-4730-bf63-28695d1060b2)
### Checklist
Delete any items that are not applicable to this PR.
- [X] Any text added follows [EUI's writing
guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses
sentence case text and includes [i18n
support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md)
- [X]
[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html)
was added for features that require explanation or tutorials
- [X] [Unit or functional
tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)
were updated or added to match the most common scenarios
- [X] Any UI touched in this PR is usable by keyboard only (learn more
about [keyboard accessibility](https://webaim.org/techniques/keyboard/))
- [X] Any UI touched in this PR does not create any new axe failures
(run axe in browser:
[FF](https://addons.mozilla.org/en-US/firefox/addon/axe-devtools/),
[Chrome](https://chrome.google.com/webstore/detail/axe-web-accessibility-tes/lhdoppojpmngadmnindnejefpokejbdd?hl=en-US))
- [X] This renders correctly on smaller devices using a responsive
layout. (You can test this [in your
browser](https://www.browserstack.com/guide/responsive-testing-on-local-server))
- [X] This was checked for [cross-browser
compatibility](https://www.elastic.co/support/matrix#matrix_browsers)
---------
Co-authored-by: Elastic Machine
Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
---
.../all_inference_endpoints/constants.ts | 2 ++
.../filter/multi_select_filter.tsx | 14 +++++++----
.../component/list_usage_results.test.tsx | 4 ++--
.../actions/component/list_usage_results.tsx | 2 +-
.../component/scan_usage_results.test.tsx | 10 ++++----
.../actions/component/scan_usage_results.tsx | 19 ++++++++-------
.../actions/component/usage_item.test.tsx | 8 +++----
.../actions/component/usage_item.tsx | 19 +++++++++------
.../delete/confirm_delete_endpoint/index.tsx | 11 +++++----
.../confirm_delete_endpoint/translations.ts | 4 ++--
.../search/table_search.tsx | 1 +
.../all_inference_endpoints/types.ts | 2 +-
.../components/inference_endpoints_header.tsx | 24 ++++++++++++++-----
.../search_inference_endpoints/tsconfig.json | 4 +++-
14 files changed, 77 insertions(+), 47 deletions(-)
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/constants.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/constants.ts
index 3e60bc33b049c..7ce1e578f1db0 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/constants.ts
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/constants.ts
@@ -32,3 +32,5 @@ export const DEFAULT_INFERENCE_ENDPOINTS_TABLE_STATE: AllInferenceEndpointsTable
filterOptions: DEFAULT_FILTER_OPTIONS,
queryParams: DEFAULT_QUERY_PARAMS,
};
+
+export const PIPELINE_URL = 'ingest/ingest_pipelines';
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.tsx
index 84883c4e85432..790bb5ec09913 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/filter/multi_select_filter.tsx
@@ -18,6 +18,7 @@ import {
} from '@elastic/eui';
import { css } from '@emotion/react';
import React, { useState } from 'react';
+import _ from 'lodash';
import * as i18n from './translations';
export interface MultiSelectFilterOption {
@@ -44,11 +45,14 @@ export const MultiSelectFilter: React.FC = ({
const { euiTheme } = useEuiTheme();
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
const toggleIsPopoverOpen = () => setIsPopoverOpen((prevValue) => !prevValue);
- const options: MultiSelectFilterOption[] = rawOptions.map(({ key, label }) => ({
- label,
- key,
- checked: selectedOptionKeys.includes(key) ? 'on' : undefined,
- }));
+ const options: MultiSelectFilterOption[] = _.uniqBy(
+ rawOptions.map(({ key, label }) => ({
+ label,
+ key,
+ checked: selectedOptionKeys.includes(key) ? 'on' : undefined,
+ })),
+ 'label'
+ );
return (
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.test.tsx
index 40f821bc104ae..2907cc7ef8014 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.test.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.test.tsx
@@ -12,11 +12,11 @@ import { render, screen, fireEvent } from '@testing-library/react';
describe('ListUsageResults', () => {
const items = [
{
- label: 'index-1',
+ id: 'index-1',
type: 'Index',
},
{
- label: 'pipeline-1',
+ id: 'pipeline-1',
type: 'Pipeline',
},
];
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.tsx
index d20520345a8ba..d42b0f6735252 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/list_usage_results.tsx
@@ -34,7 +34,7 @@ export const ListUsageResults: React.FC = ({ list }) => {
{list
- .filter((item) => item.label.toLowerCase().includes(term.toLowerCase()))
+ .filter((item) => item.id.toLowerCase().includes(term.toLowerCase()))
.map((item, id) => (
))}
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.test.tsx
index d2ec41680d249..a9cb3f1f8d389 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.test.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.test.tsx
@@ -19,11 +19,11 @@ const mockOnCheckboxChange = jest.fn();
describe('ScanUsageResults', () => {
const items = [
{
- label: 'index-1',
+ id: 'index-1',
type: 'Index',
},
{
- label: 'pipeline-1',
+ id: 'pipeline-1',
type: 'Pipeline',
},
];
@@ -40,7 +40,7 @@ describe('ScanUsageResults', () => {
);
});
@@ -58,9 +58,9 @@ describe('ScanUsageResults', () => {
it('opens index management in a new tab', () => {
fireEvent.click(screen.getByTestId('inferenceManagementOpenIndexManagement'));
- expect(mockNavigateToApp).toHaveBeenCalledWith('enterprise_search', {
+ expect(mockNavigateToApp).toHaveBeenCalledWith('enterpriseSearchContent', {
openInNewTab: true,
- path: 'content/search_indices',
+ path: 'search_indices',
});
});
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.tsx
index 0f4aa09c12be4..33d7a4dae891f 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/scan_usage_results.tsx
@@ -17,30 +17,31 @@ import {
import React from 'react';
import { euiThemeVars } from '@kbn/ui-theme';
import { css } from '@emotion/react';
+import { ENTERPRISE_SEARCH_CONTENT_APP_ID } from '@kbn/deeplinks-search';
+
import { InferenceUsageInfo } from '../../../../types';
import { useKibana } from '../../../../../../hooks/use_kibana';
import { RenderMessageWithIcon } from './render_message_with_icon';
-
import * as i18n from '../delete/confirm_delete_endpoint/translations';
import { ListUsageResults } from './list_usage_results';
interface ScanUsageResultsProps {
list: InferenceUsageInfo[];
ignoreWarningCheckbox: boolean;
- onCheckboxChange: (state: boolean) => void;
+ onIgnoreWarningCheckboxChange: (state: boolean) => void;
}
export const ScanUsageResults: React.FC = ({
list,
ignoreWarningCheckbox,
- onCheckboxChange,
+ onIgnoreWarningCheckboxChange,
}) => {
const {
services: { application },
} = useKibana();
- const handleNavigateToIndex = () => {
- application?.navigateToApp('enterprise_search', {
- path: 'content/search_indices',
+ const handleNavigateToIndexManagement = () => {
+ application?.navigateToApp(ENTERPRISE_SEARCH_CONTENT_APP_ID, {
+ path: 'search_indices',
openInNewTab: true,
});
};
@@ -59,7 +60,7 @@ export const ScanUsageResults: React.FC = ({
-
+
= ({
= ({
id={'ignoreWarningCheckbox'}
label={i18n.IGNORE_POTENTIAL_ERRORS_LABEL}
checked={ignoreWarningCheckbox}
- onChange={(e) => onCheckboxChange(e.target.checked)}
+ onChange={(e) => onIgnoreWarningCheckboxChange(e.target.checked)}
/>
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.test.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.test.tsx
index 7315de521a1c3..6c6899c71922d 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.test.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.test.tsx
@@ -29,7 +29,7 @@ describe('UsageItem', () => {
describe('index', () => {
const item: InferenceUsageInfo = {
- label: 'index-1',
+ id: 'index-1',
type: 'Index',
};
@@ -44,16 +44,16 @@ describe('UsageItem', () => {
it('opens index in a new tab', () => {
fireEvent.click(screen.getByRole('button'));
- expect(mockNavigateToApp).toHaveBeenCalledWith('enterprise_search', {
+ expect(mockNavigateToApp).toHaveBeenCalledWith('enterpriseSearchContent', {
openInNewTab: true,
- path: 'content/search_indices/index-1',
+ path: 'search_indices/index-1',
});
});
});
describe('pipeline', () => {
const item: InferenceUsageInfo = {
- label: 'pipeline-1',
+ id: 'pipeline-1',
type: 'Pipeline',
};
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.tsx
index 90bd050d67b81..577b9f8aa0e29 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/component/usage_item.tsx
@@ -14,11 +14,15 @@ import {
EuiText,
EuiTextTruncate,
EuiIcon,
+ EuiSpacer,
} from '@elastic/eui';
import React from 'react';
+import { ENTERPRISE_SEARCH_CONTENT_APP_ID } from '@kbn/deeplinks-search';
+import { MANAGEMENT_APP_ID } from '@kbn/deeplinks-management/constants';
import { useKibana } from '../../../../../../hooks/use_kibana';
import { InferenceUsageInfo } from '../../../../types';
+import { PIPELINE_URL } from '../../../../constants';
interface UsageProps {
usageItem: InferenceUsageInfo;
@@ -29,27 +33,27 @@ export const UsageItem: React.FC = ({ usageItem }) => {
} = useKibana();
const handleNavigateToIndex = () => {
if (usageItem.type === 'Index') {
- application?.navigateToApp('enterprise_search', {
- path: `content/search_indices/${usageItem.label}`,
+ application?.navigateToApp(ENTERPRISE_SEARCH_CONTENT_APP_ID, {
+ path: `search_indices/${usageItem.id}`,
openInNewTab: true,
});
} else if (usageItem.type === 'Pipeline') {
- application?.navigateToApp('management', {
- path: `ingest/ingest_pipelines?pipeline=${usageItem.label}`,
+ application?.navigateToApp(MANAGEMENT_APP_ID, {
+ path: `${PIPELINE_URL}?pipeline=${usageItem.id}`,
openInNewTab: true,
});
}
};
return (
-
+
-
+
@@ -58,7 +62,7 @@ export const UsageItem: React.FC = ({ usageItem }) => {
-
+
@@ -66,6 +70,7 @@ export const UsageItem: React.FC = ({ usageItem }) => {
+
);
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.tsx
index 965f512b32d7d..ea5c35178f9bf 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/index.tsx
@@ -37,7 +37,7 @@ export const ConfirmDeleteEndpointModal: React.FC {
+ const onIgnoreWarningCheckboxChange = (state: boolean) => {
setIgnoreWarningCheckbox(state);
if (state) {
setDeleteDisabled(false);
@@ -50,8 +50,11 @@ export const ConfirmDeleteEndpointModal: React.FC ({ label: index, type: 'Index' }));
- const pipelines = data.pipelines.map((pipeline, id) => ({ label: pipeline, type: 'Pipeline' }));
+ const indices = data.indexes.map((index, id) => ({ id: index, type: 'Index' }));
+ const pipelines = data.pipelines.map((pipeline, id) => ({
+ id: pipeline,
+ type: 'Pipeline',
+ }));
const usages: InferenceUsageInfo[] = [...indices, ...pipelines];
if (usages.length > 0) {
setDeleteDisabled(true);
@@ -106,7 +109,7 @@ export const ConfirmDeleteEndpointModal: React.FC
)}
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts
index d606e6f3c1b0e..b82fbcdf6b425 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/render_table_columns/render_actions/actions/delete/confirm_delete_endpoint/translations.ts
@@ -19,7 +19,7 @@ export const CONFIRM_DELETE_WARNING = i18n.translate(
'xpack.searchInferenceEndpoints.confirmDeleteEndpoint.confirmQuestion',
{
defaultMessage:
- 'Deleting an inference endpoint currently in use will cause failures in the ingest and query attempts.',
+ 'Deleting an inference endpoint currently in use will cause failures in ingest and query attempts.',
}
);
@@ -54,7 +54,7 @@ export const POTENTIAL_FAILURE_LABEL = i18n.translate(
export const IGNORE_POTENTIAL_ERRORS_LABEL = i18n.translate(
'xpack.searchInferenceEndpoints.confirmDeleteEndpoint.ignoreErrors',
{
- defaultMessage: 'Ignore potential errors and force deletion',
+ defaultMessage: 'Ignore errors and force deletion',
}
);
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.tsx b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.tsx
index b74e8050e5f92..b3989d60d9123 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/search/table_search.tsx
@@ -29,6 +29,7 @@ export const TableSearch: React.FC = ({ searchKey, se
onChange={(e) => setSearchKey(e.target.value)}
onSearch={onSearch}
value={searchKey}
+ data-test-subj="search-field-endpoints"
/>
);
};
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/types.ts b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/types.ts
index b5c5fc49aa1fa..0eec8a0cb177d 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/types.ts
+++ b/x-pack/plugins/search_inference_endpoints/public/components/all_inference_endpoints/types.ts
@@ -64,6 +64,6 @@ export interface InferenceEndpointUI {
}
export interface InferenceUsageInfo {
- label: string;
+ id: string;
type: string;
}
diff --git a/x-pack/plugins/search_inference_endpoints/public/components/inference_endpoints_header.tsx b/x-pack/plugins/search_inference_endpoints/public/components/inference_endpoints_header.tsx
index f12ef3e9fe8cd..acb7e82db13b2 100644
--- a/x-pack/plugins/search_inference_endpoints/public/components/inference_endpoints_header.tsx
+++ b/x-pack/plugins/search_inference_endpoints/public/components/inference_endpoints_header.tsx
@@ -5,7 +5,7 @@
* 2.0.
*/
-import { EuiPageTemplate, EuiLink } from '@elastic/eui';
+import { EuiPageTemplate, EuiButtonEmpty } from '@elastic/eui';
import React from 'react';
import * as i18n from '../../common/translations';
import { docLinks } from '../../common/doc_links';
@@ -21,16 +21,28 @@ export const InferenceEndpointsHeader: React.FC = () => {
description={i18n.MANAGE_INFERENCE_ENDPOINTS_LABEL}
bottomBorder={true}
rightSideItems={[
-
{i18n.API_DOCUMENTATION_LINK}
- ,
-
+ ,
+
{i18n.VIEW_YOUR_MODELS_LINK}
- ,
+ ,
]}
/>
);
diff --git a/x-pack/plugins/search_inference_endpoints/tsconfig.json b/x-pack/plugins/search_inference_endpoints/tsconfig.json
index 5b4a66e37d2f5..d454be99b65f0 100644
--- a/x-pack/plugins/search_inference_endpoints/tsconfig.json
+++ b/x-pack/plugins/search_inference_endpoints/tsconfig.json
@@ -31,7 +31,9 @@
"@kbn/test-jest-helpers",
"@kbn/kibana-utils-plugin",
"@kbn/features-plugin",
- "@kbn/ui-theme"
+ "@kbn/ui-theme",
+ "@kbn/deeplinks-search",
+ "@kbn/deeplinks-management"
],
"exclude": [
"target/**/*",
From 790613f37b848ed83ebd41a3e07a61deb3d30513 Mon Sep 17 00:00:00 2001
From: Stratoula Kalafateli
Date: Tue, 22 Oct 2024 20:21:17 +0200
Subject: [PATCH 025/146] [ES|QL] Supports _index_mode in the metadata options
(#197167)
---
.../src/autocomplete/autocomplete.test.ts | 2 +-
.../kbn-esql-validation-autocomplete/src/shared/constants.ts | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts
index e463902554074..deb4592428089 100644
--- a/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts
+++ b/packages/kbn-esql-validation-autocomplete/src/autocomplete/autocomplete.test.ts
@@ -1123,7 +1123,7 @@ describe('autocomplete', () => {
{ filterText: '_source', text: '_source, ', command: TRIGGER_SUGGESTION_COMMAND },
]);
// no comma if there are no more fields
- testSuggestions('FROM a METADATA _id, _ignored, _index, _source, _version/', [
+ testSuggestions('FROM a METADATA _id, _ignored, _index, _source, _index_mode, _version/', [
{ filterText: '_version', text: '_version | ', command: TRIGGER_SUGGESTION_COMMAND },
]);
});
diff --git a/packages/kbn-esql-validation-autocomplete/src/shared/constants.ts b/packages/kbn-esql-validation-autocomplete/src/shared/constants.ts
index c1942118a41e2..1a9f382d32a6d 100644
--- a/packages/kbn-esql-validation-autocomplete/src/shared/constants.ts
+++ b/packages/kbn-esql-validation-autocomplete/src/shared/constants.ts
@@ -15,4 +15,4 @@ export const SINGLE_TICK_REGEX = /`/g;
export const DOUBLE_BACKTICK = '``';
export const SINGLE_BACKTICK = '`';
-export const METADATA_FIELDS = ['_version', '_id', '_index', '_source', '_ignored'];
+export const METADATA_FIELDS = ['_version', '_id', '_index', '_source', '_ignored', '_index_mode'];
From 87bda295e50aea4ec3372a2cbaed052b68c2c08f Mon Sep 17 00:00:00 2001
From: Rachel Shen
Date: Tue, 22 Oct 2024 13:01:38 -0600
Subject: [PATCH 026/146] [Canvas] Update kbn/flot to remove table.replace()
issue (#195643)
## Summary
Closing https://github.com/elastic/kibana-team/issues/1132 to avoid an
issue with Canvas, that is planned to be removed in 10.x. @kbn/flot is
being used by the monitoring team so I'm not removing it from Kibana in
this PR.
---
packages/kbn-flot-charts/lib/jquery_flot.js | 2415 +++++++++++--------
1 file changed, 1345 insertions(+), 1070 deletions(-)
diff --git a/packages/kbn-flot-charts/lib/jquery_flot.js b/packages/kbn-flot-charts/lib/jquery_flot.js
index 3b13b317c616c..50524fd8f4926 100644
--- a/packages/kbn-flot-charts/lib/jquery_flot.js
+++ b/packages/kbn-flot-charts/lib/jquery_flot.js
@@ -1,8 +1,6 @@
/* JavaScript plotting library for jQuery, version 0.8.3.
-
Copyright (c) 2007-2014 IOLA and Ole Laursen.
Licensed under the MIT license.
-
*/
// first an inline dependency, jquery.colorhelpers.js, we inline it here
@@ -29,482 +27,602 @@ Licensed under the MIT license.
* V. 1.1: Fix error handling so e.g. parsing an empty string does
* produce a color rather than just crashing.
*/
+
(function($){$.color={};$.color.make=function(r,g,b,a){var o={};o.r=r||0;o.g=g||0;o.b=b||0;o.a=a!=null?a:1;o.add=function(c,d){for(var i=0;i=1){return"rgb("+[o.r,o.g,o.b].join(",")+")"}else{return"rgba("+[o.r,o.g,o.b,o.a].join(",")+")"}};o.normalize=function(){function clamp(min,value,max){return valuemax?max:value}o.r=clamp(0,parseInt(o.r),255);o.g=clamp(0,parseInt(o.g),255);o.b=clamp(0,parseInt(o.b),255);o.a=clamp(0,o.a,1);return o};o.clone=function(){return $.color.make(o.r,o.b,o.g,o.a)};return o.normalize()};$.color.extract=function(elem,css){var c;do{c=elem.css(css).toLowerCase();if(c!=""&&c!="transparent")break;elem=elem.parent()}while(elem.length&&!$.nodeName(elem.get(0),"body"));if(c=="rgba(0, 0, 0, 0)")c="transparent";return $.color.parse(c)};$.color.parse=function(str){var res,m=$.color.make;if(res=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10));if(res=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseInt(res[1],10),parseInt(res[2],10),parseInt(res[3],10),parseFloat(res[4]));if(res=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55);if(res=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(str))return m(parseFloat(res[1])*2.55,parseFloat(res[2])*2.55,parseFloat(res[3])*2.55,parseFloat(res[4]));if(res=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(str))return m(parseInt(res[1],16),parseInt(res[2],16),parseInt(res[3],16));if(res=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(str))return m(parseInt(res[1]+res[1],16),parseInt(res[2]+res[2],16),parseInt(res[3]+res[3],16));var name=$.trim(str).toLowerCase();if(name=="transparent")return m(255,255,255,0);else{res=lookupColors[name]||[0,0,0];return m(res[0],res[1],res[2])}};var lookupColors={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]}})(jQuery);
// the actual Flot code
+/* Javascript plotting library for jQuery, version 0.9.0-alpha.
+
+Copyright (c) 2007-2013 IOLA and Ole Laursen.
+Licensed under the MIT license.
+
+*/
+
(function($) {
- // Cache the prototype hasOwnProperty for faster access
+ // A jquery-esque isNumeric method since we currently support 1.4.4
+ // and $.isNumeric was introduced on in 1.7
+ var isNumeric = $.isNumeric || function(obj) {
+ return obj - parseFloat( obj ) >= 0;
+ };
+
+ /**
+ * The Canvas object is a wrapper around an HTML5