Skip to content

Commit

Permalink
[8.x][Core] [UA] Support API Deprecations #196081 (#198210)
Browse files Browse the repository at this point in the history
## Summary

Backport #196081
  • Loading branch information
Bamieh authored Oct 30, 2024
1 parent 7c1be44 commit 21dd620
Show file tree
Hide file tree
Showing 129 changed files with 2,046 additions and 315 deletions.
2 changes: 1 addition & 1 deletion .buildkite/ftr_platform_stateful_configs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ enabled:
- x-pack/test/task_manager_claimer_update_by_query/config.ts
- x-pack/test/ui_capabilities/security_and_spaces/config.ts
- x-pack/test/ui_capabilities/spaces_only/config.ts
- x-pack/test/upgrade_assistant_integration/config.js
- x-pack/test/upgrade_assistant_integration/config.ts
- x-pack/test/usage_collection/config.ts
- x-pack/performance/journeys_e2e/aiops_log_rate_analysis.ts
- x-pack/performance/journeys_e2e/ecommerce_dashboard.ts
Expand Down
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,7 @@ module.exports = {
'x-pack/test/*/*config.*ts',
'x-pack/test/saved_object_api_integration/*/apis/**/*',
'x-pack/test/ui_capabilities/*/tests/**/*',
'x-pack/test/upgrade_assistant_integration/**/*',
'x-pack/test/performance/**/*.ts',
'**/cypress.config.{js,ts}',
'x-pack/test_serverless/**/config*.ts',
Expand Down
6 changes: 6 additions & 0 deletions examples/routing_example/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,9 @@ export const POST_MESSAGE_ROUTE_PATH = '/api/post_message';

// Internal APIs should use the `internal` prefix, instead of the `api` prefix.
export const INTERNAL_GET_MESSAGE_BY_ID_ROUTE = '/internal/get_message';

export const DEPRECATED_ROUTES = {
REMOVED_ROUTE: '/api/routing_example/d/removed_route',
MIGRATED_ROUTE: '/api/routing_example/d/migrated_route',
VERSIONED_ROUTE: '/api/routing_example/d/versioned',
};
3 changes: 2 additions & 1 deletion examples/routing_example/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
*/

import { Plugin, CoreSetup, CoreStart } from '@kbn/core/server';
import { registerRoutes } from './routes';
import { registerRoutes, registerDeprecatedRoutes } from './routes';

export class RoutingExamplePlugin implements Plugin<{}, {}> {
public setup(core: CoreSetup) {
const router = core.http.createRouter();

registerRoutes(router);
registerDeprecatedRoutes(router);

return {};
}
Expand Down
17 changes: 17 additions & 0 deletions examples/routing_example/server/routes/deprecated_routes/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* 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 { IRouter } from '@kbn/core/server';
import { registerDeprecatedRoute } from './unversioned';
import { registerVersionedDeprecatedRoute } from './versioned';

export function registerDeprecatedRoutes(router: IRouter) {
registerDeprecatedRoute(router);
registerVersionedDeprecatedRoute(router);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* 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 { IRouter } from '@kbn/core/server';
import { schema } from '@kbn/config-schema';
import { DEPRECATED_ROUTES } from '../../../common';

export const registerDeprecatedRoute = (router: IRouter) => {
router.get(
{
path: DEPRECATED_ROUTES.REMOVED_ROUTE,
validate: false,
options: {
access: 'public',
deprecated: {
documentationUrl: 'https://elastic.co/',
severity: 'critical',
reason: { type: 'remove' },
},
},
},
async (ctx, req, res) => {
return res.ok({
body: { result: 'Called deprecated route. Check UA to see the deprecation.' },
});
}
);

router.post(
{
path: DEPRECATED_ROUTES.MIGRATED_ROUTE,
validate: {
body: schema.object({
test: schema.maybe(schema.boolean()),
}),
},
options: {
access: 'public',
deprecated: {
documentationUrl: 'https://elastic.co/',
severity: 'critical',
reason: {
type: 'migrate',
newApiMethod: 'GET',
newApiPath: `${DEPRECATED_ROUTES.VERSIONED_ROUTE}?apiVersion=2`,
},
},
},
},
async (ctx, req, res) => {
return res.ok({
body: { result: 'Called deprecated route. Check UA to see the deprecation.' },
});
}
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* 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 { RequestHandler } from '@kbn/core-http-server';
import type { IRouter } from '@kbn/core/server';
import { DEPRECATED_ROUTES } from '../../../common';

const createDummyHandler =
(version: string): RequestHandler =>
(ctx, req, res) => {
return res.ok({ body: { result: `API version ${version}.` } });
};

export const registerVersionedDeprecatedRoute = (router: IRouter) => {
const versionedRoute = router.versioned.get({
path: DEPRECATED_ROUTES.VERSIONED_ROUTE,
description: 'Routing example plugin deprecated versioned route.',
access: 'internal',
options: {
excludeFromOAS: true,
},
enableQueryVersion: true,
});

versionedRoute.addVersion(
{
options: {
deprecated: {
documentationUrl: 'https://elastic.co/',
severity: 'warning',
reason: { type: 'bump', newApiVersion: '2' },
},
},
validate: false,
version: '1',
},
createDummyHandler('1')
);

versionedRoute.addVersion(
{
version: '2',
validate: false,
},
createDummyHandler('2')
);
};
1 change: 1 addition & 0 deletions examples/routing_example/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@
*/

export { registerRoutes } from './register_routes';
export { registerDeprecatedRoutes } from './deprecated_routes';
1 change: 1 addition & 0 deletions examples/routing_example/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,6 @@
"@kbn/core-http-browser",
"@kbn/config-schema",
"@kbn/react-kibana-context-render",
"@kbn/core-http-server",
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ describe('DeprecationsClient', () => {

expect(result).toMatchInlineSnapshot(`
Object {
"reason": "This deprecation cannot be resolved automatically.",
"reason": "This deprecation cannot be resolved automatically or marked as resolved.",
"status": "fail",
}
`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import { i18n } from '@kbn/i18n';
import type { HttpStart } from '@kbn/core-http-browser';
import type { HttpFetchOptionsWithPath, HttpStart } from '@kbn/core-http-browser';
import type {
DomainDeprecationDetails,
DeprecationsGetResponse,
Expand Down Expand Up @@ -47,31 +47,70 @@ export class DeprecationsClient {
return typeof details.correctiveActions.api === 'object';
};

public resolveDeprecation = async (
private getResolveFetchDetails = (
details: DomainDeprecationDetails
): Promise<ResolveDeprecationResponse> => {
): HttpFetchOptionsWithPath | undefined => {
const { domainId, correctiveActions } = details;
// explicit check required for TS type guard
if (typeof correctiveActions.api !== 'object') {
return {
status: 'fail',
reason: i18n.translate('core.deprecations.noCorrectiveAction', {
defaultMessage: 'This deprecation cannot be resolved automatically.',
}),
};
}

const { body, method, path, omitContextFromBody = false } = correctiveActions.api;
try {
await this.http.fetch<void>({
if (correctiveActions.api) {
const { body, method, path, omitContextFromBody = false } = correctiveActions.api;

return {
path,
method,
asSystemRequest: true,
body: JSON.stringify({
...body,
...(omitContextFromBody ? {} : { deprecationDetails: { domainId } }),
}),
});
};
}

if (correctiveActions.mark_as_resolved_api) {
const { routeMethod, routePath, routeVersion, apiTotalCalls, totalMarkedAsResolved } =
correctiveActions.mark_as_resolved_api;
const incrementBy = apiTotalCalls - totalMarkedAsResolved;

return {
path: '/api/deprecations/mark_as_resolved',
method: 'POST',
asSystemRequest: true,
body: JSON.stringify({
domainId,
routeMethod,
routePath,
routeVersion,
incrementBy,
}),
};
}
};

public resolveDeprecation = async (
details: DomainDeprecationDetails
): Promise<ResolveDeprecationResponse> => {
const { correctiveActions } = details;
const noCorrectiveActionFail = {
status: 'fail' as const,
reason: i18n.translate('core.deprecations.noCorrectiveAction', {
defaultMessage: 'This deprecation cannot be resolved automatically or marked as resolved.',
}),
};

if (
typeof correctiveActions.api !== 'object' &&
typeof correctiveActions.mark_as_resolved_api !== 'object'
) {
return noCorrectiveActionFail;
}

try {
const fetchParams = this.getResolveFetchDetails(details);
if (!fetchParams) {
return noCorrectiveActionFail;
}

await this.http.fetch<void>(fetchParams);
return { status: 'ok' };
} catch (err) {
return {
Expand Down
31 changes: 28 additions & 3 deletions packages/core/deprecations/core-deprecations-common/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export interface BaseDeprecationDetails {
* The description message to be displayed for the deprecation.
* Check the README for writing deprecations in `src/core/server/deprecations/README.mdx`
*/
message: string;
message: string | string[];
/**
* levels:
* - warning: will not break deployment upon upgrade
Expand All @@ -39,7 +39,7 @@ export interface BaseDeprecationDetails {
* Predefined types are necessary to reduce having similar definitions with different keywords
* across kibana deprecations.
*/
deprecationType?: 'config' | 'feature';
deprecationType?: 'config' | 'api' | 'feature';
/** (optional) link to the documentation for more details on the deprecation. */
documentationUrl?: string;
/** (optional) specify the fix for this deprecation requires a full kibana restart. */
Expand Down Expand Up @@ -70,9 +70,31 @@ export interface BaseDeprecationDetails {
* Check the README for writing deprecations in `src/core/server/deprecations/README.mdx`
*/
manualSteps: string[];
/**
* (optional) The api to be called to mark the deprecation as resolved
* This corrective action when called should not resolve the deprecation
* instead it helps users track manually deprecated apis
* If the API used does resolve the deprecation use `correctiveActions.api`
*/
mark_as_resolved_api?: {
apiTotalCalls: number;
totalMarkedAsResolved: number;
timestamp: Date | number | string;
routePath: string;
routeMethod: string;
routeVersion?: string;
};
};
}

/**
* @public
*/
export interface ApiDeprecationDetails extends BaseDeprecationDetails {
apiId: string;
deprecationType: 'api';
}

/**
* @public
*/
Expand All @@ -91,7 +113,10 @@ export interface FeatureDeprecationDetails extends BaseDeprecationDetails {
/**
* @public
*/
export type DeprecationsDetails = ConfigDeprecationDetails | FeatureDeprecationDetails;
export type DeprecationsDetails =
| ConfigDeprecationDetails
| ApiDeprecationDetails
| FeatureDeprecationDetails;

/**
* @public
Expand Down
Loading

0 comments on commit 21dd620

Please sign in to comment.