mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Make rule type param validation required (#154257)
Resolves: #153755 This PR intends to make rule type param validation function required. --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
08a68dbe73
commit
00dfae4312
37 changed files with 325 additions and 18 deletions
|
@ -8,6 +8,7 @@
|
|||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { range } from 'lodash';
|
||||
import { RuleType } from '@kbn/alerting-plugin/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import {
|
||||
DEFAULT_INSTANCES_TO_GENERATE,
|
||||
ALERTING_EXAMPLE_APP_ID,
|
||||
|
@ -78,4 +79,16 @@ export const alertType: RuleType<
|
|||
};
|
||||
},
|
||||
producer: ALERTING_EXAMPLE_APP_ID,
|
||||
validate: {
|
||||
params: schema.object({
|
||||
instances: schema.maybe(schema.number()),
|
||||
thresholds: schema.maybe(
|
||||
schema.object({
|
||||
small: schema.maybe(schema.number()),
|
||||
medium: schema.maybe(schema.number()),
|
||||
large: schema.maybe(schema.number()),
|
||||
})
|
||||
),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import axios from 'axios';
|
||||
import { RuleType } from '@kbn/alerting-plugin/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { Operator, Craft, ALERTING_EXAMPLE_APP_ID } from '../../common/constants';
|
||||
|
||||
interface PeopleInSpace {
|
||||
|
@ -84,4 +85,11 @@ export const alertType: RuleType<
|
|||
getViewInAppRelativeUrl({ rule }) {
|
||||
return `/app/${ALERTING_EXAMPLE_APP_ID}/astros/${rule.id}`;
|
||||
},
|
||||
validate: {
|
||||
params: schema.object({
|
||||
outerSpaceCapacity: schema.number(),
|
||||
craft: schema.string(),
|
||||
op: schema.string(),
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
|
|
@ -15,6 +15,7 @@ import { ruleRunMetricsStoreMock } from '../lib/rule_run_metrics_store.mock';
|
|||
import { getAlertsForNotification, processAlerts } from '../lib';
|
||||
import { logAlerts } from '../task_runner/log_alerts';
|
||||
import { DEFAULT_FLAPPING_SETTINGS } from '../../common/rules_settings';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
const scheduleActions = jest.fn();
|
||||
const replaceState = jest.fn(() => ({ scheduleActions }));
|
||||
|
@ -86,6 +87,9 @@ const ruleType: jest.Mocked<UntypedNormalizedRuleType> = {
|
|||
producer: 'alerts',
|
||||
cancelAlertsOnRuleTimeout: true,
|
||||
ruleTaskTimeout: '5m',
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
|
||||
const testAlert1 = {
|
||||
|
|
|
@ -24,6 +24,7 @@ import { v4 as uuidv4 } from 'uuid';
|
|||
import { RecoveredActionGroup } from '../../common';
|
||||
import { RegistryRuleType } from '../rule_type_registry';
|
||||
import { AlertingAuthorizationFilterType } from './alerting_authorization_kuery';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
const ruleTypeRegistry = ruleTypeRegistryMock.create();
|
||||
const features: jest.Mocked<FeaturesStartContract> = featuresPluginMock.createStart();
|
||||
|
@ -196,6 +197,9 @@ beforeEach(() => {
|
|||
return { state: {} };
|
||||
},
|
||||
producer: 'myApp',
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
}));
|
||||
features.getKibanaFeatures.mockReturnValue([
|
||||
myAppFeature,
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
import { RuleRunMetrics } from '../rule_run_metrics_store';
|
||||
import { EVENT_LOG_ACTIONS } from '../../plugin';
|
||||
import { TaskRunnerTimerSpan } from '../../task_runner/task_runner_timer';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
const mockNow = '2020-01-01T02:00:00.000Z';
|
||||
const eventLogger = eventLoggerMock.create();
|
||||
|
@ -42,6 +43,9 @@ const ruleType: jest.Mocked<UntypedNormalizedRuleType> = {
|
|||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
ruleTaskTimeout: '1m',
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
|
||||
const context: RuleContextOpts = {
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
import { createAlertEventLogRecordObject } from './create_alert_event_log_record_object';
|
||||
import { UntypedNormalizedRuleType } from '../rule_type_registry';
|
||||
import { RecoveredActionGroup } from '../types';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
const MAINTENANCE_WINDOW_IDS = ['test-1', 'test-2'];
|
||||
|
||||
|
@ -22,6 +23,9 @@ describe('createAlertEventLogRecordObject', () => {
|
|||
recoveryActionGroup: RecoveredActionGroup,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
|
||||
test('created alert event "execute-start"', async () => {
|
||||
|
|
|
@ -72,6 +72,9 @@ describe('getLicenseCheckForRuleType', () => {
|
|||
minimumLicenseRequired: 'gold',
|
||||
isExportable: true,
|
||||
recoveryActionGroup: { id: 'recovered', name: 'Recovered' },
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -207,6 +210,9 @@ describe('ensureLicenseForRuleType()', () => {
|
|||
minimumLicenseRequired: 'gold',
|
||||
isExportable: true,
|
||||
recoveryActionGroup: { id: 'recovered', name: 'Recovered' },
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
|
|
|
@ -17,7 +17,7 @@ export function validateRuleTypeParams<Params extends RuleTypeParams>(
|
|||
}
|
||||
|
||||
try {
|
||||
return validator.validate(params);
|
||||
return validator.validate(params as Params);
|
||||
} catch (err) {
|
||||
throw Boom.badRequest(`params invalid: ${err.message}`);
|
||||
}
|
||||
|
|
|
@ -71,6 +71,9 @@ const sampleRuleType: RuleType<never, never, {}, never, never, 'default'> = {
|
|||
async executor() {
|
||||
return { state: {} };
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
|
||||
describe('Alerting Plugin', () => {
|
||||
|
|
|
@ -15,6 +15,7 @@ import { licensingMock } from '@kbn/licensing-plugin/server/mocks';
|
|||
import { loggingSystemMock } from '@kbn/core/server/mocks';
|
||||
import { inMemoryMetricsMock } from './monitoring/in_memory_metrics.mock';
|
||||
import { alertsServiceMock } from './alerts_service/alerts_service.mock';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
const logger = loggingSystemMock.create().get();
|
||||
let mockedLicenseState: jest.Mocked<ILicenseState>;
|
||||
|
@ -62,6 +63,9 @@ describe('Create Lifecycle', () => {
|
|||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
});
|
||||
expect(registry.has('foo')).toEqual(true);
|
||||
});
|
||||
|
@ -83,6 +87,9 @@ describe('Create Lifecycle', () => {
|
|||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);
|
||||
|
||||
|
@ -116,6 +123,9 @@ describe('Create Lifecycle', () => {
|
|||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);
|
||||
|
||||
|
@ -140,6 +150,9 @@ describe('Create Lifecycle', () => {
|
|||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);
|
||||
|
||||
|
@ -167,6 +180,9 @@ describe('Create Lifecycle', () => {
|
|||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
defaultScheduleInterval: 'foobar',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);
|
||||
|
||||
|
@ -193,6 +209,9 @@ describe('Create Lifecycle', () => {
|
|||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
defaultScheduleInterval: '10s',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);
|
||||
registry.register(ruleType);
|
||||
|
@ -219,6 +238,9 @@ describe('Create Lifecycle', () => {
|
|||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
defaultScheduleInterval: '10s',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry({
|
||||
...ruleTypeRegistryParams,
|
||||
|
@ -255,6 +277,9 @@ describe('Create Lifecycle', () => {
|
|||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);
|
||||
|
||||
|
@ -284,6 +309,9 @@ describe('Create Lifecycle', () => {
|
|||
producer: 'alerts',
|
||||
minimumLicenseRequired: 'basic',
|
||||
isExportable: true,
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);
|
||||
registry.register(ruleType);
|
||||
|
@ -317,6 +345,9 @@ describe('Create Lifecycle', () => {
|
|||
producer: 'alerts',
|
||||
minimumLicenseRequired: 'basic',
|
||||
isExportable: true,
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);
|
||||
registry.register(ruleType);
|
||||
|
@ -354,6 +385,9 @@ describe('Create Lifecycle', () => {
|
|||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);
|
||||
|
||||
|
@ -380,6 +414,9 @@ describe('Create Lifecycle', () => {
|
|||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
ruleTaskTimeout: '20m',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);
|
||||
registry.register(ruleType);
|
||||
|
@ -412,6 +449,9 @@ describe('Create Lifecycle', () => {
|
|||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
const registry = new RuleTypeRegistry(ruleTypeRegistryParams);
|
||||
registry.register(ruleType);
|
||||
|
@ -435,6 +475,9 @@ describe('Create Lifecycle', () => {
|
|||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
});
|
||||
expect(() =>
|
||||
registry.register({
|
||||
|
@ -451,6 +494,9 @@ describe('Create Lifecycle', () => {
|
|||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
})
|
||||
).toThrowErrorMatchingInlineSnapshot(`"Rule type \\"test\\" is already registered."`);
|
||||
});
|
||||
|
@ -475,6 +521,9 @@ describe('Create Lifecycle', () => {
|
|||
context: 'test',
|
||||
mappings: { fieldMap: { field: { type: 'keyword', required: false } } },
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
});
|
||||
|
||||
expect(alertsService.register).toHaveBeenCalledWith({
|
||||
|
@ -499,6 +548,9 @@ describe('Create Lifecycle', () => {
|
|||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
});
|
||||
|
||||
expect(alertsService.register).not.toHaveBeenCalled();
|
||||
|
@ -522,6 +574,9 @@ describe('Create Lifecycle', () => {
|
|||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
});
|
||||
const ruleType = registry.get('test');
|
||||
expect(ruleType).toMatchInlineSnapshot(`
|
||||
|
@ -552,6 +607,11 @@ describe('Create Lifecycle', () => {
|
|||
"id": "recovered",
|
||||
"name": "Recovered",
|
||||
},
|
||||
"validate": Object {
|
||||
"params": Object {
|
||||
"validate": [Function],
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
@ -589,6 +649,9 @@ describe('Create Lifecycle', () => {
|
|||
minimumLicenseRequired: 'basic',
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
});
|
||||
const result = registry.list();
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
|
@ -689,6 +752,9 @@ describe('Create Lifecycle', () => {
|
|||
minimumLicenseRequired: 'basic',
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
});
|
||||
const result = registry.getAllTypes();
|
||||
expect(result).toEqual(['test']);
|
||||
|
@ -715,6 +781,9 @@ describe('Create Lifecycle', () => {
|
|||
isExportable: true,
|
||||
minimumLicenseRequired: 'basic',
|
||||
recoveryActionGroup: { id: 'recovered', name: 'Recovered' },
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -750,6 +819,9 @@ function ruleTypeWithVariables<ActionGroupIds extends string>(
|
|||
return { state: {} };
|
||||
},
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
|
||||
if (!context && !state) return baseAlert;
|
||||
|
|
|
@ -44,6 +44,9 @@ const ruleType: jest.Mocked<UntypedNormalizedRuleType> = {
|
|||
cancelAlertsOnRuleTimeout: true,
|
||||
ruleTaskTimeout: '5m',
|
||||
getSummarizedAlerts: jest.fn(),
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
|
||||
const context = {
|
||||
|
|
|
@ -26,6 +26,9 @@ describe('validateActions', () => {
|
|||
cancelAlertsOnRuleTimeout: true,
|
||||
ruleTaskTimeout: '5m',
|
||||
getSummarizedAlerts: jest.fn(),
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
|
||||
const data = {
|
||||
|
|
|
@ -503,11 +503,11 @@ async function updateRuleAttributesAndParamsInMemory<Params extends RuleTypePara
|
|||
}
|
||||
|
||||
// validate rule params
|
||||
const validatedAlertTypeParams = validateRuleTypeParams(ruleParams, ruleType.validate?.params);
|
||||
const validatedAlertTypeParams = validateRuleTypeParams(ruleParams, ruleType.validate.params);
|
||||
const validatedMutatedAlertTypeParams = validateMutatedRuleTypeParams(
|
||||
validatedAlertTypeParams,
|
||||
rule.attributes.params,
|
||||
ruleType.validate?.params
|
||||
ruleType.validate.params
|
||||
);
|
||||
|
||||
const {
|
||||
|
|
|
@ -88,7 +88,7 @@ export async function create<Params extends RuleTypeParams = never>(
|
|||
// Throws an error if alert type isn't registered
|
||||
const ruleType = context.ruleTypeRegistry.get(data.alertTypeId);
|
||||
|
||||
const validatedAlertTypeParams = validateRuleTypeParams(data.params, ruleType.validate?.params);
|
||||
const validatedAlertTypeParams = validateRuleTypeParams(data.params, ruleType.validate.params);
|
||||
const username = await context.getUserName();
|
||||
|
||||
let createdAPIKey = null;
|
||||
|
|
|
@ -198,7 +198,7 @@ async function updateAlert<Params extends RuleTypeParams>(
|
|||
}
|
||||
|
||||
// Validate
|
||||
const validatedAlertTypeParams = validateRuleTypeParams(data.params, ruleType.validate?.params);
|
||||
const validatedAlertTypeParams = validateRuleTypeParams(data.params, ruleType.validate.params);
|
||||
await validateActions(context, ruleType, data, allowMissingConnectorSecrets);
|
||||
|
||||
// Throw error if schedule interval is less than the minimum and we are enforcing it
|
||||
|
|
|
@ -27,6 +27,7 @@ import {
|
|||
returnedRule2,
|
||||
siemRule1,
|
||||
} from './test_helpers';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { migrateLegacyActions } from '../lib';
|
||||
|
||||
jest.mock('../lib/siem_legacy_actions/migrate_legacy_actions', () => {
|
||||
|
@ -174,6 +175,9 @@ describe('bulkDelete', () => {
|
|||
return { state: {} };
|
||||
},
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -210,6 +210,9 @@ describe('bulkEdit()', () => {
|
|||
return { state: {} };
|
||||
},
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
});
|
||||
|
||||
(migrateLegacyActions as jest.Mock).mockResolvedValue(migrateLegacyActionsMock);
|
||||
|
@ -686,6 +689,9 @@ describe('bulkEdit()', () => {
|
|||
},
|
||||
producer: 'alerts',
|
||||
getSummarizedAlerts: jest.fn().mockResolvedValue({}),
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
});
|
||||
const existingAction = {
|
||||
frequency: {
|
||||
|
|
|
@ -1233,6 +1233,9 @@ describe('create()', () => {
|
|||
extractReferences: extractReferencesFn,
|
||||
injectReferences: injectReferencesFn,
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
const data = getMockData({
|
||||
params: ruleParams,
|
||||
|
@ -1414,6 +1417,9 @@ describe('create()', () => {
|
|||
extractReferences: extractReferencesFn,
|
||||
injectReferences: injectReferencesFn,
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
const data = getMockData({
|
||||
params: ruleParams,
|
||||
|
@ -2679,6 +2685,9 @@ describe('create()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: jest.fn(),
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
const createdAttributes = {
|
||||
...data,
|
||||
|
@ -2747,6 +2756,9 @@ describe('create()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: jest.fn(),
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
|
||||
const data = getMockData({ schedule: { interval: '1s' } });
|
||||
|
@ -2781,6 +2793,9 @@ describe('create()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: jest.fn(),
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
|
||||
const data = getMockData({
|
||||
|
@ -2870,6 +2885,9 @@ describe('create()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: jest.fn(),
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
|
||||
const data = getMockData({
|
||||
|
@ -2916,6 +2934,9 @@ describe('create()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: jest.fn(),
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
|
||||
const data = getMockData({
|
||||
|
@ -2975,6 +2996,9 @@ describe('create()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: jest.fn(),
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
|
||||
const data = getMockData({
|
||||
|
@ -3052,6 +3076,9 @@ describe('create()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: jest.fn(),
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
|
||||
const data = getMockData({
|
||||
|
@ -3243,6 +3270,9 @@ describe('create()', () => {
|
|||
injectReferences: jest.fn(),
|
||||
},
|
||||
getSummarizedAlerts: jest.fn().mockResolvedValue({}),
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
|
||||
const data = getMockData({
|
||||
|
@ -3292,6 +3322,9 @@ describe('create()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: jest.fn(),
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
|
||||
const data = getMockData({
|
||||
|
|
|
@ -19,6 +19,7 @@ import { auditLoggerMock } from '@kbn/security-plugin/server/audit/mocks';
|
|||
import { getBeforeSetup, setGlobalDate } from './lib';
|
||||
import { RecoveredActionGroup } from '../../../common';
|
||||
import { RegistryRuleType } from '../../rule_type_registry';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { enabledRule1, enabledRule2, siemRule1, siemRule2 } from './test_helpers';
|
||||
import { formatLegacyActions } from '../lib';
|
||||
|
||||
|
@ -370,6 +371,9 @@ describe('find()', () => {
|
|||
return { state: {} };
|
||||
},
|
||||
producer: 'myApp',
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
}));
|
||||
ruleTypeRegistry.get.mockImplementationOnce(() => ({
|
||||
id: '123',
|
||||
|
@ -387,6 +391,9 @@ describe('find()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: injectReferencesFn,
|
||||
},
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
}));
|
||||
unsecuredSavedObjectsClient.find.mockResolvedValue({
|
||||
total: 2,
|
||||
|
@ -570,6 +577,9 @@ describe('find()', () => {
|
|||
return { state: {} };
|
||||
},
|
||||
producer: 'myApp',
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
}));
|
||||
ruleTypeRegistry.get.mockImplementationOnce(() => ({
|
||||
id: '123',
|
||||
|
@ -587,6 +597,9 @@ describe('find()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: injectReferencesFn,
|
||||
},
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
}));
|
||||
unsecuredSavedObjectsClient.find.mockResolvedValue({
|
||||
total: 2,
|
||||
|
|
|
@ -232,6 +232,9 @@ describe('get()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: injectReferencesFn,
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
const rulesClient = new RulesClient(rulesClientParams);
|
||||
unsecuredSavedObjectsClient.get.mockResolvedValueOnce({
|
||||
|
@ -355,6 +358,9 @@ describe('get()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: injectReferencesFn,
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
const rulesClient = new RulesClient(rulesClientParams);
|
||||
unsecuredSavedObjectsClient.get.mockResolvedValueOnce({
|
||||
|
|
|
@ -116,6 +116,9 @@ export function getBeforeSetup(
|
|||
return { state: {} };
|
||||
},
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
rulesClientParams.getEventLogClient.mockResolvedValue(
|
||||
eventLogClient ?? eventLogClientMock.create()
|
||||
|
|
|
@ -284,6 +284,9 @@ describe('resolve()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: injectReferencesFn,
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
const rulesClient = new RulesClient(rulesClientParams);
|
||||
unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({
|
||||
|
@ -417,6 +420,9 @@ describe('resolve()', () => {
|
|||
extractReferences: jest.fn(),
|
||||
injectReferences: injectReferencesFn,
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
const rulesClient = new RulesClient(rulesClientParams);
|
||||
unsecuredSavedObjectsClient.resolve.mockResolvedValueOnce({
|
||||
|
|
|
@ -171,6 +171,9 @@ describe('update()', () => {
|
|||
return { state: {} };
|
||||
},
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
});
|
||||
(migrateLegacyActions as jest.Mock).mockResolvedValue({
|
||||
hasLegacyActions: false,
|
||||
|
@ -749,6 +752,9 @@ describe('update()', () => {
|
|||
extractReferences: extractReferencesFn,
|
||||
injectReferences: injectReferencesFn,
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
unsecuredSavedObjectsClient.create.mockResolvedValueOnce({
|
||||
id: '1',
|
||||
|
@ -1643,6 +1649,9 @@ describe('update()', () => {
|
|||
return { state: {} };
|
||||
},
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
});
|
||||
encryptedSavedObjects.getDecryptedAsInternalUser.mockResolvedValueOnce({
|
||||
id: alertId,
|
||||
|
|
|
@ -359,6 +359,9 @@ beforeEach(() => {
|
|||
return { state: {} };
|
||||
},
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
}));
|
||||
|
||||
ruleTypeRegistry.get.mockReturnValue({
|
||||
|
@ -373,6 +376,9 @@ beforeEach(() => {
|
|||
return { state: {} };
|
||||
},
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
});
|
||||
|
||||
rulesClient = new RulesClient(rulesClientParams);
|
||||
|
|
|
@ -55,6 +55,9 @@ describe('isRuleExportable', () => {
|
|||
isExportable: true,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
});
|
||||
expect(
|
||||
isRuleExportable(
|
||||
|
@ -111,6 +114,9 @@ describe('isRuleExportable', () => {
|
|||
isExportable: false,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
});
|
||||
expect(
|
||||
isRuleExportable(
|
||||
|
@ -170,6 +176,9 @@ describe('isRuleExportable', () => {
|
|||
isExportable: false,
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
});
|
||||
expect(
|
||||
isRuleExportable(
|
||||
|
|
|
@ -32,6 +32,7 @@ import { AlertInstanceState, AlertInstanceContext } from '../../common';
|
|||
import { asSavedObjectExecutionSource } from '@kbn/actions-plugin/server';
|
||||
import sinon from 'sinon';
|
||||
import { mockAAD } from './fixtures';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
jest.mock('./inject_action_params', () => ({
|
||||
injectActionParams: jest.fn(),
|
||||
|
@ -70,6 +71,9 @@ const ruleType: NormalizedRuleType<
|
|||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
getSummarizedAlerts: getSummarizedAlertsMock,
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
const rule = {
|
||||
id: '1',
|
||||
|
|
|
@ -156,6 +156,9 @@ export const ruleType: jest.Mocked<UntypedNormalizedRuleType> = {
|
|||
cancelAlertsOnRuleTimeout: true,
|
||||
ruleTaskTimeout: '5m',
|
||||
autoRecoverAlerts: true,
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
|
||||
export const mockRunNowResponse = {
|
||||
|
|
|
@ -571,7 +571,7 @@ export class TaskRunner<
|
|||
this.alertingEventLogger.start();
|
||||
|
||||
return await loadRule<Params>({
|
||||
paramValidator: this.ruleType.validate?.params,
|
||||
paramValidator: this.ruleType.validate.params,
|
||||
ruleId,
|
||||
spaceId,
|
||||
context: this.context,
|
||||
|
@ -715,7 +715,7 @@ export class TaskRunner<
|
|||
schedule = asOk(
|
||||
(
|
||||
await loadRule<Params>({
|
||||
paramValidator: this.ruleType.validate?.params,
|
||||
paramValidator: this.ruleType.validate.params,
|
||||
ruleId,
|
||||
spaceId,
|
||||
context: this.context,
|
||||
|
|
|
@ -32,6 +32,7 @@ import { dataViewPluginMocks } from '@kbn/data-views-plugin/public/mocks';
|
|||
import { rulesSettingsClientMock } from '../rules_settings_client.mock';
|
||||
import { maintenanceWindowClientMock } from '../maintenance_window_client.mock';
|
||||
import { alertsServiceMock } from '../alerts_service/alerts_service.mock';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
|
||||
const inMemoryMetrics = inMemoryMetricsMock.create();
|
||||
const executionContext = executionContextServiceMock.createSetupContract();
|
||||
|
@ -58,6 +59,9 @@ const ruleType: UntypedNormalizedRuleType = {
|
|||
},
|
||||
executor: jest.fn(),
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
let fakeTimer: sinon.SinonFakeTimers;
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ export type ExecutorType<
|
|||
) => Promise<{ state: State }>;
|
||||
|
||||
export interface RuleTypeParamsValidator<Params extends RuleTypeParams> {
|
||||
validate: (object: unknown) => Params;
|
||||
validate: (object: Partial<Params>) => Params;
|
||||
validateMutatedParams?: (mutatedOject: Params, origObject?: Params) => Params;
|
||||
}
|
||||
|
||||
|
@ -235,8 +235,8 @@ export interface RuleType<
|
|||
> {
|
||||
id: string;
|
||||
name: string;
|
||||
validate?: {
|
||||
params?: RuleTypeParamsValidator<Params>;
|
||||
validate: {
|
||||
params: RuleTypeParamsValidator<Params>;
|
||||
};
|
||||
actionGroups: Array<ActionGroup<ActionGroupIds>>;
|
||||
defaultActionGroupId: ActionGroup<ActionGroupIds>['id'];
|
||||
|
|
|
@ -103,6 +103,13 @@ export class BaseRule {
|
|||
actionVariables: {
|
||||
context: actionVariables,
|
||||
},
|
||||
// As there is "[key: string]: unknown;" in CommonAlertParams,
|
||||
// we couldn't figure out a schema for validation and created a follow on issue:
|
||||
// https://github.com/elastic/kibana/issues/153754
|
||||
// Below validate function should be overwritten in each monitoring rule type
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -116,11 +116,11 @@ describe('ruleType', () => {
|
|||
groupBy: 'all',
|
||||
};
|
||||
|
||||
expect(ruleType.validate?.params?.validate(params)).toBeTruthy();
|
||||
expect(ruleType.validate.params.validate(params)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('validator fails with invalid es query params - threshold', async () => {
|
||||
const paramsSchema = ruleType.validate?.params;
|
||||
const paramsSchema = ruleType.validate.params;
|
||||
if (!paramsSchema) throw new Error('params validator not set');
|
||||
|
||||
const params: Partial<Writable<OnlyEsQueryRuleParams>> = {
|
||||
|
@ -556,11 +556,11 @@ describe('ruleType', () => {
|
|||
});
|
||||
|
||||
it('validator succeeds with valid search source params', async () => {
|
||||
expect(ruleType.validate?.params?.validate(defaultParams)).toBeTruthy();
|
||||
expect(ruleType.validate.params.validate(defaultParams)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('validator fails with invalid search source params - esQuery provided', async () => {
|
||||
const paramsSchema = ruleType.validate?.params!;
|
||||
const paramsSchema = ruleType.validate.params;
|
||||
const params: Partial<Writable<EsQueryRuleParams>> = {
|
||||
size: 100,
|
||||
timeWindowSize: 5,
|
||||
|
|
|
@ -138,11 +138,11 @@ describe('ruleType', () => {
|
|||
threshold: [0],
|
||||
};
|
||||
|
||||
expect(ruleType.validate?.params?.validate(params)).toBeTruthy();
|
||||
expect(ruleType.validate.params.validate(params)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('validator fails with invalid params', async () => {
|
||||
const paramsSchema = ruleType.validate?.params;
|
||||
const paramsSchema = ruleType.validate.params;
|
||||
if (!paramsSchema) throw new Error('params validator not set');
|
||||
|
||||
const params: Partial<Writable<Params>> = {
|
||||
|
|
|
@ -195,6 +195,9 @@ function getCumulativeFiringAlertType() {
|
|||
},
|
||||
};
|
||||
},
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
@ -531,6 +534,9 @@ function getPatternFiringAlertType() {
|
|||
},
|
||||
};
|
||||
},
|
||||
validate: {
|
||||
params: paramsSchema,
|
||||
},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
@ -572,6 +578,9 @@ function getPatternSuccessOrFailureAlertType() {
|
|||
},
|
||||
};
|
||||
},
|
||||
validate: {
|
||||
params: paramsSchema,
|
||||
},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
@ -646,6 +655,9 @@ function getPatternFiringAutoRecoverFalseAlertType() {
|
|||
},
|
||||
};
|
||||
},
|
||||
validate: {
|
||||
params: paramsSchema,
|
||||
},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
@ -692,6 +704,9 @@ function getLongRunningPatternRuleType(cancelAlertsOnRuleTimeout: boolean = true
|
|||
}
|
||||
return { state: {} };
|
||||
},
|
||||
validate: {
|
||||
params: paramsSchema,
|
||||
},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
@ -752,6 +767,9 @@ function getCancellableRuleType() {
|
|||
|
||||
return { state: {} };
|
||||
},
|
||||
validate: {
|
||||
params: paramsSchema,
|
||||
},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
@ -851,6 +869,9 @@ export function defineAlertTypes(
|
|||
async executor() {
|
||||
return { state: {} };
|
||||
},
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
const goldNoopAlertType: RuleType<{}, {}, {}, {}, {}, 'default'> = {
|
||||
id: 'test.gold.noop',
|
||||
|
@ -863,6 +884,9 @@ export function defineAlertTypes(
|
|||
async executor() {
|
||||
return { state: {} };
|
||||
},
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
const onlyContextVariablesAlertType: RuleType<{}, {}, {}, {}, {}, 'default'> = {
|
||||
id: 'test.onlyContextVariables',
|
||||
|
@ -878,6 +902,9 @@ export function defineAlertTypes(
|
|||
async executor() {
|
||||
return { state: {} };
|
||||
},
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
const onlyStateVariablesAlertType: RuleType<{}, {}, {}, {}, {}, 'default'> = {
|
||||
id: 'test.onlyStateVariables',
|
||||
|
@ -893,6 +920,9 @@ export function defineAlertTypes(
|
|||
async executor() {
|
||||
return { state: {} };
|
||||
},
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
const throwAlertType: RuleType<{}, {}, {}, {}, {}, 'default'> = {
|
||||
id: 'test.throw',
|
||||
|
@ -910,6 +940,9 @@ export function defineAlertTypes(
|
|||
async executor() {
|
||||
throw new Error('this alert is intended to fail');
|
||||
},
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
function getLongRunningRuleType() {
|
||||
const paramsSchema = schema.object({
|
||||
|
@ -935,6 +968,9 @@ export function defineAlertTypes(
|
|||
await new Promise((resolve) => setTimeout(resolve, params.delay ?? 5000));
|
||||
return { state: {} };
|
||||
},
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
@ -953,7 +989,11 @@ export function defineAlertTypes(
|
|||
return { state: {} };
|
||||
},
|
||||
producer: 'alertsFixture',
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
|
||||
const multipleSearchesRuleType: RuleType<
|
||||
{ numSearches: number; delay: string },
|
||||
{},
|
||||
|
@ -1006,6 +1046,9 @@ export function defineAlertTypes(
|
|||
|
||||
return { state: {} };
|
||||
},
|
||||
validate: {
|
||||
params: schema.object({ numSearches: schema.number(), delay: schema.string() }),
|
||||
},
|
||||
};
|
||||
|
||||
alerting.registerType(getAlwaysFiringAlertType());
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
import { CoreSetup } from '@kbn/core/server';
|
||||
import { RuleType } from '@kbn/alerting-plugin/server';
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { FixtureStartDeps, FixtureSetupDeps } from './plugin';
|
||||
|
||||
export function defineAlertTypes(
|
||||
|
@ -25,6 +26,9 @@ export function defineAlertTypes(
|
|||
async executor() {
|
||||
return { state: {} };
|
||||
},
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
const noopUnrestrictedAlertType: RuleType<{}, {}, {}, {}, {}, 'default'> = {
|
||||
id: 'test.unrestricted-noop',
|
||||
|
@ -37,6 +41,9 @@ export function defineAlertTypes(
|
|||
async executor() {
|
||||
return { state: {} };
|
||||
},
|
||||
validate: {
|
||||
params: schema.any(),
|
||||
},
|
||||
};
|
||||
alerting.registerType(noopRestrictedAlertType);
|
||||
alerting.registerType(noopUnrestrictedAlertType);
|
||||
|
|
|
@ -82,6 +82,9 @@ export class FixturePlugin implements Plugin<void, void, FixtureSetupDeps, Fixtu
|
|||
await coreStart.elasticsearch.client.asInternalUser.ping();
|
||||
return { state: {} };
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
});
|
||||
|
||||
const router = core.http.createRouter();
|
||||
|
|
|
@ -6,7 +6,11 @@
|
|||
*/
|
||||
|
||||
import { Plugin, CoreSetup } from '@kbn/core/server';
|
||||
import { PluginSetupContract as AlertingSetup, RuleType } from '@kbn/alerting-plugin/server';
|
||||
import {
|
||||
PluginSetupContract as AlertingSetup,
|
||||
RuleType,
|
||||
RuleTypeParams,
|
||||
} from '@kbn/alerting-plugin/server';
|
||||
import { PluginSetupContract as FeaturesPluginSetup } from '@kbn/features-plugin/server';
|
||||
|
||||
// this plugin's dependendencies
|
||||
|
@ -26,10 +30,17 @@ export const noopAlertType: RuleType<{}, {}, {}, {}, {}, 'default'> = {
|
|||
return { state: {} };
|
||||
},
|
||||
producer: 'alerts',
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
|
||||
interface AlwaysFiringParams extends RuleTypeParams {
|
||||
instances: Array<{ id: string; state: any }>;
|
||||
}
|
||||
|
||||
export const alwaysFiringAlertType: RuleType<
|
||||
{ instances: Array<{ id: string; state: any }> },
|
||||
AlwaysFiringParams,
|
||||
never, // Only use if defining useSavedObjectReferences hook
|
||||
{
|
||||
globalStateValue: boolean;
|
||||
|
@ -66,6 +77,9 @@ export const alwaysFiringAlertType: RuleType<
|
|||
},
|
||||
};
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params as AlwaysFiringParams },
|
||||
},
|
||||
};
|
||||
|
||||
export const failingAlertType: RuleType<never, never, never, never, never, 'default' | 'other'> = {
|
||||
|
@ -84,6 +98,9 @@ export const failingAlertType: RuleType<never, never, never, never, never, 'defa
|
|||
async executor() {
|
||||
throw new Error('Failed to execute alert type');
|
||||
},
|
||||
validate: {
|
||||
params: { validate: (params) => params },
|
||||
},
|
||||
};
|
||||
|
||||
export class AlertingFixturePlugin implements Plugin<void, void, AlertingExampleDeps> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue