Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[ResponseOps][Alerting] Add deprecation object to legacy rule endpoints #201550

Merged
merged 28 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions docs/upgrade-notes.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,53 @@ For Elastic Security solution release information, refer to {security-guide}/rel


[discrete]
[[breaking-201550]]
.[Alerting] Legacy deprecations (9.0.0)
[%collapsible]
====
*Details* +
`POST /api/alerts/alert/{id?}` has been replaced by `POST /api/alerting/rule/{id?}`

`GET /api/alerts/alert/{id}` has been replaced by `GET /api/alerting/rule/{id}`

`PUT /api/alerts/alert/{id}` has been replaced by `PUT /api/alerting/rule/rule/{id}`

`DELETE: /api/alerts/alert/{id}` has been replaced by `DELETE /api/alerting/rule/{id}`

`POST /api/alerts/alert/{id}/_disable` has been replaced by `POST /api/alerting/rule/{id}/_disable`

`POST /api/alerts/alert/{id}/_enable` has been replaced by `POST /api/alerting/rule/{id}/_enable`

`GET /api/alerts/_find` has been replaced by `GET /api/alerting/rules/_find`

`GET /api/alerts/_health` has been replaced by `GET /api/alerting/rule/_health`

`GET /api/alerts/list_alert_types` has been replaced by `GET /api/alerting/rule_types`

`POST /api/alerts/alert/{alert_id}/alert_instance/{alert_instance_id}/_mute` has been replaced by `POST /api/alerting/rule/{rule_id}/alert/{alert_id}/_mute`

`POST /api/alerts/alert/{alert_id}/alert_instance/{alert_instance_id}/_unmute` has been replaced by `POST /api/alerting/rule/{rule_id}/alert/{alert_id}/_unmute`

`POST /api/alerts/alert/{id}/_mute_all` has been replaced by `POST /api/alerting/rule/{id}/_mute_all`

`POST /api/alerts/alert/{id}/_unmute_all` has been replaced by `POST /api/alerting/rule/{id}/_unmute_all`

`POST /api/alerts/alert/{id}/_update_api_key` has been replaced by `POST /api/alerting/rule/{id}/_update_api_key`

`GET /api/alerts/{id}/_instance_summary` has been deprecated without replacement. Will be removed in v9.0.0

`GET /api/alerts/{id}/state` has been deprecated without replacement. Will be removed in v9.0.0

*Impact* +
Deprecated endpoints will fail with a 404 status code starting from version 9.0.0

*Action* +
Remove references to `GET /api/alerts/{id}/_instance_summary` endpoint.
Remove references to `GET /api/alerts/{id}/state` endpoint.
Replace references to endpoints listed as deprecated by it's replacement. See `Details` section.
The updated APIs can be found here https://www.elastic.co/docs/api/doc/kibana/v8/group/endpoint-alerting
====

