[8.x] [Response Ops][Rules] Version Get Rule Types API (#195361) (#196175)

# Backport

This will backport the following commits from `main` to `8.x`:
- [[Response Ops][Rules] Version Get Rule Types API
(#195361)](https://github.com/elastic/kibana/pull/195361)

<!--- Backport version: 9.4.3 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sqren/backport)

<!--BACKPORT [{"author":{"name":"Julian
Gernun","email":"17549662+jcger@users.noreply.github.com"},"sourceCommit":{"committedDate":"2024-10-14T15:46:17Z","message":"[Response
Ops][Rules] Version Get Rule Types API (#195361)\n\n##
Summary\r\n\r\n`GET /api/alerting/rule_types` item
in\r\nhttps://github.com/elastic/kibana/issues/195181","sha":"512a31d7a1e42139c2e1b26e961b2226ace3836d","branchLabelMapping":{"^v9.0.0$":"main","^v8.16.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["release_note:skip","Team:ResponseOps","v9.0.0","backport:prev-minor","v8.16.0"],"title":"[Response
Ops][Rules] Version Get Rule Types
API","number":195361,"url":"https://github.com/elastic/kibana/pull/195361","mergeCommit":{"message":"[Response
Ops][Rules] Version Get Rule Types API (#195361)\n\n##
Summary\r\n\r\n`GET /api/alerting/rule_types` item
in\r\nhttps://github.com/elastic/kibana/issues/195181","sha":"512a31d7a1e42139c2e1b26e961b2226ace3836d"}},"sourceBranch":"main","suggestedTargetBranches":["8.x"],"targetPullRequestStates":[{"branch":"main","label":"v9.0.0","branchLabelMappingKey":"^v9.0.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/195361","number":195361,"mergeCommit":{"message":"[Response
Ops][Rules] Version Get Rule Types API (#195361)\n\n##
Summary\r\n\r\n`GET /api/alerting/rule_types` item
in\r\nhttps://github.com/elastic/kibana/issues/195181","sha":"512a31d7a1e42139c2e1b26e961b2226ace3836d"}},{"branch":"8.x","label":"v8.16.0","branchLabelMappingKey":"^v8.16.0$","isSourceBranch":false,"state":"NOT_CREATED"}]}]
BACKPORT-->

Co-authored-by: Julian Gernun <17549662+jcger@users.noreply.github.com>
This commit is contained in:
Kibana Machine 2024-10-16 03:02:48 +11:00 committed by GitHub
parent d0f095f2ec
commit c156cb3816
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 248 additions and 87 deletions

View file

@ -0,0 +1,18 @@
/*
* 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.
*/
export { typesRulesResponseSchema, typesRulesResponseBodySchema } from './schemas/latest';
export type { TypesRulesResponse, TypesRulesResponseBody } from './types/latest';
export {
typesRulesResponseSchema as typesRulesResponseSchemaV1,
typesRulesResponseBodySchema as typesRulesResponseBodySchemaV1,
} from './schemas/v1';
export type {
TypesRulesResponse as TypesRulesResponseV1,
TypesRulesResponseBody as TypesRulesResponseBodyV1,
} from './types/v1';

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export * from './v1';

View file

@ -0,0 +1,75 @@
/*
* 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 { schema } from '@kbn/config-schema';
const actionVariableSchema = schema.object({
name: schema.string(),
description: schema.string(),
usesPublicBaseUrl: schema.maybe(schema.boolean()),
});
const actionGroupSchema = schema.object({
id: schema.string(),
name: schema.string(),
});
export const typesRulesResponseBodySchema = schema.arrayOf(
schema.object({
action_groups: schema.maybe(schema.arrayOf(actionGroupSchema)),
action_variables: schema.maybe(
schema.object({
context: schema.maybe(schema.arrayOf(actionVariableSchema)),
state: schema.maybe(schema.arrayOf(actionVariableSchema)),
params: schema.maybe(schema.arrayOf(actionVariableSchema)),
})
),
alerts: schema.maybe(
schema.object({
context: schema.string(),
mappings: schema.maybe(
schema.object({
dynamic: schema.maybe(schema.oneOf([schema.literal(false), schema.literal('strict')])),
fieldMap: schema.recordOf(schema.string(), schema.any()),
shouldWrite: schema.maybe(schema.boolean()),
useEcs: schema.maybe(schema.boolean()),
})
),
})
),
authorized_consumers: schema.recordOf(
schema.string(),
schema.object({ read: schema.boolean(), all: schema.boolean() })
),
category: schema.string(),
default_action_group_id: schema.string(),
default_schedule_interval: schema.maybe(schema.string()),
does_set_recovery_context: schema.maybe(schema.boolean()),
enabled_in_license: schema.boolean(),
fieldsForAAD: schema.maybe(schema.arrayOf(schema.string())),
has_alerts_mappings: schema.boolean(),
has_fields_for_a_a_d: schema.boolean(),
id: schema.string(),
is_exportable: schema.boolean(),
minimum_license_required: schema.oneOf([
schema.literal('basic'),
schema.literal('gold'),
schema.literal('platinum'),
schema.literal('standard'),
schema.literal('enterprise'),
schema.literal('trial'),
]),
name: schema.string(),
producer: schema.string(),
recovery_action_group: actionGroupSchema,
rule_task_timeout: schema.maybe(schema.string()),
})
);
export const typesRulesResponseSchema = schema.object({
body: typesRulesResponseBodySchema,
});

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export * from './v1';

View file

@ -0,0 +1,13 @@
/*
* 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 type { TypeOf } from '@kbn/config-schema';
import { typesRulesResponseSchemaV1, typesRulesResponseBodySchemaV1 } from '..';
export type TypesRulesResponse = TypeOf<typeof typesRulesResponseSchemaV1>;
export type TypesRulesResponseBody = TypeOf<typeof typesRulesResponseBodySchemaV1>;

View file

@ -5,8 +5,12 @@
* 2.0.
*/
import { WriteOperations, ReadOperations, AlertingAuthorizationEntity } from '../../authorization';
import { RulesClientContext } from '../types';
import {
WriteOperations,
ReadOperations,
AlertingAuthorizationEntity,
} from '../../../../authorization';
import { RulesClientContext } from '../../../../rules_client/types';
export async function listRuleTypes(context: RulesClientContext) {
return await context.authorization.filterByRuleTypeAuthorization(

View file

@ -30,7 +30,7 @@ import { getRuleExecutionKPIRoute } from './get_rule_execution_kpi';
import { getRuleStateRoute } from './get_rule_state';
import { healthRoute } from './health';
import { resolveRuleRoute } from './rule/apis/resolve';
import { ruleTypesRoute } from './rule_types';
import { ruleTypesRoute } from './rule/apis/list_types/rule_types';
import { muteAllRuleRoute } from './rule/apis/mute_all/mute_all_rule';
import { muteAlertRoute } from './rule/apis/mute_alert/mute_alert';
import { unmuteAllRuleRoute } from './rule/apis/unmute_all';

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export { ruleTypesRoute } from './rule_types';

View file

@ -7,17 +7,17 @@
import { ruleTypesRoute } from './rule_types';
import { httpServiceMock } from '@kbn/core/server/mocks';
import { licenseStateMock } from '../lib/license_state.mock';
import { verifyApiAccess } from '../lib/license_api_access';
import { mockHandlerArguments } from './_mock_handler_arguments';
import { rulesClientMock } from '../rules_client.mock';
import { RecoveredActionGroup } from '../../common';
import { RegistryAlertTypeWithAuth } from '../authorization';
import { AsApiContract } from './lib';
import { licenseStateMock } from '../../../../lib/license_state.mock';
import { verifyApiAccess } from '../../../../lib/license_api_access';
import { mockHandlerArguments } from '../../../_mock_handler_arguments';
import { rulesClientMock } from '../../../../rules_client.mock';
import { RecoveredActionGroup } from '../../../../../common';
import { RegistryAlertTypeWithAuth } from '../../../../authorization';
import { AsApiContract } from '../../../lib';
const rulesClient = rulesClientMock.create();
jest.mock('../lib/license_api_access', () => ({
jest.mock('../../../../lib/license_api_access', () => ({
verifyApiAccess: jest.fn(),
}));

View file

@ -0,0 +1,42 @@
/*
* 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 { IRouter } from '@kbn/core/server';
import { TypesRulesResponseBodyV1 } from '../../../../../common/routes/rule/apis/list_types';
import { ILicenseState } from '../../../../lib';
import { verifyAccessAndContext } from '../../../lib';
import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../../../../types';
import { transformRuleTypesResponseV1 } from './transforms';
export const ruleTypesRoute = (
router: IRouter<AlertingRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.get(
{
path: `${BASE_ALERTING_API_PATH}/rule_types`,
options: {
access: 'public',
summary: `Get the rule types`,
tags: ['oas-tag:alerting'],
},
validate: {},
},
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const rulesClient = (await context.alerting).getRulesClient();
const ruleTypes = await rulesClient.listRuleTypes();
const responseBody: TypesRulesResponseBodyV1 = transformRuleTypesResponseV1(ruleTypes);
return res.ok({
body: responseBody,
});
})
)
);
};

View file

@ -0,0 +1,9 @@
/*
* 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.
*/
export { transformRuleTypesResponse } from './transform_rule_types_response/latest';
export { transformRuleTypesResponse as transformRuleTypesResponseV1 } from './transform_rule_types_response/v1';

View file

@ -0,0 +1,8 @@
/*
* 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.
*/
export * from './v1';

View file

@ -0,0 +1,42 @@
/*
* 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 { isBoolean } from 'lodash/fp';
import { RegistryAlertTypeWithAuth } from '../../../../../../authorization';
import type { TypesRulesResponseBodyV1 } from '../../../../../../../common/routes/rule/apis/list_types';
export const transformRuleTypesResponse = (
ruleTypes: Set<RegistryAlertTypeWithAuth>
): TypesRulesResponseBodyV1 => {
return Array.from(ruleTypes).map((ruleType: RegistryAlertTypeWithAuth) => {
return {
...(ruleType.actionGroups ? { action_groups: ruleType.actionGroups } : {}),
...(ruleType.actionVariables ? { action_variables: ruleType.actionVariables } : {}),
...(ruleType.alerts ? { alerts: ruleType.alerts } : {}),
authorized_consumers: ruleType.authorizedConsumers,
category: ruleType.category,
default_action_group_id: ruleType.defaultActionGroupId,
...(ruleType.defaultScheduleInterval
? { default_schedule_interval: ruleType.defaultScheduleInterval }
: {}),
...(isBoolean(ruleType.doesSetRecoveryContext)
? { does_set_recovery_context: ruleType.doesSetRecoveryContext }
: {}),
enabled_in_license: ruleType.enabledInLicense,
...(ruleType.fieldsForAAD ? { fieldsForAAD: ruleType.fieldsForAAD } : {}),
has_alerts_mappings: ruleType.hasAlertsMappings,
has_fields_for_a_a_d: ruleType.hasFieldsForAAD,
id: ruleType.id,
is_exportable: ruleType.isExportable,
minimum_license_required: ruleType.minimumLicenseRequired,
name: ruleType.name,
producer: ruleType.producer,
recovery_action_group: ruleType.recoveryActionGroup,
...(ruleType.ruleTaskTimeout ? { rule_task_timeout: ruleType.ruleTaskTimeout } : {}),
};
});
};

View file

@ -1,75 +0,0 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { IRouter } from '@kbn/core/server';
import { ILicenseState } from '../lib';
import { RegistryAlertTypeWithAuth } from '../authorization';
import { verifyAccessAndContext } from './lib';
import { AlertingRequestHandlerContext, BASE_ALERTING_API_PATH } from '../types';
const rewriteBodyRes = (results: RegistryAlertTypeWithAuth[]) => {
return results.map(
({
enabledInLicense,
recoveryActionGroup,
actionGroups,
defaultActionGroupId,
minimumLicenseRequired,
isExportable,
ruleTaskTimeout,
actionVariables,
authorizedConsumers,
defaultScheduleInterval,
doesSetRecoveryContext,
hasAlertsMappings,
hasFieldsForAAD,
validLegacyConsumers,
...rest
}: RegistryAlertTypeWithAuth) => ({
...rest,
enabled_in_license: enabledInLicense,
recovery_action_group: recoveryActionGroup,
action_groups: actionGroups,
default_action_group_id: defaultActionGroupId,
minimum_license_required: minimumLicenseRequired,
is_exportable: isExportable,
rule_task_timeout: ruleTaskTimeout,
action_variables: actionVariables,
authorized_consumers: authorizedConsumers,
default_schedule_interval: defaultScheduleInterval,
does_set_recovery_context: doesSetRecoveryContext,
has_alerts_mappings: !!hasAlertsMappings,
has_fields_for_a_a_d: !!hasFieldsForAAD,
})
);
};
export const ruleTypesRoute = (
router: IRouter<AlertingRequestHandlerContext>,
licenseState: ILicenseState
) => {
router.get(
{
path: `${BASE_ALERTING_API_PATH}/rule_types`,
options: {
access: 'public',
summary: `Get the rule types`,
tags: ['oas-tag:alerting'],
},
validate: {},
},
router.handleLegacyErrors(
verifyAccessAndContext(licenseState, async function (context, req, res) {
const rulesClient = (await context.alerting).getRulesClient();
const ruleTypes = Array.from(await rulesClient.listRuleTypes());
return res.ok({
body: rewriteBodyRes(ruleTypes),
});
})
)
);
};

View file

@ -72,6 +72,7 @@ export interface RegistryRuleType
| 'defaultScheduleInterval'
| 'doesSetRecoveryContext'
| 'fieldsForAAD'
| 'alerts'
> {
id: string;
enabledInLicense: boolean;

View file

@ -62,7 +62,7 @@ import { unmuteAll } from '../application/rule/methods/unmute_all';
import { muteAll } from '../application/rule/methods/mute_all';
import { unmuteInstance } from '../application/rule/methods/unmute_alert/unmute_instance';
import { runSoon } from './methods/run_soon';
import { listRuleTypes } from './methods/list_rule_types';
import { listRuleTypes } from '../application/rule/methods/rule_types/rule_types';
import { getScheduleFrequency } from '../application/rule/methods/get_schedule_frequency/get_schedule_frequency';
import {
bulkUntrackAlerts,