From b5353fbf2bed3482ff4733e66d31b795fdc5a1bd Mon Sep 17 00:00:00 2001 From: Ying Mao Date: Tue, 9 Jan 2024 11:23:49 -0500 Subject: [PATCH 01/12] [Response Ops][Alerting] AAD for alerting example rules (#174032) Towards https://github.com/elastic/response-ops-team/issues/164 Registering alerting example rules with framework AAD. This creates a new alerts index `.alerts-default.alerts-default` that will eventually hold alerts for all rules that have not customized their registration. This index contains only the mappings for the basic alerts as data documents, no custom context or payload fields. Run kibana using `--run-examples` flag. Create one of the example rule types and let it alert and then resolve and see an alert document get created in the `.alerts-default.alerts-default` index. --------- Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com> --- .../schemas/create_schema_from_field_map.ts | 1 + .../src/schemas/generated/alert_schema.ts | 1 + .../src/schemas/generated/default_schema.ts | 78 +++++++++++++++++++ .../src/schemas/generated/ecs_schema.ts | 1 + .../schemas/generated/legacy_alert_schema.ts | 1 + .../generated/ml_anomaly_detection_schema.ts | 1 + .../generated/observability_apm_schema.ts | 1 + .../generated/observability_logs_schema.ts | 1 + .../generated/observability_metrics_schema.ts | 1 + .../generated/observability_slo_schema.ts | 1 + .../generated/observability_uptime_schema.ts | 1 + .../src/schemas/generated/security_schema.ts | 1 + .../src/schemas/generated/stack_schema.ts | 1 + .../src/schemas/index.ts | 5 +- .../src/alerts_as_data_rbac.ts | 2 + .../alerting_example/server/plugin.ts | 22 +++--- .../always_firing.ts | 38 ++++++--- .../{alert_types => rule_types}/astros.ts | 39 ++++++++-- .../{alert_types => rule_types}/pattern.md | 0 .../pattern.test.ts | 18 ++--- .../{alert_types => rule_types}/pattern.ts | 26 ++++++- .../examples/alerting_example/tsconfig.json | 1 + x-pack/plugins/alerting/server/index.ts | 2 + ...type_registry_deprecated_consumers.test.ts | 1 + ...rule_type_registry_deprecated_consumers.ts | 1 + x-pack/plugins/alerting/server/types.ts | 8 +- 26 files changed, 209 insertions(+), 44 deletions(-) create mode 100644 packages/kbn-alerts-as-data-utils/src/schemas/generated/default_schema.ts rename x-pack/examples/alerting_example/server/{alert_types => rule_types}/always_firing.ts (75%) rename x-pack/examples/alerting_example/server/{alert_types => rule_types}/astros.ts (76%) rename x-pack/examples/alerting_example/server/{alert_types => rule_types}/pattern.md (100%) rename x-pack/examples/alerting_example/server/{alert_types => rule_types}/pattern.test.ts (90%) rename x-pack/examples/alerting_example/server/{alert_types => rule_types}/pattern.ts (85%) diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/create_schema_from_field_map.ts b/packages/kbn-alerts-as-data-utils/src/schemas/create_schema_from_field_map.ts index e2d45b876fb57..1ae0979f03f3f 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/create_schema_from_field_map.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/create_schema_from_field_map.ts @@ -322,6 +322,7 @@ export const schemaGeoPoint = rt.union([ export const schemaGeoPointArray = rt.array(schemaGeoPoint); // prettier-ignore const %%schemaPrefix%%Required = %%REQUIRED_FIELDS%%; +// prettier-ignore const %%schemaPrefix%%Optional = %%OPTIONAL_FIELDS%%; // prettier-ignore diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/alert_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/alert_schema.ts index 5625460f269b4..b183ca5c792f8 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/alert_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/alert_schema.ts @@ -80,6 +80,7 @@ const AlertRequired = rt.type({ 'kibana.alert.uuid': schemaString, 'kibana.space_ids': schemaStringArray, }); +// prettier-ignore const AlertOptional = rt.partial({ 'event.action': schemaString, 'event.kind': schemaString, diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/default_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/default_schema.ts new file mode 100644 index 0000000000000..919483f6c79f0 --- /dev/null +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/default_schema.ts @@ -0,0 +1,78 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ +// ---------------------------------- WARNING ---------------------------------- +// this file was generated, and should not be edited by hand +// ---------------------------------- WARNING ---------------------------------- +import * as rt from 'io-ts'; +import { Either } from 'fp-ts/lib/Either'; +import { AlertSchema } from './alert_schema'; +const ISO_DATE_PATTERN = /^d{4}-d{2}-d{2}Td{2}:d{2}:d{2}.d{3}Z$/; +export const IsoDateString = new rt.Type( + 'IsoDateString', + rt.string.is, + (input, context): Either => { + if (typeof input === 'string' && ISO_DATE_PATTERN.test(input)) { + return rt.success(input); + } else { + return rt.failure(input, context); + } + }, + rt.identity +); +export type IsoDateStringC = typeof IsoDateString; +export const schemaUnknown = rt.unknown; +export const schemaUnknownArray = rt.array(rt.unknown); +export const schemaString = rt.string; +export const schemaStringArray = rt.array(schemaString); +export const schemaNumber = rt.number; +export const schemaNumberArray = rt.array(schemaNumber); +export const schemaDate = rt.union([IsoDateString, schemaNumber]); +export const schemaDateArray = rt.array(schemaDate); +export const schemaDateRange = rt.partial({ + gte: schemaDate, + lte: schemaDate, +}); +export const schemaDateRangeArray = rt.array(schemaDateRange); +export const schemaStringOrNumber = rt.union([schemaString, schemaNumber]); +export const schemaStringOrNumberArray = rt.array(schemaStringOrNumber); +export const schemaBoolean = rt.boolean; +export const schemaBooleanArray = rt.array(schemaBoolean); +const schemaGeoPointCoords = rt.type({ + type: schemaString, + coordinates: schemaNumberArray, +}); +const schemaGeoPointString = schemaString; +const schemaGeoPointLatLon = rt.type({ + lat: schemaNumber, + lon: schemaNumber, +}); +const schemaGeoPointLocation = rt.type({ + location: schemaNumberArray, +}); +const schemaGeoPointLocationString = rt.type({ + location: schemaString, +}); +export const schemaGeoPoint = rt.union([ + schemaGeoPointCoords, + schemaGeoPointString, + schemaGeoPointLatLon, + schemaGeoPointLocation, + schemaGeoPointLocationString, +]); +export const schemaGeoPointArray = rt.array(schemaGeoPoint); +// prettier-ignore +const DefaultAlertRequired = rt.type({ +}); +// prettier-ignore +const DefaultAlertOptional = rt.partial({ +}); + +// prettier-ignore +export const DefaultAlertSchema = rt.intersection([DefaultAlertRequired, DefaultAlertOptional, AlertSchema]); +// prettier-ignore +export type DefaultAlert = rt.TypeOf; diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/ecs_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/ecs_schema.ts index b3bc0eb161720..2852039b1a9dd 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/ecs_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/ecs_schema.ts @@ -70,6 +70,7 @@ const EcsRequired = rt.type({ '@timestamp': schemaDate, 'ecs.version': schemaString, }); +// prettier-ignore const EcsOptional = rt.partial({ 'agent.build.original': schemaString, 'agent.ephemeral_id': schemaString, diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/legacy_alert_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/legacy_alert_schema.ts index faecf4c0252ca..a2031b0468cf5 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/legacy_alert_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/legacy_alert_schema.ts @@ -68,6 +68,7 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); // prettier-ignore const LegacyAlertRequired = rt.type({ }); +// prettier-ignore const LegacyAlertOptional = rt.partial({ 'ecs.version': schemaString, 'kibana.alert.risk_score': schemaNumber, diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/ml_anomaly_detection_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/ml_anomaly_detection_schema.ts index 591a51b2fec57..e5dd543f17f23 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/ml_anomaly_detection_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/ml_anomaly_detection_schema.ts @@ -69,6 +69,7 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); const MlAnomalyDetectionAlertRequired = rt.type({ 'kibana.alert.job_id': schemaString, }); +// prettier-ignore const MlAnomalyDetectionAlertOptional = rt.partial({ 'kibana.alert.anomaly_score': schemaNumber, 'kibana.alert.anomaly_timestamp': schemaDate, diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_apm_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_apm_schema.ts index 41431d4581aa9..eb69b9a5e3250 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_apm_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_apm_schema.ts @@ -69,6 +69,7 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); // prettier-ignore const ObservabilityApmAlertRequired = rt.type({ }); +// prettier-ignore const ObservabilityApmAlertOptional = rt.partial({ 'agent.name': schemaString, 'error.grouping_key': schemaString, diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_logs_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_logs_schema.ts index dd87a5b0e6c9d..373b0af5b32a2 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_logs_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_logs_schema.ts @@ -70,6 +70,7 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); // prettier-ignore const ObservabilityLogsAlertRequired = rt.type({ }); +// prettier-ignore const ObservabilityLogsAlertOptional = rt.partial({ 'kibana.alert.context': schemaUnknown, 'kibana.alert.evaluation.threshold': schemaStringOrNumber, diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_metrics_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_metrics_schema.ts index 2e34e72f88257..7b26dc924e2fb 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_metrics_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_metrics_schema.ts @@ -70,6 +70,7 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); // prettier-ignore const ObservabilityMetricsAlertRequired = rt.type({ }); +// prettier-ignore const ObservabilityMetricsAlertOptional = rt.partial({ 'kibana.alert.context': schemaUnknown, 'kibana.alert.evaluation.threshold': schemaStringOrNumber, diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_slo_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_slo_schema.ts index 523e79ecf07a9..0485ccaa93318 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_slo_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_slo_schema.ts @@ -69,6 +69,7 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); // prettier-ignore const ObservabilitySloAlertRequired = rt.type({ }); +// prettier-ignore const ObservabilitySloAlertOptional = rt.partial({ 'kibana.alert.context': schemaUnknown, 'kibana.alert.evaluation.threshold': schemaStringOrNumber, diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_uptime_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_uptime_schema.ts index d8693417f77dc..1401cdda70f1c 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_uptime_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/observability_uptime_schema.ts @@ -69,6 +69,7 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); // prettier-ignore const ObservabilityUptimeAlertRequired = rt.type({ }); +// prettier-ignore const ObservabilityUptimeAlertOptional = rt.partial({ 'agent.name': schemaString, 'anomaly.bucket_span.minutes': schemaString, diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/security_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/security_schema.ts index 5b79568cb485b..1f0f2a8e18108 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/security_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/security_schema.ts @@ -117,6 +117,7 @@ const SecurityAlertRequired = rt.type({ 'kibana.alert.uuid': schemaString, 'kibana.space_ids': schemaStringArray, }); +// prettier-ignore const SecurityAlertOptional = rt.partial({ 'ecs.version': schemaString, 'event.action': schemaString, diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/generated/stack_schema.ts b/packages/kbn-alerts-as-data-utils/src/schemas/generated/stack_schema.ts index 1588963385834..94ba544e4c96f 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/generated/stack_schema.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/generated/stack_schema.ts @@ -68,6 +68,7 @@ export const schemaGeoPointArray = rt.array(schemaGeoPoint); // prettier-ignore const StackAlertRequired = rt.type({ }); +// prettier-ignore const StackAlertOptional = rt.partial({ 'kibana.alert.evaluation.conditions': schemaString, 'kibana.alert.evaluation.threshold': schemaStringOrNumber, diff --git a/packages/kbn-alerts-as-data-utils/src/schemas/index.ts b/packages/kbn-alerts-as-data-utils/src/schemas/index.ts index 28da937087cf1..9a56c90682016 100644 --- a/packages/kbn-alerts-as-data-utils/src/schemas/index.ts +++ b/packages/kbn-alerts-as-data-utils/src/schemas/index.ts @@ -14,6 +14,7 @@ import type { ObservabilitySloAlert } from './generated/observability_slo_schema import type { ObservabilityUptimeAlert } from './generated/observability_uptime_schema'; import type { SecurityAlert } from './generated/security_schema'; import type { MlAnomalyDetectionAlert } from './generated/ml_anomaly_detection_schema'; +import type { DefaultAlert } from './generated/default_schema'; export * from './create_schema_from_field_map'; @@ -26,6 +27,7 @@ export type { ObservabilityUptimeAlert } from './generated/observability_uptime_ export type { SecurityAlert } from './generated/security_schema'; export type { StackAlert } from './generated/stack_schema'; export type { MlAnomalyDetectionAlert } from './generated/ml_anomaly_detection_schema'; +export type { DefaultAlert } from './generated/default_schema'; export type AADAlert = | Alert @@ -35,4 +37,5 @@ export type AADAlert = | ObservabilitySloAlert | ObservabilityUptimeAlert | SecurityAlert - | MlAnomalyDetectionAlert; + | MlAnomalyDetectionAlert + | DefaultAlert; diff --git a/packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts b/packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts index 0427570b1cbc6..e231a360ba36e 100644 --- a/packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts +++ b/packages/kbn-rule-data-utils/src/alerts_as_data_rbac.ts @@ -24,6 +24,8 @@ export const AlertConsumers = { SIEM: 'siem', UPTIME: 'uptime', ML: 'ml', + STACK_ALERTS: 'stackAlerts', + EXAMPLE: 'AlertingExample', } as const; export type AlertConsumers = typeof AlertConsumers[keyof typeof AlertConsumers]; export type STATUS_VALUES = 'open' | 'acknowledged' | 'closed' | 'in-progress'; // TODO: remove 'in-progress' after migration to 'acknowledged' diff --git a/x-pack/examples/alerting_example/server/plugin.ts b/x-pack/examples/alerting_example/server/plugin.ts index 32a55020ed524..8aee6fb49dbe5 100644 --- a/x-pack/examples/alerting_example/server/plugin.ts +++ b/x-pack/examples/alerting_example/server/plugin.ts @@ -12,9 +12,9 @@ import { DEFAULT_APP_CATEGORIES } from '@kbn/core-application-common'; import { PluginSetupContract as AlertingSetup } from '@kbn/alerting-plugin/server'; import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server'; -import { alertType as alwaysFiringAlert } from './alert_types/always_firing'; -import { alertType as peopleInSpaceAlert } from './alert_types/astros'; -import { alertType as patternAlert } from './alert_types/pattern'; +import { ruleType as alwaysFiringRule } from './rule_types/always_firing'; +import { ruleType as peopleInSpaceRule } from './rule_types/astros'; +import { ruleType as patternRule } from './rule_types/pattern'; // can't import static code from another plugin to support examples functional test const INDEX_THRESHOLD_ID = '.index-threshold'; import { ALERTING_EXAMPLE_APP_ID } from '../common/constants'; @@ -27,9 +27,9 @@ export interface AlertingExampleDeps { export class AlertingExamplePlugin implements Plugin { public setup(core: CoreSetup, { alerting, features }: AlertingExampleDeps) { - alerting.registerType(alwaysFiringAlert); - alerting.registerType(peopleInSpaceAlert); - alerting.registerType(patternAlert); + alerting.registerType(alwaysFiringRule); + alerting.registerType(peopleInSpaceRule); + alerting.registerType(patternRule); features.registerKibanaFeature({ id: ALERTING_EXAMPLE_APP_ID, @@ -41,15 +41,15 @@ export class AlertingExamplePlugin implements Plugin = { id: 'example.always-firing', name: 'Always firing', @@ -61,15 +75,20 @@ export const alertType: RuleType< params: { instances = DEFAULT_INSTANCES_TO_GENERATE, thresholds }, state, }) { + const { alertsClient } = services; + if (!alertsClient) { + throw new AlertsClientError(); + } const count = (state.count ?? 0) + 1; range(instances) .map(() => uuidv4()) .forEach((id: string) => { - services.alertFactory - .create(id) - .replaceState({ triggerdOnCycle: count }) - .scheduleActions(getTShirtSizeByIdAndThreshold(id, thresholds)); + alertsClient.report({ + id, + actionGroup: getTShirtSizeByIdAndThreshold(id, thresholds), + state: { triggerdOnCycle: count }, + }); }); return { @@ -92,4 +111,5 @@ export const alertType: RuleType< ), }), }, + alerts: DEFAULT_AAD_CONFIG, }; diff --git a/x-pack/examples/alerting_example/server/alert_types/astros.ts b/x-pack/examples/alerting_example/server/rule_types/astros.ts similarity index 76% rename from x-pack/examples/alerting_example/server/alert_types/astros.ts rename to x-pack/examples/alerting_example/server/rule_types/astros.ts index fda111bfcf86b..a029cea58456a 100644 --- a/x-pack/examples/alerting_example/server/alert_types/astros.ts +++ b/x-pack/examples/alerting_example/server/rule_types/astros.ts @@ -6,8 +6,15 @@ */ import axios from 'axios'; -import { RuleType } from '@kbn/alerting-plugin/server'; +import { + DEFAULT_AAD_CONFIG, + RuleType, + RuleTypeParams, + RuleTypeState, + AlertsClientError, +} from '@kbn/alerting-plugin/server'; import { schema } from '@kbn/config-schema'; +import type { DefaultAlert } from '@kbn/alerts-as-data-utils'; import { Operator, Craft, ALERTING_EXAMPLE_APP_ID } from '../../common/constants'; interface PeopleInSpace { @@ -18,6 +25,18 @@ interface PeopleInSpace { number: number; } +interface Params extends RuleTypeParams { + outerSpaceCapacity: number; + craft: string; + op: string; +} +interface State extends RuleTypeState { + peopleInSpace: number; +} +interface AlertState { + craft: string; +} + function getOperator(op: string) { switch (op) { case Operator.AreAbove: @@ -40,14 +59,15 @@ function getCraftFilter(craft: string) { craft === Craft.OuterSpace ? true : craft === person.craft; } -export const alertType: RuleType< - { outerSpaceCapacity: number; craft: string; op: string }, +export const ruleType: RuleType< + Params, never, - { peopleInSpace: number }, - { craft: string }, + State, + AlertState, never, 'default', - 'hasLandedBackOnEarth' + 'hasLandedBackOnEarth', + DefaultAlert > = { id: 'example.people-in-space', name: 'People In Space Right Now', @@ -60,6 +80,10 @@ export const alertType: RuleType< name: 'Has landed back on Earth', }, async executor({ services, params }) { + const { alertsClient } = services; + if (!alertsClient) { + throw new AlertsClientError(); + } const { outerSpaceCapacity, craft: craftToTriggerBy, op } = params; const response = await axios.get('http://api.open-notify.org/astros.json'); @@ -71,7 +95,7 @@ export const alertType: RuleType< if (getOperator(op)(peopleInCraft.length, outerSpaceCapacity)) { peopleInCraft.forEach(({ craft, name }) => { - services.alertFactory.create(name).replaceState({ craft }).scheduleActions('default'); + alertsClient.report({ id: name, actionGroup: 'default', state: { craft } }); }); } @@ -86,6 +110,7 @@ export const alertType: RuleType< getViewInAppRelativeUrl({ rule }) { return `/app/${ALERTING_EXAMPLE_APP_ID}/astros/${rule.id}`; }, + alerts: DEFAULT_AAD_CONFIG, validate: { params: schema.object({ outerSpaceCapacity: schema.number(), diff --git a/x-pack/examples/alerting_example/server/alert_types/pattern.md b/x-pack/examples/alerting_example/server/rule_types/pattern.md similarity index 100% rename from x-pack/examples/alerting_example/server/alert_types/pattern.md rename to x-pack/examples/alerting_example/server/rule_types/pattern.md diff --git a/x-pack/examples/alerting_example/server/alert_types/pattern.test.ts b/x-pack/examples/alerting_example/server/rule_types/pattern.test.ts similarity index 90% rename from x-pack/examples/alerting_example/server/alert_types/pattern.test.ts rename to x-pack/examples/alerting_example/server/rule_types/pattern.test.ts index a9e48cdc7925e..4a3254c6007a2 100644 --- a/x-pack/examples/alerting_example/server/alert_types/pattern.test.ts +++ b/x-pack/examples/alerting_example/server/rule_types/pattern.test.ts @@ -6,7 +6,7 @@ */ import { loggingSystemMock } from '@kbn/core/server/mocks'; -import { alertType } from './pattern'; +import { ruleType } from './pattern'; const logger = loggingSystemMock.create().get(); @@ -22,9 +22,10 @@ describe('pattern rule type', () => { const options = { params, state, + services: { alertsClient: {} }, }; try { - await alertType.executor(options as any); + await ruleType.executor(options as any); } catch (err) { expect(err.message).toMatchInlineSnapshot( `"errors in patterns: pattern for instA contains invalid string: \\"nope\\", pattern for instB contains invalid string: \\"hallo!\\""` @@ -50,7 +51,7 @@ describe('pattern rule type', () => { let result: any; for (let i = 0; i < 6; i++) { - result = await alertType.executor(options as any); + result = await ruleType.executor(options as any); options.state = result.state; } @@ -182,20 +183,15 @@ describe('pattern rule type', () => { }); }); -// services.alertFactory.create(instance).scheduleActions('default', context); type ScheduledAction = [string, string, any]; function getServices() { const scheduledActions: ScheduledAction[] = []; return { scheduledActions, - alertFactory: { - create(instance: string) { - return { - scheduleActions(actionGroup: string, context: any) { - scheduledActions.push([instance, actionGroup, context]); - }, - }; + alertsClient: { + report(reported: { id: string; actionGroup: string; context: any }) { + scheduledActions.push([reported.id, reported.actionGroup, reported.context]); }, }, }; diff --git a/x-pack/examples/alerting_example/server/alert_types/pattern.ts b/x-pack/examples/alerting_example/server/rule_types/pattern.ts similarity index 85% rename from x-pack/examples/alerting_example/server/alert_types/pattern.ts rename to x-pack/examples/alerting_example/server/rule_types/pattern.ts index 16177fa5de7dd..c78b0a3650f1c 100644 --- a/x-pack/examples/alerting_example/server/alert_types/pattern.ts +++ b/x-pack/examples/alerting_example/server/rule_types/pattern.ts @@ -10,7 +10,11 @@ import { RuleType as BaseRuleType, RuleTypeState, RuleExecutorOptions as BaseRuleExecutorOptions, + DEFAULT_AAD_CONFIG, + AlertsClientError, } from '@kbn/alerting-plugin/server'; +import type { DefaultAlert } from '@kbn/alerts-as-data-utils'; +import { RecoveredActionGroupId } from '@kbn/alerting-plugin/common'; type Params = TypeOf; const Params = schema.object( @@ -41,10 +45,19 @@ interface State extends RuleTypeState { runs?: number; } -type RuleExecutorOptions = BaseRuleExecutorOptions; +type RuleExecutorOptions = BaseRuleExecutorOptions; -type RuleType = BaseRuleType; -export const alertType: RuleType = getPatternRuleType(); +type RuleType = BaseRuleType< + Params, + never, + State, + {}, + {}, + 'default', + RecoveredActionGroupId, + DefaultAlert +>; +export const ruleType: RuleType = getPatternRuleType(); function getPatternRuleType(): RuleType { return { @@ -57,6 +70,7 @@ function getPatternRuleType(): RuleType { minimumLicenseRequired: 'basic', isExportable: true, executor, + alerts: DEFAULT_AAD_CONFIG, validate: { params: Params, }, @@ -65,6 +79,10 @@ function getPatternRuleType(): RuleType { async function executor(options: RuleExecutorOptions): Promise<{ state: State }> { const { services, state, params } = options; + const { alertsClient } = services; + if (!alertsClient) { + throw new AlertsClientError(); + } if (state.runs == null) { state.runs = 0; @@ -96,7 +114,7 @@ async function executor(options: RuleExecutorOptions): Promise<{ state: State }> switch (action) { case 'a': const context = { patternIndex, action, pattern, runs }; - services.alertFactory.create(instance).scheduleActions('default', context); + alertsClient.report({ id: instance, actionGroup: 'default', context }); break; case '-': break; diff --git a/x-pack/examples/alerting_example/tsconfig.json b/x-pack/examples/alerting_example/tsconfig.json index 60d23740ef343..fa7c9ae59d1ba 100644 --- a/x-pack/examples/alerting_example/tsconfig.json +++ b/x-pack/examples/alerting_example/tsconfig.json @@ -27,5 +27,6 @@ "@kbn/core-application-common", "@kbn/shared-ux-router", "@kbn/config-schema", + "@kbn/alerts-as-data-utils", ] } diff --git a/x-pack/plugins/alerting/server/index.ts b/x-pack/plugins/alerting/server/index.ts index be98d18d831b8..5634c3e2eb74e 100644 --- a/x-pack/plugins/alerting/server/index.ts +++ b/x-pack/plugins/alerting/server/index.ts @@ -34,6 +34,8 @@ export type { GetViewInAppRelativeUrlFnOpts, DataStreamAdapter, } from './types'; +export { DEFAULT_AAD_CONFIG } from './types'; +export { RULE_SAVED_OBJECT_TYPE } from './saved_objects'; export { RuleNotifyWhen } from '../common'; export { DEFAULT_MAX_EPHEMERAL_ACTIONS_PER_ALERT } from './config'; export type { PluginSetupContract, PluginStartContract } from './plugin'; diff --git a/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.test.ts b/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.test.ts index 94d8aa923ea19..e54e2570ada09 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.test.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.test.ts @@ -16,6 +16,7 @@ describe('rule_type_registry_deprecated_consumers', () => { expect(Object.keys(ruleTypeIdWithValidLegacyConsumers)).toMatchInlineSnapshot(` Array [ "example.always-firing", + "example.people-in-space", "transform_health", ".index-threshold", ".geo-containment", diff --git a/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.ts b/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.ts index 7394736968609..ecb96f0248a35 100644 --- a/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.ts +++ b/x-pack/plugins/alerting/server/rule_type_registry_deprecated_consumers.ts @@ -9,6 +9,7 @@ import { ALERTS_FEATURE_ID } from './types'; export const ruleTypeIdWithValidLegacyConsumers: Record = { 'example.always-firing': [ALERTS_FEATURE_ID], + 'example.people-in-space': [ALERTS_FEATURE_ID], transform_health: [ALERTS_FEATURE_ID], '.index-threshold': [ALERTS_FEATURE_ID], '.geo-containment': [ALERTS_FEATURE_ID], diff --git a/x-pack/plugins/alerting/server/types.ts b/x-pack/plugins/alerting/server/types.ts index 6dc28eef47861..5a8c44f8523e5 100644 --- a/x-pack/plugins/alerting/server/types.ts +++ b/x-pack/plugins/alerting/server/types.ts @@ -22,7 +22,7 @@ import { } from '@kbn/core/server'; import type { PublicMethodsOf } from '@kbn/utility-types'; import { SharePluginStart } from '@kbn/share-plugin/server'; -import type { FieldMap } from '@kbn/alerts-as-data-utils'; +import type { DefaultAlert, FieldMap } from '@kbn/alerts-as-data-utils'; import { Alert } from '@kbn/alerts-as-data-utils'; import { Filter } from '@kbn/es-query'; import { ActionsApiRequestHandlerContext } from '@kbn/actions-plugin/server'; @@ -211,6 +211,12 @@ export type FormatAlert = ( alert: Partial ) => Partial; +export const DEFAULT_AAD_CONFIG: IRuleTypeAlerts = { + context: `default`, + mappings: { fieldMap: {} }, + shouldWrite: true, +}; + export interface IRuleTypeAlerts { /** * Specifies the target alerts-as-data resource From 29fa70df3708b6b2085f9e59e1eb4478b4b19004 Mon Sep 17 00:00:00 2001 From: Zacqary Xeper Date: Wed, 17 Jan 2024 11:28:16 -0600 Subject: [PATCH 02/12] [RAM] Add example log system action type --- .../triggers_actions_ui_example/kibana.jsonc | 9 +- .../system_log_example/system_log_example.tsx | 54 +++++++++ .../system_log_example_params.tsx | 63 +++++++++++ .../public/connector_types/types/index.ts | 10 ++ .../public/plugin.tsx | 3 + .../connector_types/system_log_example.ts | 99 +++++++++++++++++ .../server/index.ts | 13 +++ .../server/plugin.ts | 33 ++++++ .../apis/connector_types/schemas/v1.ts | 1 + .../routes/connector/apis/connectors/index.ts | 12 ++ .../apis/connectors/schemas/latest.ts | 8 ++ .../connector/apis/connectors/schemas/v1.ts | 12 ++ .../connector/apis/connectors/types/latest.ts | 8 ++ .../connector/apis/connectors/types/v1.ts | 11 ++ .../routes/connector/get_all/get_all.ts | 14 ++- .../routes/connector/list_types/list_types.ts | 5 +- x-pack/plugins/alerting/common/rule.ts | 5 + .../rule/methods/resolve/resolve_rule.ts | 18 +-- .../server/routes/lib/rewrite_rule.ts | 2 + .../rule/apis/resolve/resolve_rule_route.ts | 30 ++--- .../alerting/server/routes/update_rule.ts | 6 +- .../saved_objects/schemas/raw_rule/v1.ts | 2 +- .../action_connector_api/connector_types.ts | 8 +- .../lib/action_connector_api/connectors.ts | 7 +- .../public/application/lib/rule_api/update.ts | 22 +++- .../action_connector_form/action_form.tsx | 105 +++++++++++++----- .../action_type_form.tsx | 92 ++++++++------- .../sections/rule_form/rule_edit.tsx | 25 ++++- 28 files changed, 568 insertions(+), 109 deletions(-) create mode 100644 x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example.tsx create mode 100644 x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example_params.tsx create mode 100644 x-pack/examples/triggers_actions_ui_example/public/connector_types/types/index.ts create mode 100644 x-pack/examples/triggers_actions_ui_example/server/connector_types/system_log_example.ts create mode 100644 x-pack/examples/triggers_actions_ui_example/server/index.ts create mode 100644 x-pack/examples/triggers_actions_ui_example/server/plugin.ts create mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts create mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/latest.ts create mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts create mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connectors/types/latest.ts create mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts diff --git a/x-pack/examples/triggers_actions_ui_example/kibana.jsonc b/x-pack/examples/triggers_actions_ui_example/kibana.jsonc index 9de248572a5bb..e3e6c8adc4a97 100644 --- a/x-pack/examples/triggers_actions_ui_example/kibana.jsonc +++ b/x-pack/examples/triggers_actions_ui_example/kibana.jsonc @@ -4,7 +4,7 @@ "owner": "@elastic/response-ops", "plugin": { "id": "triggersActionsUiExample", - "server": false, + "server": true, "browser": true, "requiredPlugins": [ "triggersActionsUi", @@ -12,10 +12,9 @@ "alerting", "developerExamples", "kibanaReact", - "cases" - ], - "optionalPlugins": [ - "spaces" + "cases", + "actions" ], + "optionalPlugins": ["spaces"] } } diff --git a/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example.tsx b/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example.tsx new file mode 100644 index 0000000000000..cdd6551c91bcf --- /dev/null +++ b/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example.tsx @@ -0,0 +1,54 @@ +/* + * 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 { lazy } from 'react'; +import { i18n } from '@kbn/i18n'; +import type { + ActionTypeModel as ConnectorTypeModel, + GenericValidationResult, +} from '@kbn/triggers-actions-ui-plugin/public/types'; +import { SystemLogActionParams } from '../types'; + +export function getConnectorType(): ConnectorTypeModel { + return { + id: '.system-log-example', + iconClass: 'logsApp', + selectMessage: i18n.translate( + 'xpack.stackConnectors.components.systemLogExample.selectMessageText', + { + defaultMessage: 'Example of a system action that sends logs to the Kibana server', + } + ), + actionTypeTitle: i18n.translate( + 'xpack.stackConnectors.components.serverLog.connectorTypeTitle', + { + defaultMessage: 'Send to System log - Example', + } + ), + validateParams: ( + actionParams: SystemLogActionParams + ): Promise>> => { + const errors = { + message: new Array(), + }; + const validationResult = { errors }; + if (!actionParams.message?.length) { + errors.message.push( + i18n.translate( + 'xpack.stackConnectors.components.serverLog.error.requiredServerLogMessageText', + { + defaultMessage: 'Message is required.', + } + ) + ); + } + return Promise.resolve(validationResult); + }, + actionConnectorFields: null, + actionParamsFields: lazy(() => import('./system_log_example_params')), + }; +} diff --git a/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example_params.tsx b/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example_params.tsx new file mode 100644 index 0000000000000..b1d7e186dd8b3 --- /dev/null +++ b/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example_params.tsx @@ -0,0 +1,63 @@ +/* + * 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 React, { useEffect, useState } from 'react'; +import { i18n } from '@kbn/i18n'; +import type { ActionParamsProps } from '@kbn/triggers-actions-ui-plugin/public'; +import { TextAreaWithMessageVariables } from '@kbn/triggers-actions-ui-plugin/public'; +import { SystemLogActionParams } from '../types'; + +export const ServerLogParamsFields: React.FunctionComponent< + ActionParamsProps +> = ({ + actionParams, + editAction, + index, + errors, + messageVariables, + defaultMessage, + useDefaultMessage, +}) => { + const { message } = actionParams; + + const [[isUsingDefault, defaultMessageUsed], setDefaultMessageUsage] = useState< + [boolean, string | undefined] + >([false, defaultMessage]); + useEffect(() => { + if ( + useDefaultMessage || + !actionParams?.message || + (isUsingDefault && + actionParams?.message === defaultMessageUsed && + defaultMessageUsed !== defaultMessage) + ) { + setDefaultMessageUsage([true, defaultMessage]); + editAction('message', defaultMessage, index); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [defaultMessage]); + + return ( + + ); +}; + +// eslint-disable-next-line import/no-default-export +export { ServerLogParamsFields as default }; diff --git a/x-pack/examples/triggers_actions_ui_example/public/connector_types/types/index.ts b/x-pack/examples/triggers_actions_ui_example/public/connector_types/types/index.ts new file mode 100644 index 0000000000000..98181b856c3fa --- /dev/null +++ b/x-pack/examples/triggers_actions_ui_example/public/connector_types/types/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export interface SystemLogActionParams { + message: string; +} diff --git a/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx b/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx index 8979c138242fa..d1da02ef08328 100644 --- a/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx +++ b/x-pack/examples/triggers_actions_ui_example/public/plugin.tsx @@ -29,6 +29,7 @@ import { } from '@kbn/triggers-actions-ui-plugin/public/types'; import { SortCombinations } from '@elastic/elasticsearch/lib/api/types'; import { EuiDataGridColumn } from '@elastic/eui'; +import { getConnectorType as getSystemLogExampleConnectorType } from './connector_types/system_log_example/system_log_example'; export interface TriggersActionsUiExamplePublicSetupDeps { alerting: AlertingSetup; @@ -151,6 +152,8 @@ export class TriggersActionsUiExamplePlugin }; alertsTableConfigurationRegistry.register(config); + + triggersActionsUi.actionTypeRegistry.register(getSystemLogExampleConnectorType()); } public stop() {} diff --git a/x-pack/examples/triggers_actions_ui_example/server/connector_types/system_log_example.ts b/x-pack/examples/triggers_actions_ui_example/server/connector_types/system_log_example.ts new file mode 100644 index 0000000000000..e952a7ed01195 --- /dev/null +++ b/x-pack/examples/triggers_actions_ui_example/server/connector_types/system_log_example.ts @@ -0,0 +1,99 @@ +/* + * 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 { i18n } from '@kbn/i18n'; +import { schema, TypeOf } from '@kbn/config-schema'; + +import { LogMeta } from '@kbn/core/server'; +import type { + ActionType as ConnectorType, + ActionTypeExecutorOptions as ConnectorTypeExecutorOptions, + ActionTypeExecutorResult as ConnectorTypeExecutorResult, +} from '@kbn/actions-plugin/server/types'; +import { + AlertingConnectorFeatureId, + UptimeConnectorFeatureId, +} from '@kbn/actions-plugin/common/connector_feature_config'; +import { ConnectorAdapter } from '@kbn/alerting-plugin/server'; + +// see: https://en.wikipedia.org/wiki/Unicode_control_characters +// but don't include tabs (0x09), they're fine +const CONTROL_CHAR_PATTERN = /[\x00-\x08]|[\x0A-\x1F]|[\x7F-\x9F]|[\u2028-\u2029]/g; + +// replaces control characters in string with ;, but leaves tabs +function withoutControlCharacters(s: string): string { + return s.replace(CONTROL_CHAR_PATTERN, ';'); +} + +export type ServerLogConnectorType = ConnectorType<{}, {}, ActionParamsType>; +export type ServerLogConnectorTypeExecutorOptions = ConnectorTypeExecutorOptions< + {}, + {}, + ActionParamsType +>; + +// params definition + +export type ActionParamsType = TypeOf; + +const ParamsSchema = schema.object({ + message: schema.string(), +}); + +export const ConnectorTypeId = '.system-log-example'; +// connector type definition +export function getConnectorType(): ServerLogConnectorType { + return { + id: ConnectorTypeId, + isSystemActionType: true, + minimumLicenseRequired: 'gold', // Third party action types require at least gold + name: i18n.translate('xpack.stackConnectors.systemLogExample.title', { + defaultMessage: 'System log - example', + }), + supportedFeatureIds: [AlertingConnectorFeatureId, UptimeConnectorFeatureId], + validate: { + config: { schema: schema.object({}, { defaultValue: {} }) }, + secrets: { schema: schema.object({}, { defaultValue: {} }) }, + params: { + schema: ParamsSchema, + }, + }, + executor, + }; +} + +export const connectorAdapter: ConnectorAdapter = { + connectorTypeId: ConnectorTypeId, + ruleActionParamsSchema: ParamsSchema, + buildActionParams: ({ alerts, rule, params, spaceId, ruleUrl }) => { + return { ...params }; + }, +}; + +// action executor + +async function executor( + execOptions: ServerLogConnectorTypeExecutorOptions +): Promise> { + const { actionId, params, logger } = execOptions; + const sanitizedMessage = withoutControlCharacters(params.message); + try { + logger.info(`SYSTEM ACTION EXAMPLE Server log: ${sanitizedMessage}`); + } catch (err) { + const message = i18n.translate('xpack.stackConnectors.serverLog.errorLoggingErrorMessage', { + defaultMessage: 'error logging message', + }); + return { + status: 'error', + message, + serviceMessage: err.message, + actionId, + }; + } + + return { status: 'ok', actionId }; +} diff --git a/x-pack/examples/triggers_actions_ui_example/server/index.ts b/x-pack/examples/triggers_actions_ui_example/server/index.ts new file mode 100644 index 0000000000000..6479960a80604 --- /dev/null +++ b/x-pack/examples/triggers_actions_ui_example/server/index.ts @@ -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 { PluginInitializer } from '@kbn/core/server'; + +export const plugin: PluginInitializer = async () => { + const { TriggersActionsUiExamplePlugin } = await import('./plugin'); + return new TriggersActionsUiExamplePlugin(); +}; diff --git a/x-pack/examples/triggers_actions_ui_example/server/plugin.ts b/x-pack/examples/triggers_actions_ui_example/server/plugin.ts new file mode 100644 index 0000000000000..6d55bbb2cb55d --- /dev/null +++ b/x-pack/examples/triggers_actions_ui_example/server/plugin.ts @@ -0,0 +1,33 @@ +/* + * 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 { Plugin, CoreSetup } from '@kbn/core/server'; + +import { PluginSetupContract as ActionsSetup } from '@kbn/actions-plugin/server'; +import { PluginSetupContract as AlertingSetup } from '@kbn/alerting-plugin/server'; + +import { + getConnectorType as getSystemLogExampleConnectorType, + connectorAdapter as systemLogConnectorAdapter, +} from './connector_types/system_log_example'; + +// this plugin's dependencies +export interface TriggersActionsUiExampleDeps { + alerting: AlertingSetup; + actions: ActionsSetup; +} +export class TriggersActionsUiExamplePlugin + implements Plugin +{ + public setup(core: CoreSetup, { actions, alerting }: TriggersActionsUiExampleDeps) { + actions.registerType(getSystemLogExampleConnectorType()); + alerting.registerConnectorAdapter(systemLogConnectorAdapter); + } + + public start() {} + public stop() {} +} diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/v1.ts b/x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/v1.ts index bdbc01efc4b7a..243c04f2c00f1 100644 --- a/x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/v1.ts +++ b/x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/v1.ts @@ -9,4 +9,5 @@ import { schema } from '@kbn/config-schema'; export const connectorTypesQuerySchema = schema.object({ feature_id: schema.maybe(schema.string()), + include_system_actions: schema.maybe(schema.boolean()), }); diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts new file mode 100644 index 0000000000000..2903655aff01a --- /dev/null +++ b/x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts @@ -0,0 +1,12 @@ +/* + * 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 { connectorsQuerySchema } from './schemas/latest'; +export type { ConnectorsRequestQuery } from './types/latest'; + +export { connectorsQuerySchema as connectorsQuerySchemaV1 } from './schemas/v1'; +export type { ConnectorsRequestQuery as ConnectorsRequestQueryV1 } from './types/v1'; diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/latest.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/latest.ts new file mode 100644 index 0000000000000..25300c97a6d2e --- /dev/null +++ b/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/latest.ts @@ -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'; diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts new file mode 100644 index 0000000000000..cc70f68f9777d --- /dev/null +++ b/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts @@ -0,0 +1,12 @@ +/* + * 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'; + +export const connectorsQuerySchema = schema.object({ + include_system_actions: schema.maybe(schema.boolean()), +}); diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/latest.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/latest.ts new file mode 100644 index 0000000000000..25300c97a6d2e --- /dev/null +++ b/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/latest.ts @@ -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'; diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts new file mode 100644 index 0000000000000..142d8ec50cfb3 --- /dev/null +++ b/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts @@ -0,0 +1,11 @@ +/* + * 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 { connectorsQuerySchemaV1 } from '..'; + +export type ConnectorsRequestQuery = TypeOf; diff --git a/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts b/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts index 18885f90512e8..f5338e05af108 100644 --- a/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts @@ -7,6 +7,10 @@ import { IRouter } from '@kbn/core/server'; import { AllConnectorsResponseV1 } from '../../../../common/routes/connector/response'; +import { + connectorsQuerySchemaV1, + ConnectorsRequestQueryV1, +} from '../../../../common/routes/connector/apis/connectors'; import { transformGetAllConnectorsResponseV1 } from './transforms'; import { ActionsRequestHandlerContext } from '../../../types'; import { BASE_ACTION_API_PATH } from '../../../../common'; @@ -20,12 +24,18 @@ export const getAllConnectorsRoute = ( router.get( { path: `${BASE_ACTION_API_PATH}/connectors`, - validate: {}, + validate: { + query: connectorsQuerySchemaV1, + }, }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { + const query: ConnectorsRequestQueryV1 = req.query; + const actionsClient = (await context.actions).getActionsClient(); - const result = await actionsClient.getAll(); + const result = await actionsClient.getAll({ + includeSystemActions: query?.include_system_actions, + }); const responseBody: AllConnectorsResponseV1[] = transformGetAllConnectorsResponseV1(result); return res.ok({ body: responseBody }); diff --git a/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts b/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts index 078c51743c4d9..1b84ac5b25815 100644 --- a/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts +++ b/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts @@ -35,7 +35,10 @@ export const listTypesRoute = ( // Assert versioned inputs const query: ConnectorTypesRequestQueryV1 = req.query; - const connectorTypes = await actionsClient.listTypes({ featureId: query?.feature_id }); + const connectorTypes = await actionsClient.listTypes({ + featureId: query?.feature_id, + includeSystemActionTypes: query?.include_system_actions, + }); const responseBody: ConnectorTypesResponseV1[] = transformListTypesResponseV1(connectorTypes); diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index 28093ff7a5a65..eb5e955f4a6de 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -142,6 +142,11 @@ export interface RuleSystemAction { params: RuleActionParams; type: typeof RuleActionTypes.SYSTEM; useAlertDataForTemplate?: boolean; + // Workaround for typescript issues where boolean types are only defined in one of the union type members + // Allow frequency and alertsFilter to be deemed present in a system action, but require them to be undefined + // This prevents code like action.frequency?.someProperty from throwing an error + frequency?: undefined; + alertsFilter?: undefined; } export type RuleAction = RuleDefaultAction | RuleSystemAction; diff --git a/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve_rule.ts b/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve_rule.ts index cbde52a44b1fe..fe670b9574203 100644 --- a/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve_rule.ts +++ b/x-pack/plugins/alerting/server/application/rule/methods/resolve/resolve_rule.ts @@ -65,13 +65,17 @@ Promise> { }) ); - const ruleDomain = transformRuleAttributesToRuleDomain(result.attributes, { - id: result.id, - logger: context.logger, - ruleType: context.ruleTypeRegistry.get(result.attributes.alertTypeId), - references: result.references, - includeSnoozeData, - }); + const ruleDomain = transformRuleAttributesToRuleDomain( + result.attributes, + { + id: result.id, + logger: context.logger, + ruleType: context.ruleTypeRegistry.get(result.attributes.alertTypeId), + references: result.references, + includeSnoozeData, + }, + context.isSystemAction + ); const rule = transformRuleDomainToRule(ruleDomain); diff --git a/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.ts b/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.ts index 816bd485d14fe..9f8dacbff4031 100644 --- a/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.ts +++ b/x-pack/plugins/alerting/server/routes/lib/rewrite_rule.ts @@ -69,6 +69,8 @@ export const rewriteRule = ({ if (type === RuleActionTypes.SYSTEM) { return { ...action, + id, + params, connector_type_id: actionTypeId, ...(typeof useAlertDataForTemplate !== 'undefined' ? { use_alert_data_for_template: useAlertDataForTemplate } diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.ts b/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.ts index 2e6f016b5f6ac..680f62eae9ca0 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.ts @@ -33,19 +33,23 @@ export const resolveRuleRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const rulesClient = (await context.alerting).getRulesClient(); - const params: ResolveRuleRequestParamsV1 = req.params; - const { id } = params; - // TODO (http-versioning): Remove this cast, this enables us to move forward - // without fixing all of other solution types - const rule = (await rulesClient.resolve({ - id, - includeSnoozeData: true, - })) as ResolvedRule; - const response: ResolveRuleResponseV1 = { - body: transformResolveResponseV1(rule), - }; - return res.ok(response); + try { + const rulesClient = (await context.alerting).getRulesClient(); + const params: ResolveRuleRequestParamsV1 = req.params; + const { id } = params; + // TODO (http-versioning): Remove this cast, this enables us to move forward + // without fixing all of other solution types + const rule = (await rulesClient.resolve({ + id, + includeSnoozeData: true, + })) as ResolvedRule; + const response: ResolveRuleResponseV1 = { + body: transformResolveResponseV1(rule), + }; + return res.ok(response); + } catch (e) { + throw e; + } }) ) ); diff --git a/x-pack/plugins/alerting/server/routes/update_rule.ts b/x-pack/plugins/alerting/server/routes/update_rule.ts index 02d59dc11db6c..c9cfa7962ac77 100644 --- a/x-pack/plugins/alerting/server/routes/update_rule.ts +++ b/x-pack/plugins/alerting/server/routes/update_rule.ts @@ -146,13 +146,15 @@ export const updateRuleRoute = ( router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { const rulesClient = (await context.alerting).getRulesClient(); - const { isSystemAction } = (await context.actions).getActionsClient(); + const actionsClient = (await context.actions).getActionsClient(); const { id } = req.params; const rule = req.body; try { const alertRes = await rulesClient.update( - rewriteBodyReq({ id, data: rule }, isSystemAction) + rewriteBodyReq({ id, data: rule }, (connectorId: string) => + actionsClient.isSystemAction(connectorId) + ) ); return res.ok({ body: rewriteBodyRes(alertRes), diff --git a/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts b/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts index 76c6241396dc3..3b15494a61ca4 100644 --- a/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts +++ b/x-pack/plugins/alerting/server/saved_objects/schemas/raw_rule/v1.ts @@ -194,7 +194,7 @@ const rawRuleAlertsFilterSchema = schema.object({ const rawRuleActionSchema = schema.object({ uuid: schema.maybe(schema.string()), - group: schema.string(), + group: schema.maybe(schema.string()), actionRef: schema.string(), actionTypeId: schema.string(), params: schema.recordOf(schema.string(), schema.any()), diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts index be16cfc65309e..6da3e16cd1909 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts @@ -43,11 +43,17 @@ export async function loadActionTypes({ { query: { feature_id: featureId, + include_system_actions: true, }, } ) : await http.get[0]>( - `${BASE_ACTION_API_PATH}/connector_types` + `${BASE_ACTION_API_PATH}/connector_types`, + { + query: { + include_system_actions: true, + }, + } ); return rewriteResponseRes(res); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts index 6938654b294ac..e76d110ef54f0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts @@ -39,7 +39,12 @@ const transformConnector: RewriteRequestCase< export async function loadAllActions({ http }: { http: HttpSetup }): Promise { const res = await http.get[0]>( - `${BASE_ACTION_API_PATH}/connectors` + `${BASE_ACTION_API_PATH}/connectors`, + { + query: { + include_system_actions: true, + }, + } ); return rewriteResponseRes(res) as ActionConnector[]; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts index 52158bfa2f034..dbabf74535807 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts @@ -7,6 +7,7 @@ import { HttpSetup } from '@kbn/core/public'; import { pick } from 'lodash'; import { RewriteResponseCase, AsApiContract } from '@kbn/actions-plugin/common'; +import { isSystemAction, SanitizedDefaultRuleAction } from '@kbn/alerting-plugin/common'; import { BASE_ALERTING_API_PATH } from '../../constants'; import { Rule, RuleUpdates } from '../../../types'; import { transformRule } from './common_transformations'; @@ -17,8 +18,21 @@ type RuleUpdatesBody = Pick< >; const rewriteBodyRequest: RewriteResponseCase = ({ actions, ...res }): any => ({ ...res, - actions: actions.map( - ({ group, id, params, frequency, uuid, alertsFilter, useAlertDataForTemplate }) => ({ + actions: actions.map((action) => { + if (isSystemAction(action)) { + const { id, params, uuid, useAlertDataForTemplate } = action; + return { + id, + params, + ...(typeof useAlertDataForTemplate !== 'undefined' + ? { use_alert_data_for_template: useAlertDataForTemplate } + : {}), + ...(uuid && { uuid }), + }; + } + const { group, id, params, frequency, uuid, alertsFilter, useAlertDataForTemplate } = + action as SanitizedDefaultRuleAction; + return { group, id, params, @@ -32,8 +46,8 @@ const rewriteBodyRequest: RewriteResponseCase = ({ actions, ... ? { use_alert_data_for_template: useAlertDataForTemplate } : {}), ...(uuid && { uuid }), - }) - ), + }; + }), }); export async function updateRule({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 6d91e1837b830..65049b007ce94 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -18,12 +18,15 @@ import { EuiKeyPadMenuItem, EuiToolTip, EuiLink, + EuiEmptyPrompt, + EuiText, } from '@elastic/eui'; import { ActionGroup, RuleActionAlertsFilterProperty, RuleActionFrequency, RuleActionParam, + RuleActionTypes, } from '@kbn/alerting-plugin/common'; import { v4 as uuidv4 } from 'uuid'; import { betaBadgeProps } from './beta_badge_props'; @@ -240,33 +243,57 @@ export const ActionForm = ({ } setIsAddActionPanelOpen(false); const allowGroupConnector = (actionTypeModel?.subtype ?? []).map((atm) => atm.id); + const isSystemAction = Boolean( + actionTypesIndex && actionTypesIndex[actionTypeModel.id]?.isSystemActionType + ); let actionTypeConnectors = connectors.filter( (field) => field.actionTypeId === actionTypeModel.id ); - if (actionTypeConnectors.length > 0) { - actions.push({ - id: '', - actionTypeId: actionTypeModel.id, - group: defaultActionGroupId, - params: {}, - frequency: defaultRuleFrequency, - uuid: uuidv4(), - }); + actions.push( + isSystemAction + ? { + id: '', + actionTypeId: actionTypeModel.id, + params: {}, + uuid: uuidv4(), + type: RuleActionTypes.SYSTEM, + } + : { + id: '', + actionTypeId: actionTypeModel.id, + group: defaultActionGroupId, + params: {}, + frequency: defaultRuleFrequency, + uuid: uuidv4(), + type: RuleActionTypes.DEFAULT, + } + ); setActionIdByIndex(actionTypeConnectors[0].id, actions.length - 1); } else { actionTypeConnectors = connectors.filter((field) => allowGroupConnector.includes(field.actionTypeId) ); if (actionTypeConnectors.length > 0) { - actions.push({ - id: '', - actionTypeId: actionTypeConnectors[0].actionTypeId, - group: defaultActionGroupId, - params: {}, - frequency: DEFAULT_FREQUENCY, - uuid: uuidv4(), - }); + actions.push( + isSystemAction + ? { + id: '', + actionTypeId: actionTypeModel.id, + params: {}, + uuid: uuidv4(), + type: RuleActionTypes.SYSTEM, + } + : { + id: '', + actionTypeId: actionTypeConnectors[0].actionTypeId, + group: defaultActionGroupId, + params: {}, + frequency: DEFAULT_FREQUENCY, + uuid: uuidv4(), + type: RuleActionTypes.DEFAULT, + } + ); setActionIdByIndex(actionTypeConnectors[0].id, actions.length - 1); } } @@ -274,13 +301,23 @@ export const ActionForm = ({ if (actionTypeConnectors.length === 0) { // if no connectors exists or all connectors is already assigned an action under current alert // set actionType as id to be able to create new connector within the alert form - actions.push({ - id: '', - actionTypeId: actionTypeModel.id, - group: defaultActionGroupId, - params: {}, - frequency: defaultRuleFrequency, - }); + actions.push( + isSystemAction + ? { + id: '', + actionTypeId: actionTypeModel.id, + params: {}, + type: RuleActionTypes.SYSTEM, + } + : { + id: '', + actionTypeId: actionTypeModel.id, + group: defaultActionGroupId, + params: {}, + frequency: defaultRuleFrequency, + type: RuleActionTypes.DEFAULT, + } + ); setActionIdByIndex(actions.length.toString(), actions.length - 1); setEmptyActionsIds([...emptyActionsIds, actions.length.toString()]); } @@ -371,8 +408,25 @@ export const ActionForm = ({ )} {actionTypesIndex && actions.map((actionItem: RuleAction, index: number) => { + const isSystemAction = Boolean( + actionTypesIndex[actionItem.actionTypeId]?.isSystemActionType + ); const actionConnector = connectors.find((field) => field.id === actionItem.id); - // connectors doesn't exists + if (isSystemAction && !actionConnector) { + return ( + + + + } + /> + ); + } + // If connector does not exist if (!actionConnector) { return ( { const { application: { capabilities }, @@ -408,7 +410,8 @@ export const ActionTypeForm = ({ /> ); - const actionTypeRegistered = actionTypeRegistry.get(actionConnector.actionTypeId); + const actionTypeRegistered = + actionConnector && actionTypeRegistry.get(actionConnector.actionTypeId); if (!actionTypeRegistered) return null; const allowGroupConnector = (actionTypeRegistered?.subtype ?? []).map((atr) => atr.id); @@ -426,9 +429,10 @@ export const ActionTypeForm = ({ actionGroups && selectedActionGroup && setActionGroupIdByIndex && + !isSystemAction && !actionItem.frequency?.summary; - const showActionAlertsFilter = hasFieldsForAAD; + const showActionAlertsFilter = hasFieldsForAAD && !isSystemAction; const accordionContent = checkEnabledResult.isEnabled ? ( <> @@ -436,48 +440,52 @@ export const ActionTypeForm = ({ color="subdued" style={{ borderBottom: `1px solid ${euiTheme.colors.lightShade}` }} > - - } - labelAppend={ - canSave && - actionTypesIndex && - actionTypesIndex[actionConnector.actionTypeId].enabledInConfig ? ( - + {!isSystemAction && ( + <> + - - ) : null - } - > - - - - {!hideNotifyWhen && actionNotifyWhen} + } + labelAppend={ + canSave && + actionTypesIndex && + actionTypesIndex[actionConnector.actionTypeId].enabledInConfig ? ( + + + + ) : null + } + > + + + + + )} + {!hideNotifyWhen && !isSystemAction && actionNotifyWhen} {showSelectActionGroup && ( <> {!hideNotifyWhen && } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx index 3f1c050fb7e25..7252c61b91488 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx @@ -7,7 +7,7 @@ import React, { useReducer, useState, useEffect, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { RuleNotifyWhen } from '@kbn/alerting-plugin/common'; +import { RuleActionTypes, RuleNotifyWhen, SanitizedRuleAction } from '@kbn/alerting-plugin/common'; import { EuiTitle, EuiFlyoutHeader, @@ -73,10 +73,25 @@ const cloneAndMigrateRule = (initialRule: Rule) => { initialRule.notifyWhen === RuleNotifyWhen.THROTTLE ? initialRule.throttle! : null, } : { summary: false, notifyWhen: RuleNotifyWhen.THROTTLE, throttle: initialRule.throttle! }; - clonedRule.actions = clonedRule.actions.map((action) => ({ - ...action, - frequency, - })); + clonedRule.actions = clonedRule.actions.map((action) => + action.type === RuleActionTypes.SYSTEM + ? action + : { + ...action, + frequency, + } + ); + } else { + // Migrate untyped actions + clonedRule.actions = clonedRule.actions.map( + (action: SanitizedRuleAction | Omit) => + 'type' in action + ? action + : ({ + ...action, + type: action.frequency ? RuleActionTypes.DEFAULT : RuleActionTypes.SYSTEM, + } as SanitizedRuleAction) + ); } return clonedRule; }; From 74ac6ce9b513c533dcdbcdae66677ec3aa74eda3 Mon Sep 17 00:00:00 2001 From: Zacqary Xeper Date: Tue, 23 Jan 2024 12:50:24 -0600 Subject: [PATCH 03/12] Simplify action form code, remove unneeded try/catch, add comment --- .../system_log_example_params.tsx | 2 + .../rule/apis/resolve/resolve_rule_route.ts | 30 +++--- .../action_connector_form/action_form.tsx | 96 ++++++------------- 3 files changed, 46 insertions(+), 82 deletions(-) diff --git a/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example_params.tsx b/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example_params.tsx index b1d7e186dd8b3..199b86b3ac5ce 100644 --- a/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example_params.tsx +++ b/x-pack/examples/triggers_actions_ui_example/public/connector_types/system_log_example/system_log_example_params.tsx @@ -27,6 +27,8 @@ export const ServerLogParamsFields: React.FunctionComponent< const [[isUsingDefault, defaultMessageUsed], setDefaultMessageUsage] = useState< [boolean, string | undefined] >([false, defaultMessage]); + // This params component is derived primarily from server_log_params.tsx, see that file and its + // corresponding unit tests for details on functionality useEffect(() => { if ( useDefaultMessage || diff --git a/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.ts b/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.ts index 680f62eae9ca0..2e6f016b5f6ac 100644 --- a/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.ts +++ b/x-pack/plugins/alerting/server/routes/rule/apis/resolve/resolve_rule_route.ts @@ -33,23 +33,19 @@ export const resolveRuleRoute = ( }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - try { - const rulesClient = (await context.alerting).getRulesClient(); - const params: ResolveRuleRequestParamsV1 = req.params; - const { id } = params; - // TODO (http-versioning): Remove this cast, this enables us to move forward - // without fixing all of other solution types - const rule = (await rulesClient.resolve({ - id, - includeSnoozeData: true, - })) as ResolvedRule; - const response: ResolveRuleResponseV1 = { - body: transformResolveResponseV1(rule), - }; - return res.ok(response); - } catch (e) { - throw e; - } + const rulesClient = (await context.alerting).getRulesClient(); + const params: ResolveRuleRequestParamsV1 = req.params; + const { id } = params; + // TODO (http-versioning): Remove this cast, this enables us to move forward + // without fixing all of other solution types + const rule = (await rulesClient.resolve({ + id, + includeSnoozeData: true, + })) as ResolvedRule; + const response: ResolveRuleResponseV1 = { + body: transformResolveResponseV1(rule), + }; + return res.ok(response); }) ) ); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index 65049b007ce94..b3568a35f1949 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -249,77 +249,43 @@ export const ActionForm = ({ let actionTypeConnectors = connectors.filter( (field) => field.actionTypeId === actionTypeModel.id ); - if (actionTypeConnectors.length > 0) { - actions.push( - isSystemAction - ? { - id: '', - actionTypeId: actionTypeModel.id, - params: {}, - uuid: uuidv4(), - type: RuleActionTypes.SYSTEM, - } - : { - id: '', - actionTypeId: actionTypeModel.id, - group: defaultActionGroupId, - params: {}, - frequency: defaultRuleFrequency, - uuid: uuidv4(), - type: RuleActionTypes.DEFAULT, - } - ); - setActionIdByIndex(actionTypeConnectors[0].id, actions.length - 1); - } else { + + const actionToPush = isSystemAction + ? { + id: '', + actionTypeId: actionTypeModel.id, + params: {}, + uuid: uuidv4(), + type: RuleActionTypes.SYSTEM, + } + : { + id: '', + actionTypeId: actionTypeModel.id, + group: defaultActionGroupId, + params: {}, + frequency: defaultRuleFrequency, + uuid: uuidv4(), + type: RuleActionTypes.DEFAULT, + }; + + if (actionTypeConnectors.length === 0) { actionTypeConnectors = connectors.filter((field) => allowGroupConnector.includes(field.actionTypeId) ); if (actionTypeConnectors.length > 0) { - actions.push( - isSystemAction - ? { - id: '', - actionTypeId: actionTypeModel.id, - params: {}, - uuid: uuidv4(), - type: RuleActionTypes.SYSTEM, - } - : { - id: '', - actionTypeId: actionTypeConnectors[0].actionTypeId, - group: defaultActionGroupId, - params: {}, - frequency: DEFAULT_FREQUENCY, - uuid: uuidv4(), - type: RuleActionTypes.DEFAULT, - } - ); + // If a connector was successfully found, update the actionTypeId + actions.push({ ...actionToPush, actionTypeId: actionTypeConnectors[0].actionTypeId }); setActionIdByIndex(actionTypeConnectors[0].id, actions.length - 1); + } else { + // if no connectors exists or all connectors is already assigned an action under current alert + // set actionType as id to be able to create new connector within the alert form + actions.push(actionToPush); + setActionIdByIndex(actions.length.toString(), actions.length - 1); + setEmptyActionsIds([...emptyActionsIds, actions.length.toString()]); } - } - - if (actionTypeConnectors.length === 0) { - // if no connectors exists or all connectors is already assigned an action under current alert - // set actionType as id to be able to create new connector within the alert form - actions.push( - isSystemAction - ? { - id: '', - actionTypeId: actionTypeModel.id, - params: {}, - type: RuleActionTypes.SYSTEM, - } - : { - id: '', - actionTypeId: actionTypeModel.id, - group: defaultActionGroupId, - params: {}, - frequency: defaultRuleFrequency, - type: RuleActionTypes.DEFAULT, - } - ); - setActionIdByIndex(actions.length.toString(), actions.length - 1); - setEmptyActionsIds([...emptyActionsIds, actions.length.toString()]); + } else { + actions.push(actionToPush); + setActionIdByIndex(actionTypeConnectors[0].id, actions.length - 1); } } From 0028ece6266bcf16e2d80dcb8fc348003dbea5e9 Mon Sep 17 00:00:00 2001 From: Zacqary Xeper Date: Wed, 24 Jan 2024 12:52:39 -0600 Subject: [PATCH 04/12] Fix issues with rewriting create rule body --- .../public/application/lib/rule_api/create.ts | 21 +++++++++++++---- .../sections/rule_form/rule_edit.tsx | 23 ++++++++++++++++--- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts index 2304ee8c48930..61eb400845a50 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts @@ -6,6 +6,7 @@ */ import { HttpSetup } from '@kbn/core/public'; import { AsApiContract, RewriteResponseCase } from '@kbn/actions-plugin/common'; +import { isSystemAction, SanitizedDefaultRuleAction } from '@kbn/alerting-plugin/common'; import { Rule, RuleUpdates } from '../../../types'; import { BASE_ALERTING_API_PATH } from '../../constants'; import { transformRule } from './common_transformations'; @@ -27,8 +28,20 @@ const rewriteBodyRequest: RewriteResponseCase = ({ }): any => ({ ...res, rule_type_id: ruleTypeId, - actions: actions.map( - ({ group, id, params, frequency, alertsFilter, useAlertDataForTemplate }) => ({ + actions: actions.map((action) => { + if (isSystemAction(action)) { + const { id, params, useAlertDataForTemplate } = action; + return { + id, + params, + ...(typeof useAlertDataForTemplate !== 'undefined' + ? { use_alert_data_for_template: useAlertDataForTemplate } + : {}), + }; + } + const { group, id, params, frequency, alertsFilter, useAlertDataForTemplate } = + action as SanitizedDefaultRuleAction; + return { group, id, params, @@ -41,8 +54,8 @@ const rewriteBodyRequest: RewriteResponseCase = ({ ...(typeof useAlertDataForTemplate !== 'undefined' ? { use_alert_data_for_template: useAlertDataForTemplate } : {}), - }) - ), + }; + }), }); export async function createRule({ diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx index 7252c61b91488..64de282e57de0 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx @@ -7,7 +7,12 @@ import React, { useReducer, useState, useEffect, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { RuleActionTypes, RuleNotifyWhen, SanitizedRuleAction } from '@kbn/alerting-plugin/common'; +import { + isSystemAction, + RuleActionTypes, + RuleNotifyWhen, + SanitizedRuleAction, +} from '@kbn/alerting-plugin/common'; import { EuiTitle, EuiFlyoutHeader, @@ -58,6 +63,14 @@ const defaultUpdateRuleErrorMessage = i18n.translate( } ); +// Separate function for determining if an untyped action has a group property or not, which helps determine if +// it is a default action or a system action. Consolidated here to deal with type definition complexity +const actionHasDefinedGroup = (action: SanitizedRuleAction) => { + if (!('group' in action)) return false; + // If the group property is present, ensure that it isn't null or undefined + return Boolean(action.group); +}; + const cloneAndMigrateRule = (initialRule: Rule) => { const clonedRule = cloneDeep(omit(initialRule, 'notifyWhen', 'throttle')); @@ -74,7 +87,7 @@ const cloneAndMigrateRule = (initialRule: Rule) => { } : { summary: false, notifyWhen: RuleNotifyWhen.THROTTLE, throttle: initialRule.throttle! }; clonedRule.actions = clonedRule.actions.map((action) => - action.type === RuleActionTypes.SYSTEM + isSystemAction(action) ? action : { ...action, @@ -83,13 +96,17 @@ const cloneAndMigrateRule = (initialRule: Rule) => { ); } else { // Migrate untyped actions + // Note that this type is supposed to be stripped before sending to the server. This type property is used + // to tell the downstream rewrite functions whether to include properties such as frequency, alertsFilter, etc. clonedRule.actions = clonedRule.actions.map( (action: SanitizedRuleAction | Omit) => 'type' in action ? action : ({ ...action, - type: action.frequency ? RuleActionTypes.DEFAULT : RuleActionTypes.SYSTEM, + type: actionHasDefinedGroup(action as SanitizedRuleAction) + ? RuleActionTypes.DEFAULT + : RuleActionTypes.SYSTEM, } as SanitizedRuleAction) ); } From 4479ce6c0c07fa9ab1aba621af432a26f059ab9c Mon Sep 17 00:00:00 2001 From: Zacqary Xeper Date: Wed, 24 Jan 2024 14:08:17 -0600 Subject: [PATCH 05/12] Fix migrating old rules without action types --- .../sections/rule_form/rule_edit.tsx | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx index 64de282e57de0..60c3d658356db 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx @@ -86,16 +86,32 @@ const cloneAndMigrateRule = (initialRule: Rule) => { initialRule.notifyWhen === RuleNotifyWhen.THROTTLE ? initialRule.throttle! : null, } : { summary: false, notifyWhen: RuleNotifyWhen.THROTTLE, throttle: initialRule.throttle! }; - clonedRule.actions = clonedRule.actions.map((action) => - isSystemAction(action) - ? action - : { + clonedRule.actions = clonedRule.actions.map( + // Clone untyped actions and add a type property to them. This is necessary for the downstream rewrite functions. + (action: SanitizedRuleAction | Omit) => { + if ('type' in action) { + return isSystemAction(action) + ? action + : ({ + ...action, + frequency, + } as SanitizedRuleAction); + } + if (actionHasDefinedGroup(action as SanitizedRuleAction)) { + return { ...action, frequency, - } + type: RuleActionTypes.DEFAULT, + } as SanitizedRuleAction; + } + return { + ...action, + type: RuleActionTypes.SYSTEM, + } as SanitizedRuleAction; + } ); } else { - // Migrate untyped actions + // Clone untyped actions and add a type property to them. This is necessary for the downstream rewrite functions. // Note that this type is supposed to be stripped before sending to the server. This type property is used // to tell the downstream rewrite functions whether to include properties such as frequency, alertsFilter, etc. clonedRule.actions = clonedRule.actions.map( From 51bc88b34953566e317bf804fd39d70fb6ac10fa Mon Sep 17 00:00:00 2001 From: Zacqary Xeper Date: Wed, 24 Jan 2024 15:48:38 -0600 Subject: [PATCH 06/12] Remove type awareness from rule UI, handle undefined frequencies --- .../public/application/lib/rule_api/create.ts | 24 ++++------ .../public/application/lib/rule_api/update.ts | 25 ++++------ .../sections/rule_form/rule_edit.tsx | 47 +++---------------- 3 files changed, 22 insertions(+), 74 deletions(-) diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts index 61eb400845a50..7d46384bbe451 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/create.ts @@ -6,7 +6,7 @@ */ import { HttpSetup } from '@kbn/core/public'; import { AsApiContract, RewriteResponseCase } from '@kbn/actions-plugin/common'; -import { isSystemAction, SanitizedDefaultRuleAction } from '@kbn/alerting-plugin/common'; +import { SanitizedDefaultRuleAction } from '@kbn/alerting-plugin/common'; import { Rule, RuleUpdates } from '../../../types'; import { BASE_ALERTING_API_PATH } from '../../constants'; import { transformRule } from './common_transformations'; @@ -29,27 +29,19 @@ const rewriteBodyRequest: RewriteResponseCase = ({ ...res, rule_type_id: ruleTypeId, actions: actions.map((action) => { - if (isSystemAction(action)) { - const { id, params, useAlertDataForTemplate } = action; - return { - id, - params, - ...(typeof useAlertDataForTemplate !== 'undefined' - ? { use_alert_data_for_template: useAlertDataForTemplate } - : {}), - }; - } const { group, id, params, frequency, alertsFilter, useAlertDataForTemplate } = action as SanitizedDefaultRuleAction; return { group, id, params, - frequency: { - notify_when: frequency!.notifyWhen, - throttle: frequency!.throttle, - summary: frequency!.summary, - }, + frequency: frequency + ? { + notify_when: frequency!.notifyWhen, + throttle: frequency!.throttle, + summary: frequency!.summary, + } + : undefined, alerts_filter: alertsFilter, ...(typeof useAlertDataForTemplate !== 'undefined' ? { use_alert_data_for_template: useAlertDataForTemplate } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts index dbabf74535807..7048e5b6b55f7 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/rule_api/update.ts @@ -7,7 +7,7 @@ import { HttpSetup } from '@kbn/core/public'; import { pick } from 'lodash'; import { RewriteResponseCase, AsApiContract } from '@kbn/actions-plugin/common'; -import { isSystemAction, SanitizedDefaultRuleAction } from '@kbn/alerting-plugin/common'; +import { SanitizedDefaultRuleAction } from '@kbn/alerting-plugin/common'; import { BASE_ALERTING_API_PATH } from '../../constants'; import { Rule, RuleUpdates } from '../../../types'; import { transformRule } from './common_transformations'; @@ -19,28 +19,19 @@ type RuleUpdatesBody = Pick< const rewriteBodyRequest: RewriteResponseCase = ({ actions, ...res }): any => ({ ...res, actions: actions.map((action) => { - if (isSystemAction(action)) { - const { id, params, uuid, useAlertDataForTemplate } = action; - return { - id, - params, - ...(typeof useAlertDataForTemplate !== 'undefined' - ? { use_alert_data_for_template: useAlertDataForTemplate } - : {}), - ...(uuid && { uuid }), - }; - } const { group, id, params, frequency, uuid, alertsFilter, useAlertDataForTemplate } = action as SanitizedDefaultRuleAction; return { group, id, params, - frequency: { - notify_when: frequency!.notifyWhen, - throttle: frequency!.throttle, - summary: frequency!.summary, - }, + frequency: frequency + ? { + notify_when: frequency!.notifyWhen, + throttle: frequency!.throttle, + summary: frequency!.summary, + } + : undefined, alerts_filter: alertsFilter, ...(typeof useAlertDataForTemplate !== 'undefined' ? { use_alert_data_for_template: useAlertDataForTemplate } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx index 60c3d658356db..d1bdb804d0785 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_form/rule_edit.tsx @@ -7,12 +7,7 @@ import React, { useReducer, useState, useEffect, useCallback } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - isSystemAction, - RuleActionTypes, - RuleNotifyWhen, - SanitizedRuleAction, -} from '@kbn/alerting-plugin/common'; +import { RuleNotifyWhen, SanitizedRuleAction } from '@kbn/alerting-plugin/common'; import { EuiTitle, EuiFlyoutHeader, @@ -86,45 +81,15 @@ const cloneAndMigrateRule = (initialRule: Rule) => { initialRule.notifyWhen === RuleNotifyWhen.THROTTLE ? initialRule.throttle! : null, } : { summary: false, notifyWhen: RuleNotifyWhen.THROTTLE, throttle: initialRule.throttle! }; - clonedRule.actions = clonedRule.actions.map( - // Clone untyped actions and add a type property to them. This is necessary for the downstream rewrite functions. - (action: SanitizedRuleAction | Omit) => { - if ('type' in action) { - return isSystemAction(action) - ? action - : ({ - ...action, - frequency, - } as SanitizedRuleAction); - } - if (actionHasDefinedGroup(action as SanitizedRuleAction)) { - return { - ...action, - frequency, - type: RuleActionTypes.DEFAULT, - } as SanitizedRuleAction; - } + clonedRule.actions = clonedRule.actions.map((action: SanitizedRuleAction) => { + if (actionHasDefinedGroup(action as SanitizedRuleAction)) { return { ...action, - type: RuleActionTypes.SYSTEM, + frequency, } as SanitizedRuleAction; } - ); - } else { - // Clone untyped actions and add a type property to them. This is necessary for the downstream rewrite functions. - // Note that this type is supposed to be stripped before sending to the server. This type property is used - // to tell the downstream rewrite functions whether to include properties such as frequency, alertsFilter, etc. - clonedRule.actions = clonedRule.actions.map( - (action: SanitizedRuleAction | Omit) => - 'type' in action - ? action - : ({ - ...action, - type: actionHasDefinedGroup(action as SanitizedRuleAction) - ? RuleActionTypes.DEFAULT - : RuleActionTypes.SYSTEM, - } as SanitizedRuleAction) - ); + return action as SanitizedRuleAction; + }); } return clonedRule; }; From 372e6a3db68b2033c75ca731693830b726fc0491 Mon Sep 17 00:00:00 2001 From: Zacqary Xeper Date: Mon, 29 Jan 2024 14:20:54 -0600 Subject: [PATCH 07/12] Remove include_system_actions query from get_all --- .../routes/connector/apis/connectors/index.ts | 12 --- .../connector/apis/connectors/schemas/v1.ts | 12 --- .../routes/connector/get_all/get_all.ts | 14 +-- .../get_all_system/get_all_system.test.ts | 96 +++++++++++++++++++ .../get_all_system/get_all_system.ts | 37 +++++++ .../routes/connector/get_all_system/index.ts} | 2 +- .../get_all_system/transforms/index.ts} | 5 +- .../transform_connectors_response}/latest.ts | 2 +- .../transform_connectors_response/v1.ts | 37 +++++++ x-pack/plugins/actions/server/routes/index.ts | 2 + x-pack/plugins/alerting/common/rule.ts | 5 - .../rule_details/components/rule_actions.tsx | 24 +++-- 12 files changed, 192 insertions(+), 56 deletions(-) delete mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts delete mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts create mode 100644 x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts create mode 100644 x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts rename x-pack/plugins/actions/{common/routes/connector/apis/connectors/types/latest.ts => server/routes/connector/get_all_system/index.ts} (79%) rename x-pack/plugins/actions/{common/routes/connector/apis/connectors/types/v1.ts => server/routes/connector/get_all_system/transforms/index.ts} (53%) rename x-pack/plugins/actions/{common/routes/connector/apis/connectors/schemas => server/routes/connector/get_all_system/transforms/transform_connectors_response}/latest.ts (81%) create mode 100644 x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/v1.ts diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts deleted file mode 100644 index 2903655aff01a..0000000000000 --- a/x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts +++ /dev/null @@ -1,12 +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. - */ - -export { connectorsQuerySchema } from './schemas/latest'; -export type { ConnectorsRequestQuery } from './types/latest'; - -export { connectorsQuerySchema as connectorsQuerySchemaV1 } from './schemas/v1'; -export type { ConnectorsRequestQuery as ConnectorsRequestQueryV1 } from './types/v1'; diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts deleted file mode 100644 index cc70f68f9777d..0000000000000 --- a/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts +++ /dev/null @@ -1,12 +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 { schema } from '@kbn/config-schema'; - -export const connectorsQuerySchema = schema.object({ - include_system_actions: schema.maybe(schema.boolean()), -}); diff --git a/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts b/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts index f5338e05af108..18885f90512e8 100644 --- a/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts @@ -7,10 +7,6 @@ import { IRouter } from '@kbn/core/server'; import { AllConnectorsResponseV1 } from '../../../../common/routes/connector/response'; -import { - connectorsQuerySchemaV1, - ConnectorsRequestQueryV1, -} from '../../../../common/routes/connector/apis/connectors'; import { transformGetAllConnectorsResponseV1 } from './transforms'; import { ActionsRequestHandlerContext } from '../../../types'; import { BASE_ACTION_API_PATH } from '../../../../common'; @@ -24,18 +20,12 @@ export const getAllConnectorsRoute = ( router.get( { path: `${BASE_ACTION_API_PATH}/connectors`, - validate: { - query: connectorsQuerySchemaV1, - }, + validate: {}, }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const query: ConnectorsRequestQueryV1 = req.query; - const actionsClient = (await context.actions).getActionsClient(); - const result = await actionsClient.getAll({ - includeSystemActions: query?.include_system_actions, - }); + const result = await actionsClient.getAll(); const responseBody: AllConnectorsResponseV1[] = transformGetAllConnectorsResponseV1(result); return res.ok({ body: responseBody }); diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts new file mode 100644 index 0000000000000..223a2d56c0843 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts @@ -0,0 +1,96 @@ +/* + * 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 { getAllConnectorsRoute } from './get_all'; +import { httpServiceMock } from '@kbn/core/server/mocks'; +import { licenseStateMock } from '../../../lib/license_state.mock'; +import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { verifyAccessAndContext } from '../../verify_access_and_context'; +import { actionsClientMock } from '../../../actions_client/actions_client.mock'; + +jest.mock('../../verify_access_and_context', () => ({ + verifyAccessAndContext: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); + (verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler); +}); + +describe('getAllConnectorsRoute', () => { + it('get all connectors with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getAllConnectorsRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/actions/connectors"`); + + const actionsClient = actionsClientMock.create(); + actionsClient.getAll.mockResolvedValueOnce([]); + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Array [], + } + `); + + expect(actionsClient.getAll).toHaveBeenCalledTimes(1); + + expect(res.ok).toHaveBeenCalledWith({ + body: [], + }); + }); + + it('ensures the license allows getting all connectors', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getAllConnectorsRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/actions/connectors"`); + + const actionsClient = actionsClientMock.create(); + actionsClient.getAll.mockResolvedValueOnce([]); + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + await handler(context, req, res); + + expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); + }); + + it('ensures the license check prevents getting all connectors', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => { + throw new Error('OMG'); + }); + + getAllConnectorsRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/actions/connectors"`); + + const actionsClient = actionsClientMock.create(); + actionsClient.getAll.mockResolvedValueOnce([]); + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); + }); +}); diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts new file mode 100644 index 0000000000000..17d5358fea3fd --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts @@ -0,0 +1,37 @@ +/* + * 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 { AllConnectorsResponseV1 } from '../../../../common/routes/connector/response'; +import { transformGetAllConnectorsResponseV1 } from './transforms'; +import { ActionsRequestHandlerContext } from '../../../types'; +import { INTERNAL_BASE_ACTION_API_PATH } from '../../../../common'; +import { ILicenseState } from '../../../lib'; +import { verifyAccessAndContext } from '../../verify_access_and_context'; + +export const getAllSystemConnectorsRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${INTERNAL_BASE_ACTION_API_PATH}/connectors/system`, + validate: {}, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const actionsClient = (await context.actions).getActionsClient(); + const result = await actionsClient.getAll({ + includeSystemActions: true, + }); + + const responseBody: AllConnectorsResponseV1[] = transformGetAllConnectorsResponseV1(result); + return res.ok({ body: responseBody }); + }) + ) + ); +}; diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/latest.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/index.ts similarity index 79% rename from x-pack/plugins/actions/common/routes/connector/apis/connectors/types/latest.ts rename to x-pack/plugins/actions/server/routes/connector/get_all_system/index.ts index 25300c97a6d2e..cee0018a992ee 100644 --- a/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/latest.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './v1'; +export { getAllSystemConnectorsRoute } from './get_all_system'; diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/index.ts similarity index 53% rename from x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts rename to x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/index.ts index 142d8ec50cfb3..e989acda56cce 100644 --- a/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/index.ts @@ -5,7 +5,6 @@ * 2.0. */ -import type { TypeOf } from '@kbn/config-schema'; -import { connectorsQuerySchemaV1 } from '..'; +export { transformGetAllConnectorsResponse } from './transform_connectors_response/latest'; -export type ConnectorsRequestQuery = TypeOf; +export { transformGetAllConnectorsResponse as transformGetAllConnectorsResponseV1 } from './transform_connectors_response/v1'; diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/latest.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/latest.ts similarity index 81% rename from x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/latest.ts rename to x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/latest.ts index 25300c97a6d2e..500cc1435aadb 100644 --- a/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/latest.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/latest.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './v1'; +export { transformGetAllConnectorsResponse } from './v1'; diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/v1.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/v1.ts new file mode 100644 index 0000000000000..a651255536eaf --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/v1.ts @@ -0,0 +1,37 @@ +/* + * 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 { ConnectorWithExtraFindData } from '../../../../../application/connector/types'; +import { AllConnectorsResponseV1 } from '../../../../../../common/routes/connector/response'; + +export const transformGetAllConnectorsResponse = ( + results: ConnectorWithExtraFindData[] +): AllConnectorsResponseV1[] => { + return results.map( + ({ + id, + name, + config, + actionTypeId, + isPreconfigured, + isDeprecated, + referencedByCount, + isMissingSecrets, + isSystemAction, + }) => ({ + id, + name, + config, + connector_type_id: actionTypeId, + is_preconfigured: isPreconfigured, + is_deprecated: isDeprecated, + referenced_by_count: referencedByCount, + is_missing_secrets: isMissingSecrets, + is_system_action: isSystemAction, + }) + ); +}; diff --git a/x-pack/plugins/actions/server/routes/index.ts b/x-pack/plugins/actions/server/routes/index.ts index ad28c55959039..4624588cfb214 100644 --- a/x-pack/plugins/actions/server/routes/index.ts +++ b/x-pack/plugins/actions/server/routes/index.ts @@ -8,6 +8,7 @@ import { IRouter } from '@kbn/core/server'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import { getAllConnectorsRoute } from './connector/get_all'; +import { getAllSystemConnectorsRoute } from './connector/get_all_system'; import { listTypesRoute } from './connector/list_types'; import { ILicenseState } from '../lib'; import { ActionsRequestHandlerContext } from '../types'; @@ -45,4 +46,5 @@ export function defineRoutes(opts: RouteOptions) { getGlobalExecutionKPIRoute(router, licenseState); getOAuthAccessToken(router, licenseState, actionsConfigUtils); + getAllSystemConnectorsRoute(router, licenseState); } diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index eb5e955f4a6de..28093ff7a5a65 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -142,11 +142,6 @@ export interface RuleSystemAction { params: RuleActionParams; type: typeof RuleActionTypes.SYSTEM; useAlertDataForTemplate?: boolean; - // Workaround for typescript issues where boolean types are only defined in one of the union type members - // Allow frequency and alertsFilter to be deemed present in a system action, but require them to be undefined - // This prevents code like action.frequency?.someProperty from throwing an error - frequency?: undefined; - alertsFilter?: undefined; } export type RuleAction = RuleDefaultAction | RuleSystemAction; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx index 6ff952e7f30bb..858edb17d4267 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx @@ -15,7 +15,7 @@ import { EuiLoadingSpinner, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; +import { isSystemAction, RuleDefaultAction, RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; import { ActionTypeRegistryContract, RuleAction, suspendedComponentWithProps } from '../../../..'; import { useFetchRuleActionConnectors } from '../../../hooks/use_fetch_rule_action_connectors'; import { NOTIFY_WHEN_OPTIONS } from '../../rule_form/rule_notify_when'; @@ -51,7 +51,7 @@ export function RuleActions({ ); } - const getNotifyText = (action: RuleAction) => + const getNotifyText = (action: RuleDefaultAction) => (NOTIFY_WHEN_OPTIONS.find((options) => options.value === action.frequency?.notifyWhen) ?.inputDisplay || action.frequency?.notifyWhen) ?? @@ -93,14 +93,18 @@ export function RuleActions({ - - - {String(getNotifyText(action))} - - + {!isSystemAction(action) && ( + + + {String(getNotifyText(action))} + + + )} From eb01f0487203c92c520301bb4428d20997a196a0 Mon Sep 17 00:00:00 2001 From: Zacqary Xeper Date: Mon, 29 Jan 2024 16:02:32 -0600 Subject: [PATCH 08/12] Revert "Remove include_system_actions query from get_all" This reverts commit 372e6a3db68b2033c75ca731693830b726fc0491. --- .../routes/connector/apis/connectors/index.ts | 12 +++ .../apis/connectors/schemas}/latest.ts | 2 +- .../connector/apis/connectors/schemas/v1.ts} | 6 +- .../apis/connectors/types/latest.ts} | 2 +- .../connector/apis/connectors/types/v1.ts | 11 +++ .../routes/connector/get_all/get_all.ts | 14 ++- .../get_all_system/get_all_system.test.ts | 96 ------------------- .../get_all_system/get_all_system.ts | 37 ------- .../transform_connectors_response/v1.ts | 37 ------- x-pack/plugins/actions/server/routes/index.ts | 2 - x-pack/plugins/alerting/common/rule.ts | 5 + .../rule_details/components/rule_actions.tsx | 24 ++--- 12 files changed, 56 insertions(+), 192 deletions(-) create mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts rename x-pack/plugins/actions/{server/routes/connector/get_all_system/transforms/transform_connectors_response => common/routes/connector/apis/connectors/schemas}/latest.ts (81%) rename x-pack/plugins/actions/{server/routes/connector/get_all_system/transforms/index.ts => common/routes/connector/apis/connectors/schemas/v1.ts} (53%) rename x-pack/plugins/actions/{server/routes/connector/get_all_system/index.ts => common/routes/connector/apis/connectors/types/latest.ts} (79%) create mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts delete mode 100644 x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts delete mode 100644 x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts delete mode 100644 x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/v1.ts diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts new file mode 100644 index 0000000000000..2903655aff01a --- /dev/null +++ b/x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts @@ -0,0 +1,12 @@ +/* + * 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 { connectorsQuerySchema } from './schemas/latest'; +export type { ConnectorsRequestQuery } from './types/latest'; + +export { connectorsQuerySchema as connectorsQuerySchemaV1 } from './schemas/v1'; +export type { ConnectorsRequestQuery as ConnectorsRequestQueryV1 } from './types/v1'; diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/latest.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/latest.ts similarity index 81% rename from x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/latest.ts rename to x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/latest.ts index 500cc1435aadb..25300c97a6d2e 100644 --- a/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/latest.ts +++ b/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/latest.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { transformGetAllConnectorsResponse } from './v1'; +export * from './v1'; diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/index.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts similarity index 53% rename from x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/index.ts rename to x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts index e989acda56cce..cc70f68f9777d 100644 --- a/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/index.ts +++ b/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts @@ -5,6 +5,8 @@ * 2.0. */ -export { transformGetAllConnectorsResponse } from './transform_connectors_response/latest'; +import { schema } from '@kbn/config-schema'; -export { transformGetAllConnectorsResponse as transformGetAllConnectorsResponseV1 } from './transform_connectors_response/v1'; +export const connectorsQuerySchema = schema.object({ + include_system_actions: schema.maybe(schema.boolean()), +}); diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/index.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/latest.ts similarity index 79% rename from x-pack/plugins/actions/server/routes/connector/get_all_system/index.ts rename to x-pack/plugins/actions/common/routes/connector/apis/connectors/types/latest.ts index cee0018a992ee..25300c97a6d2e 100644 --- a/x-pack/plugins/actions/server/routes/connector/get_all_system/index.ts +++ b/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/latest.ts @@ -5,4 +5,4 @@ * 2.0. */ -export { getAllSystemConnectorsRoute } from './get_all_system'; +export * from './v1'; diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts new file mode 100644 index 0000000000000..142d8ec50cfb3 --- /dev/null +++ b/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts @@ -0,0 +1,11 @@ +/* + * 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 { connectorsQuerySchemaV1 } from '..'; + +export type ConnectorsRequestQuery = TypeOf; diff --git a/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts b/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts index 18885f90512e8..f5338e05af108 100644 --- a/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts @@ -7,6 +7,10 @@ import { IRouter } from '@kbn/core/server'; import { AllConnectorsResponseV1 } from '../../../../common/routes/connector/response'; +import { + connectorsQuerySchemaV1, + ConnectorsRequestQueryV1, +} from '../../../../common/routes/connector/apis/connectors'; import { transformGetAllConnectorsResponseV1 } from './transforms'; import { ActionsRequestHandlerContext } from '../../../types'; import { BASE_ACTION_API_PATH } from '../../../../common'; @@ -20,12 +24,18 @@ export const getAllConnectorsRoute = ( router.get( { path: `${BASE_ACTION_API_PATH}/connectors`, - validate: {}, + validate: { + query: connectorsQuerySchemaV1, + }, }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { + const query: ConnectorsRequestQueryV1 = req.query; + const actionsClient = (await context.actions).getActionsClient(); - const result = await actionsClient.getAll(); + const result = await actionsClient.getAll({ + includeSystemActions: query?.include_system_actions, + }); const responseBody: AllConnectorsResponseV1[] = transformGetAllConnectorsResponseV1(result); return res.ok({ body: responseBody }); diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts deleted file mode 100644 index 223a2d56c0843..0000000000000 --- a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts +++ /dev/null @@ -1,96 +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 { getAllConnectorsRoute } from './get_all'; -import { httpServiceMock } from '@kbn/core/server/mocks'; -import { licenseStateMock } from '../../../lib/license_state.mock'; -import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; -import { verifyAccessAndContext } from '../../verify_access_and_context'; -import { actionsClientMock } from '../../../actions_client/actions_client.mock'; - -jest.mock('../../verify_access_and_context', () => ({ - verifyAccessAndContext: jest.fn(), -})); - -beforeEach(() => { - jest.resetAllMocks(); - (verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler); -}); - -describe('getAllConnectorsRoute', () => { - it('get all connectors with proper parameters', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - getAllConnectorsRoute(router, licenseState); - - const [config, handler] = router.get.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions/connectors"`); - - const actionsClient = actionsClientMock.create(); - actionsClient.getAll.mockResolvedValueOnce([]); - - const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); - - expect(await handler(context, req, res)).toMatchInlineSnapshot(` - Object { - "body": Array [], - } - `); - - expect(actionsClient.getAll).toHaveBeenCalledTimes(1); - - expect(res.ok).toHaveBeenCalledWith({ - body: [], - }); - }); - - it('ensures the license allows getting all connectors', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - getAllConnectorsRoute(router, licenseState); - - const [config, handler] = router.get.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions/connectors"`); - - const actionsClient = actionsClientMock.create(); - actionsClient.getAll.mockResolvedValueOnce([]); - - const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); - - await handler(context, req, res); - - expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); - }); - - it('ensures the license check prevents getting all connectors', async () => { - const licenseState = licenseStateMock.create(); - const router = httpServiceMock.createRouter(); - - (verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => { - throw new Error('OMG'); - }); - - getAllConnectorsRoute(router, licenseState); - - const [config, handler] = router.get.mock.calls[0]; - - expect(config.path).toMatchInlineSnapshot(`"/api/actions/connectors"`); - - const actionsClient = actionsClientMock.create(); - actionsClient.getAll.mockResolvedValueOnce([]); - - const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); - - expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); - - expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); - }); -}); diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts deleted file mode 100644 index 17d5358fea3fd..0000000000000 --- a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts +++ /dev/null @@ -1,37 +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 { AllConnectorsResponseV1 } from '../../../../common/routes/connector/response'; -import { transformGetAllConnectorsResponseV1 } from './transforms'; -import { ActionsRequestHandlerContext } from '../../../types'; -import { INTERNAL_BASE_ACTION_API_PATH } from '../../../../common'; -import { ILicenseState } from '../../../lib'; -import { verifyAccessAndContext } from '../../verify_access_and_context'; - -export const getAllSystemConnectorsRoute = ( - router: IRouter, - licenseState: ILicenseState -) => { - router.get( - { - path: `${INTERNAL_BASE_ACTION_API_PATH}/connectors/system`, - validate: {}, - }, - router.handleLegacyErrors( - verifyAccessAndContext(licenseState, async function (context, req, res) { - const actionsClient = (await context.actions).getActionsClient(); - const result = await actionsClient.getAll({ - includeSystemActions: true, - }); - - const responseBody: AllConnectorsResponseV1[] = transformGetAllConnectorsResponseV1(result); - return res.ok({ body: responseBody }); - }) - ) - ); -}; diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/v1.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/v1.ts deleted file mode 100644 index a651255536eaf..0000000000000 --- a/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/v1.ts +++ /dev/null @@ -1,37 +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 { ConnectorWithExtraFindData } from '../../../../../application/connector/types'; -import { AllConnectorsResponseV1 } from '../../../../../../common/routes/connector/response'; - -export const transformGetAllConnectorsResponse = ( - results: ConnectorWithExtraFindData[] -): AllConnectorsResponseV1[] => { - return results.map( - ({ - id, - name, - config, - actionTypeId, - isPreconfigured, - isDeprecated, - referencedByCount, - isMissingSecrets, - isSystemAction, - }) => ({ - id, - name, - config, - connector_type_id: actionTypeId, - is_preconfigured: isPreconfigured, - is_deprecated: isDeprecated, - referenced_by_count: referencedByCount, - is_missing_secrets: isMissingSecrets, - is_system_action: isSystemAction, - }) - ); -}; diff --git a/x-pack/plugins/actions/server/routes/index.ts b/x-pack/plugins/actions/server/routes/index.ts index 4624588cfb214..ad28c55959039 100644 --- a/x-pack/plugins/actions/server/routes/index.ts +++ b/x-pack/plugins/actions/server/routes/index.ts @@ -8,7 +8,6 @@ import { IRouter } from '@kbn/core/server'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import { getAllConnectorsRoute } from './connector/get_all'; -import { getAllSystemConnectorsRoute } from './connector/get_all_system'; import { listTypesRoute } from './connector/list_types'; import { ILicenseState } from '../lib'; import { ActionsRequestHandlerContext } from '../types'; @@ -46,5 +45,4 @@ export function defineRoutes(opts: RouteOptions) { getGlobalExecutionKPIRoute(router, licenseState); getOAuthAccessToken(router, licenseState, actionsConfigUtils); - getAllSystemConnectorsRoute(router, licenseState); } diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index 28093ff7a5a65..eb5e955f4a6de 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -142,6 +142,11 @@ export interface RuleSystemAction { params: RuleActionParams; type: typeof RuleActionTypes.SYSTEM; useAlertDataForTemplate?: boolean; + // Workaround for typescript issues where boolean types are only defined in one of the union type members + // Allow frequency and alertsFilter to be deemed present in a system action, but require them to be undefined + // This prevents code like action.frequency?.someProperty from throwing an error + frequency?: undefined; + alertsFilter?: undefined; } export type RuleAction = RuleDefaultAction | RuleSystemAction; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx index 858edb17d4267..6ff952e7f30bb 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx @@ -15,7 +15,7 @@ import { EuiLoadingSpinner, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { isSystemAction, RuleDefaultAction, RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; +import { RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; import { ActionTypeRegistryContract, RuleAction, suspendedComponentWithProps } from '../../../..'; import { useFetchRuleActionConnectors } from '../../../hooks/use_fetch_rule_action_connectors'; import { NOTIFY_WHEN_OPTIONS } from '../../rule_form/rule_notify_when'; @@ -51,7 +51,7 @@ export function RuleActions({ ); } - const getNotifyText = (action: RuleDefaultAction) => + const getNotifyText = (action: RuleAction) => (NOTIFY_WHEN_OPTIONS.find((options) => options.value === action.frequency?.notifyWhen) ?.inputDisplay || action.frequency?.notifyWhen) ?? @@ -93,18 +93,14 @@ export function RuleActions({ - {!isSystemAction(action) && ( - - - {String(getNotifyText(action))} - - - )} + + + {String(getNotifyText(action))} + + From 9d948546e22d0483b79484da9ecc7d6ba0eb7cd5 Mon Sep 17 00:00:00 2001 From: Zacqary Xeper Date: Mon, 29 Jan 2024 16:06:59 -0600 Subject: [PATCH 09/12] Revert "Revert "Remove include_system_actions query from get_all"" This reverts commit eb01f0487203c92c520301bb4428d20997a196a0. --- .../routes/connector/apis/connectors/index.ts | 12 --- .../connector/apis/connectors/schemas/v1.ts | 12 --- .../routes/connector/get_all/get_all.ts | 14 +-- .../get_all_system/get_all_system.test.ts | 96 +++++++++++++++++++ .../get_all_system/get_all_system.ts | 37 +++++++ .../routes/connector/get_all_system/index.ts} | 2 +- .../get_all_system/transforms/index.ts} | 5 +- .../transform_connectors_response}/latest.ts | 2 +- .../transform_connectors_response/v1.ts | 37 +++++++ x-pack/plugins/actions/server/routes/index.ts | 2 + x-pack/plugins/alerting/common/rule.ts | 5 - .../rule_details/components/rule_actions.tsx | 24 +++-- 12 files changed, 192 insertions(+), 56 deletions(-) delete mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts delete mode 100644 x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts create mode 100644 x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts create mode 100644 x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts rename x-pack/plugins/actions/{common/routes/connector/apis/connectors/types/latest.ts => server/routes/connector/get_all_system/index.ts} (79%) rename x-pack/plugins/actions/{common/routes/connector/apis/connectors/types/v1.ts => server/routes/connector/get_all_system/transforms/index.ts} (53%) rename x-pack/plugins/actions/{common/routes/connector/apis/connectors/schemas => server/routes/connector/get_all_system/transforms/transform_connectors_response}/latest.ts (81%) create mode 100644 x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/v1.ts diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts deleted file mode 100644 index 2903655aff01a..0000000000000 --- a/x-pack/plugins/actions/common/routes/connector/apis/connectors/index.ts +++ /dev/null @@ -1,12 +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. - */ - -export { connectorsQuerySchema } from './schemas/latest'; -export type { ConnectorsRequestQuery } from './types/latest'; - -export { connectorsQuerySchema as connectorsQuerySchemaV1 } from './schemas/v1'; -export type { ConnectorsRequestQuery as ConnectorsRequestQueryV1 } from './types/v1'; diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts b/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts deleted file mode 100644 index cc70f68f9777d..0000000000000 --- a/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/v1.ts +++ /dev/null @@ -1,12 +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 { schema } from '@kbn/config-schema'; - -export const connectorsQuerySchema = schema.object({ - include_system_actions: schema.maybe(schema.boolean()), -}); diff --git a/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts b/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts index f5338e05af108..18885f90512e8 100644 --- a/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all/get_all.ts @@ -7,10 +7,6 @@ import { IRouter } from '@kbn/core/server'; import { AllConnectorsResponseV1 } from '../../../../common/routes/connector/response'; -import { - connectorsQuerySchemaV1, - ConnectorsRequestQueryV1, -} from '../../../../common/routes/connector/apis/connectors'; import { transformGetAllConnectorsResponseV1 } from './transforms'; import { ActionsRequestHandlerContext } from '../../../types'; import { BASE_ACTION_API_PATH } from '../../../../common'; @@ -24,18 +20,12 @@ export const getAllConnectorsRoute = ( router.get( { path: `${BASE_ACTION_API_PATH}/connectors`, - validate: { - query: connectorsQuerySchemaV1, - }, + validate: {}, }, router.handleLegacyErrors( verifyAccessAndContext(licenseState, async function (context, req, res) { - const query: ConnectorsRequestQueryV1 = req.query; - const actionsClient = (await context.actions).getActionsClient(); - const result = await actionsClient.getAll({ - includeSystemActions: query?.include_system_actions, - }); + const result = await actionsClient.getAll(); const responseBody: AllConnectorsResponseV1[] = transformGetAllConnectorsResponseV1(result); return res.ok({ body: responseBody }); diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts new file mode 100644 index 0000000000000..223a2d56c0843 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts @@ -0,0 +1,96 @@ +/* + * 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 { getAllConnectorsRoute } from './get_all'; +import { httpServiceMock } from '@kbn/core/server/mocks'; +import { licenseStateMock } from '../../../lib/license_state.mock'; +import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { verifyAccessAndContext } from '../../verify_access_and_context'; +import { actionsClientMock } from '../../../actions_client/actions_client.mock'; + +jest.mock('../../verify_access_and_context', () => ({ + verifyAccessAndContext: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); + (verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler); +}); + +describe('getAllConnectorsRoute', () => { + it('get all connectors with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getAllConnectorsRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/actions/connectors"`); + + const actionsClient = actionsClientMock.create(); + actionsClient.getAll.mockResolvedValueOnce([]); + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Array [], + } + `); + + expect(actionsClient.getAll).toHaveBeenCalledTimes(1); + + expect(res.ok).toHaveBeenCalledWith({ + body: [], + }); + }); + + it('ensures the license allows getting all connectors', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + getAllConnectorsRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/actions/connectors"`); + + const actionsClient = actionsClientMock.create(); + actionsClient.getAll.mockResolvedValueOnce([]); + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + await handler(context, req, res); + + expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); + }); + + it('ensures the license check prevents getting all connectors', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => { + throw new Error('OMG'); + }); + + getAllConnectorsRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/actions/connectors"`); + + const actionsClient = actionsClientMock.create(); + actionsClient.getAll.mockResolvedValueOnce([]); + + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); + }); +}); diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts new file mode 100644 index 0000000000000..17d5358fea3fd --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts @@ -0,0 +1,37 @@ +/* + * 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 { AllConnectorsResponseV1 } from '../../../../common/routes/connector/response'; +import { transformGetAllConnectorsResponseV1 } from './transforms'; +import { ActionsRequestHandlerContext } from '../../../types'; +import { INTERNAL_BASE_ACTION_API_PATH } from '../../../../common'; +import { ILicenseState } from '../../../lib'; +import { verifyAccessAndContext } from '../../verify_access_and_context'; + +export const getAllSystemConnectorsRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${INTERNAL_BASE_ACTION_API_PATH}/connectors/system`, + validate: {}, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const actionsClient = (await context.actions).getActionsClient(); + const result = await actionsClient.getAll({ + includeSystemActions: true, + }); + + const responseBody: AllConnectorsResponseV1[] = transformGetAllConnectorsResponseV1(result); + return res.ok({ body: responseBody }); + }) + ) + ); +}; diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/latest.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/index.ts similarity index 79% rename from x-pack/plugins/actions/common/routes/connector/apis/connectors/types/latest.ts rename to x-pack/plugins/actions/server/routes/connector/get_all_system/index.ts index 25300c97a6d2e..cee0018a992ee 100644 --- a/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/latest.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/index.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './v1'; +export { getAllSystemConnectorsRoute } from './get_all_system'; diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/index.ts similarity index 53% rename from x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts rename to x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/index.ts index 142d8ec50cfb3..e989acda56cce 100644 --- a/x-pack/plugins/actions/common/routes/connector/apis/connectors/types/v1.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/index.ts @@ -5,7 +5,6 @@ * 2.0. */ -import type { TypeOf } from '@kbn/config-schema'; -import { connectorsQuerySchemaV1 } from '..'; +export { transformGetAllConnectorsResponse } from './transform_connectors_response/latest'; -export type ConnectorsRequestQuery = TypeOf; +export { transformGetAllConnectorsResponse as transformGetAllConnectorsResponseV1 } from './transform_connectors_response/v1'; diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/latest.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/latest.ts similarity index 81% rename from x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/latest.ts rename to x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/latest.ts index 25300c97a6d2e..500cc1435aadb 100644 --- a/x-pack/plugins/actions/common/routes/connector/apis/connectors/schemas/latest.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/latest.ts @@ -5,4 +5,4 @@ * 2.0. */ -export * from './v1'; +export { transformGetAllConnectorsResponse } from './v1'; diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/v1.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/v1.ts new file mode 100644 index 0000000000000..a651255536eaf --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/transforms/transform_connectors_response/v1.ts @@ -0,0 +1,37 @@ +/* + * 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 { ConnectorWithExtraFindData } from '../../../../../application/connector/types'; +import { AllConnectorsResponseV1 } from '../../../../../../common/routes/connector/response'; + +export const transformGetAllConnectorsResponse = ( + results: ConnectorWithExtraFindData[] +): AllConnectorsResponseV1[] => { + return results.map( + ({ + id, + name, + config, + actionTypeId, + isPreconfigured, + isDeprecated, + referencedByCount, + isMissingSecrets, + isSystemAction, + }) => ({ + id, + name, + config, + connector_type_id: actionTypeId, + is_preconfigured: isPreconfigured, + is_deprecated: isDeprecated, + referenced_by_count: referencedByCount, + is_missing_secrets: isMissingSecrets, + is_system_action: isSystemAction, + }) + ); +}; diff --git a/x-pack/plugins/actions/server/routes/index.ts b/x-pack/plugins/actions/server/routes/index.ts index ad28c55959039..4624588cfb214 100644 --- a/x-pack/plugins/actions/server/routes/index.ts +++ b/x-pack/plugins/actions/server/routes/index.ts @@ -8,6 +8,7 @@ import { IRouter } from '@kbn/core/server'; import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import { getAllConnectorsRoute } from './connector/get_all'; +import { getAllSystemConnectorsRoute } from './connector/get_all_system'; import { listTypesRoute } from './connector/list_types'; import { ILicenseState } from '../lib'; import { ActionsRequestHandlerContext } from '../types'; @@ -45,4 +46,5 @@ export function defineRoutes(opts: RouteOptions) { getGlobalExecutionKPIRoute(router, licenseState); getOAuthAccessToken(router, licenseState, actionsConfigUtils); + getAllSystemConnectorsRoute(router, licenseState); } diff --git a/x-pack/plugins/alerting/common/rule.ts b/x-pack/plugins/alerting/common/rule.ts index eb5e955f4a6de..28093ff7a5a65 100644 --- a/x-pack/plugins/alerting/common/rule.ts +++ b/x-pack/plugins/alerting/common/rule.ts @@ -142,11 +142,6 @@ export interface RuleSystemAction { params: RuleActionParams; type: typeof RuleActionTypes.SYSTEM; useAlertDataForTemplate?: boolean; - // Workaround for typescript issues where boolean types are only defined in one of the union type members - // Allow frequency and alertsFilter to be deemed present in a system action, but require them to be undefined - // This prevents code like action.frequency?.someProperty from throwing an error - frequency?: undefined; - alertsFilter?: undefined; } export type RuleAction = RuleDefaultAction | RuleSystemAction; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx index 6ff952e7f30bb..858edb17d4267 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/rule_details/components/rule_actions.tsx @@ -15,7 +15,7 @@ import { EuiLoadingSpinner, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; +import { isSystemAction, RuleDefaultAction, RuleNotifyWhenType } from '@kbn/alerting-plugin/common'; import { ActionTypeRegistryContract, RuleAction, suspendedComponentWithProps } from '../../../..'; import { useFetchRuleActionConnectors } from '../../../hooks/use_fetch_rule_action_connectors'; import { NOTIFY_WHEN_OPTIONS } from '../../rule_form/rule_notify_when'; @@ -51,7 +51,7 @@ export function RuleActions({ ); } - const getNotifyText = (action: RuleAction) => + const getNotifyText = (action: RuleDefaultAction) => (NOTIFY_WHEN_OPTIONS.find((options) => options.value === action.frequency?.notifyWhen) ?.inputDisplay || action.frequency?.notifyWhen) ?? @@ -93,14 +93,18 @@ export function RuleActions({ - - - {String(getNotifyText(action))} - - + {!isSystemAction(action) && ( + + + {String(getNotifyText(action))} + + + )} From 165c717b81143c947e9fb32cdba980321ec46233 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 29 Jan 2024 22:09:26 +0000 Subject: [PATCH 10/12] [CI] Auto-commit changed files from 'node scripts/lint_ts_projects --fix' --- x-pack/examples/triggers_actions_ui_example/tsconfig.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x-pack/examples/triggers_actions_ui_example/tsconfig.json b/x-pack/examples/triggers_actions_ui_example/tsconfig.json index f999d2709a662..64e9254eadc95 100644 --- a/x-pack/examples/triggers_actions_ui_example/tsconfig.json +++ b/x-pack/examples/triggers_actions_ui_example/tsconfig.json @@ -23,5 +23,8 @@ "@kbn/data-plugin", "@kbn/i18n-react", "@kbn/shared-ux-router", + "@kbn/i18n", + "@kbn/actions-plugin", + "@kbn/config-schema", ] } From 2f5f87e17bb4575e9c512bb1d0237adc5c024706 Mon Sep 17 00:00:00 2001 From: kibanamachine <42973632+kibanamachine@users.noreply.github.com> Date: Mon, 29 Jan 2024 22:16:46 +0000 Subject: [PATCH 11/12] [CI] Auto-commit changed files from 'node scripts/notice' --- NOTICE.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NOTICE.txt b/NOTICE.txt index 45af6e5231783..d02031c4b5a2b 100644 --- a/NOTICE.txt +++ b/NOTICE.txt @@ -1,5 +1,5 @@ Kibana source code with Kibana X-Pack source code -Copyright 2012-2023 Elasticsearch B.V. +Copyright 2012-2024 Elasticsearch B.V. --- Pretty handling of logarithmic axes. From c3d1ab6b93daeeac6036e9bd386e9b33936ad8cb Mon Sep 17 00:00:00 2001 From: Zacqary Xeper Date: Mon, 29 Jan 2024 16:33:51 -0600 Subject: [PATCH 12/12] Add list_types_system route, only list system actioins on example rules --- .../apis/connector_types/schemas/v1.ts | 1 - .../get_all_system/get_all_system.test.ts | 10 +- .../get_all_system/get_all_system.ts | 2 +- .../routes/connector/list_types/list_types.ts | 1 - .../connector/list_types_system/index.ts | 8 + .../list_types_system.test.ts | 248 ++++++++++++++++++ .../list_types_system/list_types_system.ts | 50 ++++ .../list_types_system/transforms/index.ts | 9 + .../transform_list_types_response/latest.ts | 8 + .../transform_list_types_response/v1.ts | 35 +++ x-pack/plugins/actions/server/routes/index.ts | 2 + .../action_connector_api/connector_types.ts | 35 ++- .../lib/action_connector_api/connectors.ts | 17 +- .../action_connector_form/action_form.tsx | 10 +- 14 files changed, 399 insertions(+), 37 deletions(-) create mode 100644 x-pack/plugins/actions/server/routes/connector/list_types_system/index.ts create mode 100644 x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.test.ts create mode 100644 x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.ts create mode 100644 x-pack/plugins/actions/server/routes/connector/list_types_system/transforms/index.ts create mode 100644 x-pack/plugins/actions/server/routes/connector/list_types_system/transforms/transform_list_types_response/latest.ts create mode 100644 x-pack/plugins/actions/server/routes/connector/list_types_system/transforms/transform_list_types_response/v1.ts diff --git a/x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/v1.ts b/x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/v1.ts index 243c04f2c00f1..bdbc01efc4b7a 100644 --- a/x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/v1.ts +++ b/x-pack/plugins/actions/common/routes/connector/apis/connector_types/schemas/v1.ts @@ -9,5 +9,4 @@ import { schema } from '@kbn/config-schema'; export const connectorTypesQuerySchema = schema.object({ feature_id: schema.maybe(schema.string()), - include_system_actions: schema.maybe(schema.boolean()), }); diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts index 223a2d56c0843..57c2d4150da6a 100644 --- a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.test.ts @@ -5,7 +5,7 @@ * 2.0. */ -import { getAllConnectorsRoute } from './get_all'; +import { getAllSystemConnectorsRoute } from './get_all_system'; import { httpServiceMock } from '@kbn/core/server/mocks'; import { licenseStateMock } from '../../../lib/license_state.mock'; import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; @@ -21,12 +21,12 @@ beforeEach(() => { (verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler); }); -describe('getAllConnectorsRoute', () => { +describe('getAllSystemConnectorsRoute', () => { it('get all connectors with proper parameters', async () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); - getAllConnectorsRoute(router, licenseState); + getAllSystemConnectorsRoute(router, licenseState); const [config, handler] = router.get.mock.calls[0]; @@ -54,7 +54,7 @@ describe('getAllConnectorsRoute', () => { const licenseState = licenseStateMock.create(); const router = httpServiceMock.createRouter(); - getAllConnectorsRoute(router, licenseState); + getAllSystemConnectorsRoute(router, licenseState); const [config, handler] = router.get.mock.calls[0]; @@ -78,7 +78,7 @@ describe('getAllConnectorsRoute', () => { throw new Error('OMG'); }); - getAllConnectorsRoute(router, licenseState); + getAllSystemConnectorsRoute(router, licenseState); const [config, handler] = router.get.mock.calls[0]; diff --git a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts index 17d5358fea3fd..6ab12950a73c3 100644 --- a/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts +++ b/x-pack/plugins/actions/server/routes/connector/get_all_system/get_all_system.ts @@ -19,7 +19,7 @@ export const getAllSystemConnectorsRoute = ( ) => { router.get( { - path: `${INTERNAL_BASE_ACTION_API_PATH}/connectors/system`, + path: `${INTERNAL_BASE_ACTION_API_PATH}/connectors_with_system`, validate: {}, }, router.handleLegacyErrors( diff --git a/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts b/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts index 1b84ac5b25815..b53b213dcc188 100644 --- a/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts +++ b/x-pack/plugins/actions/server/routes/connector/list_types/list_types.ts @@ -37,7 +37,6 @@ export const listTypesRoute = ( const connectorTypes = await actionsClient.listTypes({ featureId: query?.feature_id, - includeSystemActionTypes: query?.include_system_actions, }); const responseBody: ConnectorTypesResponseV1[] = diff --git a/x-pack/plugins/actions/server/routes/connector/list_types_system/index.ts b/x-pack/plugins/actions/server/routes/connector/list_types_system/index.ts new file mode 100644 index 0000000000000..5fd0c22014a71 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/list_types_system/index.ts @@ -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 { listTypesSystemRoute } from './list_types_system'; diff --git a/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.test.ts b/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.test.ts new file mode 100644 index 0000000000000..c1210c7395953 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.test.ts @@ -0,0 +1,248 @@ +/* + * 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 { httpServiceMock } from '@kbn/core/server/mocks'; +import { LicenseType } from '@kbn/licensing-plugin/server'; +import { licenseStateMock } from '../../../lib/license_state.mock'; +import { mockHandlerArguments } from '../../legacy/_mock_handler_arguments'; +import { listTypesSystemRoute } from './list_types_system'; +import { verifyAccessAndContext } from '../../verify_access_and_context'; +import { actionsClientMock } from '../../../mocks'; + +jest.mock('../../verify_access_and_context', () => ({ + verifyAccessAndContext: jest.fn(), +})); + +beforeEach(() => { + jest.resetAllMocks(); + (verifyAccessAndContext as jest.Mock).mockImplementation((license, handler) => handler); +}); + +describe('listTypesSystemRoute', () => { + it('lists action types with proper parameters', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + listTypesSystemRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector_types"`); + + const listTypes = [ + { + id: '1', + name: 'name', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + minimumLicenseRequired: 'gold' as LicenseType, + supportedFeatureIds: ['alerting'], + isSystemActionType: false, + }, + ]; + + const actionsClient = actionsClientMock.create(); + actionsClient.listTypes.mockResolvedValueOnce(listTypes); + const [context, req, res] = mockHandlerArguments({ actionsClient }, {}, ['ok']); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Array [ + Object { + "enabled": true, + "enabled_in_config": true, + "enabled_in_license": true, + "id": "1", + "is_system_action_type": false, + "minimum_license_required": "gold", + "name": "name", + "supported_feature_ids": Array [ + "alerting", + ], + }, + ], + } + `); + + expect(res.ok).toHaveBeenCalledWith({ + body: [ + { + id: '1', + name: 'name', + enabled: true, + enabled_in_config: true, + enabled_in_license: true, + supported_feature_ids: ['alerting'], + minimum_license_required: 'gold', + is_system_action_type: false, + }, + ], + }); + }); + + it('passes feature_id if provided as query parameter', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + listTypesSystemRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector_types"`); + + const listTypes = [ + { + id: '1', + name: 'name', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + supportedFeatureIds: ['alerting'], + minimumLicenseRequired: 'gold' as LicenseType, + isSystemActionType: false, + }, + ]; + + const actionsClient = actionsClientMock.create(); + actionsClient.listTypes.mockResolvedValueOnce(listTypes); + const [context, req, res] = mockHandlerArguments( + { actionsClient }, + { + query: { + feature_id: 'alerting', + }, + }, + ['ok'] + ); + + expect(await handler(context, req, res)).toMatchInlineSnapshot(` + Object { + "body": Array [ + Object { + "enabled": true, + "enabled_in_config": true, + "enabled_in_license": true, + "id": "1", + "is_system_action_type": false, + "minimum_license_required": "gold", + "name": "name", + "supported_feature_ids": Array [ + "alerting", + ], + }, + ], + } + `); + + expect(actionsClient.listTypes).toHaveBeenCalledTimes(1); + expect(actionsClient.listTypes.mock.calls[0]).toMatchInlineSnapshot(` + Array [ + Object { + "featureId": "alerting", + }, + ] + `); + + expect(res.ok).toHaveBeenCalledWith({ + body: [ + { + id: '1', + name: 'name', + enabled: true, + enabled_in_config: true, + enabled_in_license: true, + supported_feature_ids: ['alerting'], + minimum_license_required: 'gold', + is_system_action_type: false, + }, + ], + }); + }); + + it('ensures the license allows listing action types', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + listTypesSystemRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector_types"`); + + const listTypes = [ + { + id: '1', + name: 'name', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + supportedFeatureIds: ['alerting'], + minimumLicenseRequired: 'gold' as LicenseType, + isSystemActionType: false, + }, + ]; + + const actionsClient = actionsClientMock.create(); + actionsClient.listTypes.mockResolvedValueOnce(listTypes); + + const [context, req, res] = mockHandlerArguments( + { actionsClient }, + { + params: { id: '1' }, + }, + ['ok'] + ); + + await handler(context, req, res); + + expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); + }); + + it('ensures the license check prevents listing action types', async () => { + const licenseState = licenseStateMock.create(); + const router = httpServiceMock.createRouter(); + + (verifyAccessAndContext as jest.Mock).mockImplementation(() => async () => { + throw new Error('OMG'); + }); + + listTypesSystemRoute(router, licenseState); + + const [config, handler] = router.get.mock.calls[0]; + + expect(config.path).toMatchInlineSnapshot(`"/api/actions/connector_types"`); + + const listTypes = [ + { + id: '1', + name: 'name', + enabled: true, + enabledInConfig: true, + enabledInLicense: true, + supportedFeatureIds: ['alerting'], + minimumLicenseRequired: 'gold' as LicenseType, + isSystemActionType: false, + }, + ]; + + const actionsClient = actionsClientMock.create(); + actionsClient.listTypes.mockResolvedValueOnce(listTypes); + + const [context, req, res] = mockHandlerArguments( + { actionsClient }, + { + params: { id: '1' }, + }, + ['ok'] + ); + + expect(handler(context, req, res)).rejects.toMatchInlineSnapshot(`[Error: OMG]`); + + expect(verifyAccessAndContext).toHaveBeenCalledWith(licenseState, expect.any(Function)); + }); +}); diff --git a/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.ts b/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.ts new file mode 100644 index 0000000000000..4d81cb31be87e --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/list_types_system/list_types_system.ts @@ -0,0 +1,50 @@ +/* + * 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 { ConnectorTypesResponseV1 } from '../../../../common/routes/connector/response'; +import { + connectorTypesQuerySchemaV1, + ConnectorTypesRequestQueryV1, +} from '../../../../common/routes/connector/apis/connector_types'; +import { transformListTypesResponseV1 } from './transforms'; +import { ActionsRequestHandlerContext } from '../../../types'; +import { INTERNAL_BASE_ACTION_API_PATH } from '../../../../common'; +import { ILicenseState } from '../../../lib'; +import { verifyAccessAndContext } from '../../verify_access_and_context'; + +export const listTypesSystemRoute = ( + router: IRouter, + licenseState: ILicenseState +) => { + router.get( + { + path: `${INTERNAL_BASE_ACTION_API_PATH}/connector_types_with_system`, + validate: { + query: connectorTypesQuerySchemaV1, + }, + }, + router.handleLegacyErrors( + verifyAccessAndContext(licenseState, async function (context, req, res) { + const actionsClient = (await context.actions).getActionsClient(); + + // Assert versioned inputs + const query: ConnectorTypesRequestQueryV1 = req.query; + + const connectorTypes = await actionsClient.listTypes({ + featureId: query?.feature_id, + includeSystemActionTypes: true, + }); + + const responseBody: ConnectorTypesResponseV1[] = + transformListTypesResponseV1(connectorTypes); + + return res.ok({ body: responseBody }); + }) + ) + ); +}; diff --git a/x-pack/plugins/actions/server/routes/connector/list_types_system/transforms/index.ts b/x-pack/plugins/actions/server/routes/connector/list_types_system/transforms/index.ts new file mode 100644 index 0000000000000..35e5f1db443c2 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/list_types_system/transforms/index.ts @@ -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 { transformListTypesResponse } from './transform_list_types_response/latest'; +export { transformListTypesResponse as transformListTypesResponseV1 } from './transform_list_types_response/v1'; diff --git a/x-pack/plugins/actions/server/routes/connector/list_types_system/transforms/transform_list_types_response/latest.ts b/x-pack/plugins/actions/server/routes/connector/list_types_system/transforms/transform_list_types_response/latest.ts new file mode 100644 index 0000000000000..5fd887263233c --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/list_types_system/transforms/transform_list_types_response/latest.ts @@ -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 { transformListTypesResponse } from './v1'; diff --git a/x-pack/plugins/actions/server/routes/connector/list_types_system/transforms/transform_list_types_response/v1.ts b/x-pack/plugins/actions/server/routes/connector/list_types_system/transforms/transform_list_types_response/v1.ts new file mode 100644 index 0000000000000..e32bec2f9e1a1 --- /dev/null +++ b/x-pack/plugins/actions/server/routes/connector/list_types_system/transforms/transform_list_types_response/v1.ts @@ -0,0 +1,35 @@ +/* + * 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 { ConnectorType } from '../../../../../application/connector/types'; +import { ConnectorTypesResponseV1 } from '../../../../../../common/routes/connector/response'; + +export const transformListTypesResponse = ( + results: ConnectorType[] +): ConnectorTypesResponseV1[] => { + return results.map( + ({ + id, + name, + enabled, + enabledInConfig, + enabledInLicense, + minimumLicenseRequired, + supportedFeatureIds, + isSystemActionType, + }) => ({ + id, + name, + enabled, + enabled_in_config: enabledInConfig, + enabled_in_license: enabledInLicense, + minimum_license_required: minimumLicenseRequired, + supported_feature_ids: supportedFeatureIds, + is_system_action_type: isSystemActionType, + }) + ); +}; diff --git a/x-pack/plugins/actions/server/routes/index.ts b/x-pack/plugins/actions/server/routes/index.ts index 4624588cfb214..7b3aa6d35150d 100644 --- a/x-pack/plugins/actions/server/routes/index.ts +++ b/x-pack/plugins/actions/server/routes/index.ts @@ -10,6 +10,7 @@ import { UsageCounter } from '@kbn/usage-collection-plugin/server'; import { getAllConnectorsRoute } from './connector/get_all'; import { getAllSystemConnectorsRoute } from './connector/get_all_system'; import { listTypesRoute } from './connector/list_types'; +import { listTypesSystemRoute } from './connector/list_types_system'; import { ILicenseState } from '../lib'; import { ActionsRequestHandlerContext } from '../types'; import { createActionRoute } from './create'; @@ -47,4 +48,5 @@ export function defineRoutes(opts: RouteOptions) { getOAuthAccessToken(router, licenseState, actionsConfigUtils); getAllSystemConnectorsRoute(router, licenseState); + listTypesSystemRoute(router, licenseState); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts index 6da3e16cd1909..97fc1bd376b6f 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connector_types.ts @@ -6,7 +6,11 @@ */ import { HttpSetup } from '@kbn/core/public'; -import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; +import { + AsApiContract, + INTERNAL_BASE_ACTION_API_PATH, + RewriteRequestCase, +} from '@kbn/actions-plugin/common'; import { BASE_ACTION_API_PATH } from '../../constants'; import type { ActionType } from '../../../types'; @@ -33,27 +37,22 @@ const rewriteBodyReq: RewriteRequestCase = ({ export async function loadActionTypes({ http, featureId, + includeSystemActions = false, }: { http: HttpSetup; featureId?: string; + includeSystemActions?: boolean; }): Promise { + const path = includeSystemActions + ? `${INTERNAL_BASE_ACTION_API_PATH}/connector_types_with_system` + : `${BASE_ACTION_API_PATH}/connector_types`; + const res = featureId - ? await http.get[0]>( - `${BASE_ACTION_API_PATH}/connector_types`, - { - query: { - feature_id: featureId, - include_system_actions: true, - }, - } - ) - : await http.get[0]>( - `${BASE_ACTION_API_PATH}/connector_types`, - { - query: { - include_system_actions: true, - }, - } - ); + ? await http.get[0]>(path, { + query: { + feature_id: featureId, + }, + }) + : await http.get[0]>(path, {}); return rewriteResponseRes(res); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts index e76d110ef54f0..7aa3cbbf1cf27 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api/connectors.ts @@ -5,8 +5,11 @@ * 2.0. */ import { HttpSetup } from '@kbn/core/public'; -import { AsApiContract, RewriteRequestCase } from '@kbn/actions-plugin/common'; -import { BASE_ACTION_API_PATH } from '../../constants'; +import { + AsApiContract, + INTERNAL_BASE_ACTION_API_PATH, + RewriteRequestCase, +} from '@kbn/actions-plugin/common'; import type { ActionConnector, ActionConnectorProps } from '../../../types'; const rewriteResponseRes = ( @@ -38,13 +41,11 @@ const transformConnector: RewriteRequestCase< }); export async function loadAllActions({ http }: { http: HttpSetup }): Promise { + // Use the internal get_all_system route to load all action connectors and preconfigured system action connectors + // This is necessary to load UI elements that require system action connectors, even if they're not selectable and + // editable from the connector selection UI like a normal action connector. const res = await http.get[0]>( - `${BASE_ACTION_API_PATH}/connectors`, - { - query: { - include_system_actions: true, - }, - } + `${INTERNAL_BASE_ACTION_API_PATH}/connectors_with_system` ); return rewriteResponseRes(res) as ActionConnector[]; diff --git a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx index b3568a35f1949..b6cbff0bb3a79 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx +++ b/x-pack/plugins/triggers_actions_ui/public/application/sections/action_connector_form/action_form.tsx @@ -155,9 +155,13 @@ export const ActionForm = ({ (async () => { try { setIsLoadingActionTypes(true); - const registeredActionTypes = (await loadActionTypes({ http, featureId })).sort((a, b) => - a.name.localeCompare(b.name) - ); + const registeredActionTypes = ( + await loadActionTypes({ + http, + featureId, + includeSystemActions: ruleTypeId?.startsWith('example.'), + }) + ).sort((a, b) => a.name.localeCompare(b.name)); const index: ActionTypeIndex = {}; for (const actionTypeItem of registeredActionTypes) { index[actionTypeItem.id] = actionTypeItem;