[[breaking-199656]]
.Removed all security v1 endpoints (9.0.0)
[%collapsible]
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-doc-links/src/get_doc_links.ts
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,7 @@ export const getDocLinks = ({ kibanaBranch, buildFlavor }: GetDocLinkOptions): D
slackApiAction: `${KIBANA_DOCS}slack-action-type.html#configuring-slack-web-api`,
teamsAction: `${KIBANA_DOCS}teams-action-type.html#configuring-teams`,
connectors: `${KIBANA_DOCS}action-types.html`,
legacyRuleApiDeprecations: `${KIBANA_DOCS}breaking-changes-summary.html#breaking-201550`,
},
taskManager: {
healthMonitoring: `${KIBANA_DOCS}task-manager-health-monitoring.html`,
Expand Down
1 change: 1 addition & 0 deletions packages/kbn-doc-links/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,7 @@ export interface DocLinks {
slackApiAction: string;
teamsAction: string;
connectors: string;
legacyRuleApiDeprecations: string;
}>;
readonly taskManager: Readonly<{
healthMonitoring: string;
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/alerting/server/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ export class AlertingPlugin {
encryptedSavedObjects: plugins.encryptedSavedObjects,
config$: plugins.unifiedSearch.autocomplete.getInitializerContextConfig().create(),
isServerless: !!plugins.serverless,
docLinks: core.docLinks,
});

return {
Expand Down
3 changes: 2 additions & 1 deletion x-pack/plugins/alerting/server/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import { IRouter } from '@kbn/core/server';
import { DocLinksServiceSetup, IRouter } from '@kbn/core/server';
import { UsageCounter } from '@kbn/usage-collection-plugin/server';
import { EncryptedSavedObjectsPluginSetup } from '@kbn/encrypted-saved-objects-plugin/server';
import type { ConfigSchema } from '@kbn/unified-search-plugin/server/config';
Expand Down Expand Up @@ -81,6 +81,7 @@ export interface RouteOptions {
usageCounter?: UsageCounter;
config$?: Observable<ConfigSchema>;
isServerless?: boolean;
docLinks: DocLinksServiceSetup;
}

export function defineRoutes(opts: RouteOptions) {
Expand Down
50 changes: 47 additions & 3 deletions x-pack/plugins/alerting/server/routes/legacy/create.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Rule, RuleSystemAction } from '../../../common/rule';
import { RuleTypeDisabledError } from '../../lib/errors/rule_type_disabled';
import { encryptedSavedObjectsMock } from '@kbn/encrypted-saved-objects-plugin/server/mocks';
import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage';
import { docLinksServiceMock } from '@kbn/core/server/mocks';

const rulesClient = rulesClientMock.create();

Expand All @@ -32,6 +33,7 @@ beforeEach(() => {
});

describe('createAlertRoute', () => {
const docLinks = docLinksServiceMock.createSetupContract();
const createdAt = new Date();
const updatedAt = new Date();

Expand Down Expand Up @@ -104,6 +106,7 @@ describe('createAlertRoute', () => {
licenseState,
encryptedSavedObjects,
usageCounter: mockUsageCounter,
docLinks,
});

const [config, handler] = router.post.mock.calls[0];
Expand Down Expand Up @@ -179,6 +182,7 @@ describe('createAlertRoute', () => {
encryptedSavedObjects,
usageCounter: mockUsageCounter,
isServerless: true,
docLinks,
});

const [config] = router.post.mock.calls[0];
Expand All @@ -204,6 +208,7 @@ describe('createAlertRoute', () => {
licenseState,
encryptedSavedObjects,
usageCounter: mockUsageCounter,
docLinks,
});

const [config, handler] = router.post.mock.calls[0];
Expand Down Expand Up @@ -281,6 +286,7 @@ describe('createAlertRoute', () => {
licenseState,
encryptedSavedObjects,
usageCounter: mockUsageCounter,
docLinks,
});

const [config, handler] = router.post.mock.calls[0];
Expand Down Expand Up @@ -359,6 +365,7 @@ describe('createAlertRoute', () => {
licenseState,
encryptedSavedObjects,
usageCounter: mockUsageCounter,
docLinks,
});

const [config, handler] = router.post.mock.calls[0];
Expand Down Expand Up @@ -426,7 +433,7 @@ describe('createAlertRoute', () => {
const router = httpServiceMock.createRouter();
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });

createAlertRoute({ router, licenseState, encryptedSavedObjects });
createAlertRoute({ router, licenseState, encryptedSavedObjects, docLinks });

const [, handler] = router.post.mock.calls[0];

Expand All @@ -448,7 +455,7 @@ describe('createAlertRoute', () => {
throw new Error('OMG');
});

createAlertRoute({ router, licenseState, encryptedSavedObjects });
createAlertRoute({ router, licenseState, encryptedSavedObjects, docLinks });

const [, handler] = router.post.mock.calls[0];

Expand All @@ -466,7 +473,7 @@ describe('createAlertRoute', () => {
const router = httpServiceMock.createRouter();
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });

createAlertRoute({ router, licenseState, encryptedSavedObjects });
createAlertRoute({ router, licenseState, encryptedSavedObjects, docLinks });

const [, handler] = router.post.mock.calls[0];

Expand All @@ -491,6 +498,7 @@ describe('createAlertRoute', () => {
licenseState,
encryptedSavedObjects,
usageCounter: mockUsageCounter,
docLinks,
});
const [, handler] = router.post.mock.calls[0];
rulesClient.create.mockResolvedValueOnce(createResult);
Expand All @@ -511,6 +519,7 @@ describe('createAlertRoute', () => {
licenseState,
encryptedSavedObjects,
usageCounter: mockUsageCounter,
docLinks,
});

const [config, handler] = router.post.mock.calls[0];
Expand Down Expand Up @@ -570,4 +579,39 @@ describe('createAlertRoute', () => {
body: createResult,
});
});

it('should be deprecated', () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();
const encryptedSavedObjects = encryptedSavedObjectsMock.createSetup({ canEncrypt: true });
const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract();
const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test');

createAlertRoute({
router,
licenseState,
encryptedSavedObjects,
usageCounter: mockUsageCounter,
docLinks,
});

const [config] = router.post.mock.calls[0];

expect(config.options?.deprecated).toMatchInlineSnapshot(
{
documentationUrl: expect.stringMatching(/#breaking-201550$/),
},
`
Object {
"documentationUrl": StringMatching /#breaking-201550\\$/,
"reason": Object {
"newApiMethod": "POST",
"newApiPath": "/api/alerting/rule/{id?}",
"type": "migrate",
},
"severity": "warning",
}
`
);
});
});
12 changes: 10 additions & 2 deletions x-pack/plugins/alerting/server/routes/legacy/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export const createAlertRoute = ({
licenseState,
usageCounter,
isServerless,
docLinks,
}: RouteOptions) => {
router.post(
{
Expand All @@ -65,8 +66,15 @@ 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,
deprecated: {
documentationUrl: docLinks.links.alerting.legacyRuleApiDeprecations,
severity: 'warning',
reason: {
type: 'migrate',
newApiMethod: 'POST',
newApiPath: '/api/alerting/rule/{id?}',
},
},
},
},
handleDisabledApiKeysError(
Expand Down
39 changes: 34 additions & 5 deletions x-pack/plugins/alerting/server/routes/legacy/delete.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { verifyApiAccess } from '../../lib/license_api_access';
import { mockHandlerArguments } from '../_mock_handler_arguments';
import { rulesClientMock } from '../../rules_client.mock';
import { trackLegacyRouteUsage } from '../../lib/track_legacy_route_usage';
import { docLinksServiceMock } from '@kbn/core/server/mocks';

const rulesClient = rulesClientMock.create();

Expand All @@ -29,11 +30,13 @@ beforeEach(() => {
});

describe('deleteAlertRoute', () => {
const docLinks = docLinksServiceMock.createSetupContract();

it('deletes an alert with proper parameters', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();

deleteAlertRoute(router, licenseState);
deleteAlertRoute(router, licenseState, docLinks);

const [config, handler] = router.delete.mock.calls[0];

Expand Down Expand Up @@ -70,7 +73,7 @@ describe('deleteAlertRoute', () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();

deleteAlertRoute(router, licenseState, undefined, true);
deleteAlertRoute(router, licenseState, docLinks, undefined, true);

const [config] = router.delete.mock.calls[0];

Expand All @@ -82,7 +85,7 @@ describe('deleteAlertRoute', () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();

deleteAlertRoute(router, licenseState);
deleteAlertRoute(router, licenseState, docLinks);

const [, handler] = router.delete.mock.calls[0];

Expand All @@ -108,7 +111,7 @@ describe('deleteAlertRoute', () => {
throw new Error('OMG');
});

deleteAlertRoute(router, licenseState);
deleteAlertRoute(router, licenseState, docLinks);

const [, handler] = router.delete.mock.calls[0];

Expand All @@ -132,12 +135,38 @@ describe('deleteAlertRoute', () => {
const mockUsageCountersSetup = usageCountersServiceMock.createSetupContract();
const mockUsageCounter = mockUsageCountersSetup.createUsageCounter('test');

deleteAlertRoute(router, licenseState, mockUsageCounter);
deleteAlertRoute(router, licenseState, docLinks, mockUsageCounter);
const [, handler] = router.delete.mock.calls[0];
const [context, req, res] = mockHandlerArguments({ rulesClient }, { params: { id: '1' } }, [
'ok',
]);
await handler(context, req, res);
expect(trackLegacyRouteUsage).toHaveBeenCalledWith('delete', mockUsageCounter);
});

it('should be deprecated', async () => {
const licenseState = licenseStateMock.create();
const router = httpServiceMock.createRouter();

deleteAlertRoute(router, licenseState, docLinks);

const [config] = router.delete.mock.calls[0];

expect(config.options?.deprecated).toMatchInlineSnapshot(
{
documentationUrl: expect.stringMatching(/#breaking-201550$/),
},
`
Object {
"documentationUrl": StringMatching /#breaking-201550\\$/,
"reason": Object {
"newApiMethod": "DELETE",
"newApiPath": "/api/alerting/rule/{id}",
"type": "migrate",
},
"severity": "warning",
}
`
);
});
});
13 changes: 11 additions & 2 deletions x-pack/plugins/alerting/server/routes/legacy/delete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import { schema } from '@kbn/config-schema';
import { UsageCounter } from '@kbn/usage-collection-plugin/server';
import { DocLinksServiceSetup } from '@kbn/core/server';
import type { AlertingRouter } from '../../types';
import { ILicenseState } from '../../lib/license_state';
import { verifyApiAccess } from '../../lib/license_api_access';
Expand All @@ -20,6 +21,7 @@ const paramSchema = schema.object({
export const deleteAlertRoute = (
router: AlertingRouter,
licenseState: ILicenseState,
docLinks: DocLinksServiceSetup,
usageCounter?: UsageCounter,
isServerless?: boolean
) => {
Expand All @@ -33,8 +35,15 @@ 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,
deprecated: {
documentationUrl: docLinks.links.alerting.legacyRuleApiDeprecations,
severity: 'warning',
reason: {
type: 'migrate',
newApiMethod: 'DELETE',
newApiPath: '/api/alerting/rule/{id}',
},
},
},
},
router.handleLegacyErrors(async function (context, req, res) {
Expand Down
Loading