mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Security Solution] DetectionRulesClient
: return RuleResponse
from all methods (#186179)
**Partially addresses: https://github.com/elastic/kibana/issues/184364** ## Summary This PR is a follow-up to [PR #185748](https://github.com/elastic/kibana/pull/185748) and it converts the remaining `DetectionRulesClient` methods to return `RuleResponse`. Changes in this PR: - These methods now return `RuleResponse` instead of internal `RuleAlertType` type: - `updateRule` - `patchRule` - `upgradePrebuiltRule` - `importRule`
This commit is contained in:
parent
385bb2b35b
commit
55687dd539
26 changed files with 224 additions and 165 deletions
|
@ -21,7 +21,6 @@ import type { SecuritySolutionPluginRouter } from '../../../../../types';
|
|||
import { buildRouteValidation } from '../../../../../utils/build_validation/route_validation';
|
||||
import type { PromisePoolError } from '../../../../../utils/promise_pool';
|
||||
import { buildSiemResponse } from '../../../routes/utils';
|
||||
import { internalRuleToAPIResponse } from '../../../rule_management/normalization/rule_converters';
|
||||
import { aggregatePrebuiltRuleErrors } from '../../logic/aggregate_prebuilt_rule_errors';
|
||||
import { performTimelinesInstallation } from '../../logic/perform_timelines_installation';
|
||||
import { createPrebuiltRuleAssetsClient } from '../../logic/rule_assets/prebuilt_rule_assets_client';
|
||||
|
@ -182,7 +181,7 @@ export const performRuleUpgradeRoute = (router: SecuritySolutionPluginRouter) =>
|
|||
failed: ruleErrors.length,
|
||||
},
|
||||
results: {
|
||||
updated: updatedRules.map(({ result }) => internalRuleToAPIResponse(result)),
|
||||
updated: updatedRules.map(({ result }) => result),
|
||||
skipped: skippedRules,
|
||||
},
|
||||
errors: allErrors,
|
||||
|
|
|
@ -17,6 +17,10 @@ import {
|
|||
typicalMlRulePayload,
|
||||
} from '../../../../routes/__mocks__/request_responses';
|
||||
import { serverMock, requestContextMock, requestMock } from '../../../../routes/__mocks__';
|
||||
import {
|
||||
getRulesSchemaMock,
|
||||
getRulesMlSchemaMock,
|
||||
} from '../../../../../../../common/api/detection_engine/model/rule_schema/rule_response_schema.mock';
|
||||
import { bulkPatchRulesRoute } from './route';
|
||||
import { getCreateRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks';
|
||||
import { getMlRuleParams, getQueryRuleParams } from '../../../../rule_schema/mocks';
|
||||
|
@ -34,7 +38,7 @@ describe('Bulk patch rules route', () => {
|
|||
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists
|
||||
clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // update succeeds
|
||||
clients.detectionRulesClient.patchRule.mockResolvedValue(getRuleMock(getQueryRuleParams()));
|
||||
clients.detectionRulesClient.patchRule.mockResolvedValue(getRulesSchemaMock());
|
||||
|
||||
bulkPatchRulesRoute(server.router, logger);
|
||||
});
|
||||
|
@ -72,14 +76,11 @@ describe('Bulk patch rules route', () => {
|
|||
...getFindResultWithSingleHit(),
|
||||
data: [getRuleMock(getMlRuleParams())],
|
||||
});
|
||||
clients.detectionRulesClient.patchRule.mockResolvedValueOnce(
|
||||
getRuleMock(
|
||||
getMlRuleParams({
|
||||
anomalyThreshold,
|
||||
machineLearningJobId: [machineLearningJobId],
|
||||
})
|
||||
)
|
||||
);
|
||||
clients.detectionRulesClient.patchRule.mockResolvedValueOnce({
|
||||
...getRulesMlSchemaMock(),
|
||||
anomaly_threshold: anomalyThreshold,
|
||||
machine_learning_job_id: [machineLearningJobId],
|
||||
});
|
||||
|
||||
const request = requestMock.create({
|
||||
method: 'patch',
|
||||
|
|
|
@ -17,7 +17,6 @@ import {
|
|||
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
|
||||
import { transformBulkError, buildSiemResponse } from '../../../../routes/utils';
|
||||
import { getIdBulkError } from '../../../utils/utils';
|
||||
import { transformValidateBulkError } from '../../../utils/validate';
|
||||
import { readRules } from '../../../logic/detection_rules_client/read_rules';
|
||||
import { getDeprecatedBulkEndpointHeader, logDeprecatedBulkEndpoint } from '../../deprecation';
|
||||
import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list';
|
||||
|
@ -86,11 +85,11 @@ export const bulkPatchRulesRoute = (router: SecuritySolutionPluginRouter, logger
|
|||
ruleId: payloadRule.id,
|
||||
});
|
||||
|
||||
const rule = await detectionRulesClient.patchRule({
|
||||
const patchedRule = await detectionRulesClient.patchRule({
|
||||
nextParams: payloadRule,
|
||||
});
|
||||
|
||||
return transformValidateBulkError(rule.id, rule);
|
||||
return patchedRule;
|
||||
} catch (err) {
|
||||
return transformBulkError(idOrRuleIdOrUnknown, err);
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import {
|
|||
typicalMlRulePayload,
|
||||
} from '../../../../routes/__mocks__/request_responses';
|
||||
import { serverMock, requestContextMock, requestMock } from '../../../../routes/__mocks__';
|
||||
import { getRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/rule_response_schema.mock';
|
||||
import { bulkUpdateRulesRoute } from './route';
|
||||
import type { BulkError } from '../../../../routes/utils';
|
||||
import { getCreateRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/mocks';
|
||||
|
@ -32,7 +33,7 @@ describe('Bulk update rules route', () => {
|
|||
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit());
|
||||
clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams()));
|
||||
clients.detectionRulesClient.updateRule.mockResolvedValue(getRuleMock(getQueryRuleParams()));
|
||||
clients.detectionRulesClient.updateRule.mockResolvedValue(getRulesSchemaMock());
|
||||
clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index');
|
||||
|
||||
bulkUpdateRulesRoute(server.router, logger);
|
||||
|
|
|
@ -16,7 +16,6 @@ import {
|
|||
import type { SecuritySolutionPluginRouter } from '../../../../../../types';
|
||||
import { DETECTION_ENGINE_RULES_BULK_UPDATE } from '../../../../../../../common/constants';
|
||||
import { getIdBulkError } from '../../../utils/utils';
|
||||
import { transformValidateBulkError } from '../../../utils/validate';
|
||||
import {
|
||||
transformBulkError,
|
||||
buildSiemResponse,
|
||||
|
@ -97,11 +96,11 @@ export const bulkUpdateRulesRoute = (router: SecuritySolutionPluginRouter, logge
|
|||
ruleId: payloadRule.id,
|
||||
});
|
||||
|
||||
const rule = await detectionRulesClient.updateRule({
|
||||
const updatedRule = await detectionRulesClient.updateRule({
|
||||
ruleUpdate: payloadRule,
|
||||
});
|
||||
|
||||
return transformValidateBulkError(rule.id, rule);
|
||||
return updatedRule;
|
||||
} catch (err) {
|
||||
return transformBulkError(idOrRuleIdOrUnknown, err);
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
ruleIdsToNdJsonString,
|
||||
rulesToNdJsonString,
|
||||
} from '../../../../../../../common/api/detection_engine/rule_management/mocks';
|
||||
import { getRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/rule_response_schema.mock';
|
||||
|
||||
import type { requestMock } from '../../../../routes/__mocks__';
|
||||
import { createMockConfig, requestContextMock, serverMock } from '../../../../routes/__mocks__';
|
||||
|
@ -47,7 +48,8 @@ describe('Import rules route', () => {
|
|||
|
||||
clients.rulesClient.find.mockResolvedValue(getEmptyFindResult()); // no extant rules
|
||||
clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams()));
|
||||
clients.detectionRulesClient.importRule.mockResolvedValue(getRuleMock(getQueryRuleParams()));
|
||||
clients.detectionRulesClient.createCustomRule.mockResolvedValue(getRulesSchemaMock());
|
||||
clients.detectionRulesClient.importRule.mockResolvedValue(getRulesSchemaMock());
|
||||
clients.actionsClient.getAll.mockResolvedValue([]);
|
||||
context.core.elasticsearch.client.asCurrentUser.search.mockResolvedValue(
|
||||
elasticsearchClientMock.createSuccessTransportRequestPromise(getBasicEmptySearchResponse())
|
||||
|
|
|
@ -20,6 +20,11 @@ import {
|
|||
|
||||
import { getMlRuleParams, getQueryRuleParams } from '../../../../rule_schema/mocks';
|
||||
|
||||
import {
|
||||
getRulesSchemaMock,
|
||||
getRulesMlSchemaMock,
|
||||
} from '../../../../../../../common/api/detection_engine/model/rule_schema/rule_response_schema.mock';
|
||||
|
||||
import { patchRuleRoute } from './route';
|
||||
import { HttpAuthzError } from '../../../../../machine_learning/validation';
|
||||
|
||||
|
@ -34,7 +39,7 @@ describe('Patch rule route', () => {
|
|||
clients.rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); // existing rule
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // existing rule
|
||||
clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // successful update
|
||||
clients.detectionRulesClient.patchRule.mockResolvedValue(getRuleMock(getQueryRuleParams()));
|
||||
clients.detectionRulesClient.patchRule.mockResolvedValue(getRulesSchemaMock());
|
||||
|
||||
patchRuleRoute(server.router);
|
||||
});
|
||||
|
@ -99,14 +104,11 @@ describe('Patch rule route', () => {
|
|||
|
||||
const anomalyThreshold = 4;
|
||||
const machineLearningJobId = 'some_job_id';
|
||||
clients.detectionRulesClient.patchRule.mockResolvedValueOnce(
|
||||
getRuleMock(
|
||||
getMlRuleParams({
|
||||
anomalyThreshold,
|
||||
machineLearningJobId: [machineLearningJobId],
|
||||
})
|
||||
)
|
||||
);
|
||||
clients.detectionRulesClient.patchRule.mockResolvedValueOnce({
|
||||
...getRulesMlSchemaMock(),
|
||||
anomaly_threshold: anomalyThreshold,
|
||||
machine_learning_job_id: [machineLearningJobId],
|
||||
});
|
||||
|
||||
const request = requestMock.create({
|
||||
method: 'patch',
|
||||
|
|
|
@ -20,7 +20,6 @@ import { readRules } from '../../../logic/detection_rules_client/read_rules';
|
|||
import { checkDefaultRuleExceptionListReferences } from '../../../logic/exceptions/check_for_default_rule_exception_list';
|
||||
import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list';
|
||||
import { getIdError } from '../../../utils/utils';
|
||||
import { transformValidate } from '../../../utils/validate';
|
||||
|
||||
export const patchRuleRoute = (router: SecuritySolutionPluginRouter) => {
|
||||
router.versioned
|
||||
|
@ -76,12 +75,12 @@ export const patchRuleRoute = (router: SecuritySolutionPluginRouter) => {
|
|||
ruleId: params.id,
|
||||
});
|
||||
|
||||
const rule = await detectionRulesClient.patchRule({
|
||||
const patchedRule = await detectionRulesClient.patchRule({
|
||||
nextParams: params,
|
||||
});
|
||||
|
||||
return response.ok({
|
||||
body: transformValidate(rule),
|
||||
body: patchedRule,
|
||||
});
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
|
|
|
@ -13,6 +13,7 @@ import {
|
|||
typicalMlRulePayload,
|
||||
} from '../../../../routes/__mocks__/request_responses';
|
||||
import { requestContextMock, serverMock, requestMock } from '../../../../routes/__mocks__';
|
||||
import { getRulesSchemaMock } from '../../../../../../../common/api/detection_engine/model/rule_schema/rule_response_schema.mock';
|
||||
import { DETECTION_ENGINE_RULES_URL } from '../../../../../../../common/constants';
|
||||
import { updateRuleRoute } from './route';
|
||||
import {
|
||||
|
@ -34,7 +35,7 @@ describe('Update rule route', () => {
|
|||
clients.rulesClient.get.mockResolvedValue(getRuleMock(getQueryRuleParams())); // existing rule
|
||||
clients.rulesClient.find.mockResolvedValue(getFindResultWithSingleHit()); // rule exists
|
||||
clients.rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams())); // successful update
|
||||
clients.detectionRulesClient.updateRule.mockResolvedValue(getRuleMock(getQueryRuleParams()));
|
||||
clients.detectionRulesClient.updateRule.mockResolvedValue(getRulesSchemaMock());
|
||||
clients.appClient.getSignalsIndex.mockReturnValue('.siem-signals-test-index');
|
||||
|
||||
updateRuleRoute(server.router);
|
||||
|
|
|
@ -20,7 +20,7 @@ import { readRules } from '../../../logic/detection_rules_client/read_rules';
|
|||
import { checkDefaultRuleExceptionListReferences } from '../../../logic/exceptions/check_for_default_rule_exception_list';
|
||||
import { validateRuleDefaultExceptionList } from '../../../logic/exceptions/validate_rule_default_exception_list';
|
||||
import { getIdError } from '../../../utils/utils';
|
||||
import { transformValidate, validateResponseActionsPermissions } from '../../../utils/validate';
|
||||
import { validateResponseActionsPermissions } from '../../../utils/validate';
|
||||
|
||||
export const updateRuleRoute = (router: SecuritySolutionPluginRouter) => {
|
||||
router.versioned
|
||||
|
@ -80,12 +80,12 @@ export const updateRuleRoute = (router: SecuritySolutionPluginRouter) => {
|
|||
existingRule
|
||||
);
|
||||
|
||||
const rule = await detectionRulesClient.updateRule({
|
||||
const updatedRule = await detectionRulesClient.updateRule({
|
||||
ruleUpdate: request.body,
|
||||
});
|
||||
|
||||
return response.ok({
|
||||
body: transformValidate(rule),
|
||||
body: updatedRule,
|
||||
});
|
||||
} catch (err) {
|
||||
const error = transformError(err);
|
||||
|
|
|
@ -19,8 +19,6 @@ import { buildMlAuthz } from '../../../../machine_learning/authz';
|
|||
import { throwAuthzError } from '../../../../machine_learning/validation';
|
||||
import { createDetectionRulesClient } from './detection_rules_client';
|
||||
import type { IDetectionRulesClient } from './detection_rules_client_interface';
|
||||
import { RuleResponseValidationError } from './utils';
|
||||
import type { RuleAlertType } from '../../../rule_schema';
|
||||
|
||||
jest.mock('../../../../machine_learning/authz');
|
||||
jest.mock('../../../../machine_learning/validation');
|
||||
|
@ -70,20 +68,6 @@ describe('DetectionRulesClient.createCustomRule', () => {
|
|||
expect(rulesClient.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('throws if RuleResponse validation fails', async () => {
|
||||
const internalRuleMock: RuleAlertType = getRuleMock({
|
||||
...getQueryRuleParams(),
|
||||
/* Casting as 'query' suppress to TS error */
|
||||
type: 'fake-non-existent-type' as 'query',
|
||||
});
|
||||
|
||||
rulesClient.create.mockResolvedValueOnce(internalRuleMock);
|
||||
|
||||
await expect(
|
||||
detectionRulesClient.createCustomRule({ params: getCreateMachineLearningRulesSchemaMock() })
|
||||
).rejects.toThrow(RuleResponseValidationError);
|
||||
});
|
||||
|
||||
it('calls the rulesClient with legacy ML params', async () => {
|
||||
await detectionRulesClient.createCustomRule({
|
||||
params: getCreateMachineLearningRulesSchemaMock(),
|
||||
|
|
|
@ -42,6 +42,8 @@ describe('DetectionRulesClient.importRule', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
rulesClient = rulesClientMock.create();
|
||||
rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams()));
|
||||
rulesClient.update.mockResolvedValue(getRuleMock(getQueryRuleParams()));
|
||||
detectionRulesClient = createDetectionRulesClient(rulesClient, mlAuthz);
|
||||
});
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
import type { RulesClient } from '@kbn/alerting-plugin/server';
|
||||
import type { MlAuthz } from '../../../../machine_learning/authz';
|
||||
|
||||
import type { RuleAlertType } from '../../../rule_schema';
|
||||
import type { RuleResponse } from '../../../../../../common/api/detection_engine/model/rule_schema';
|
||||
import type {
|
||||
IDetectionRulesClient,
|
||||
|
@ -47,13 +46,13 @@ export const createDetectionRulesClient = (
|
|||
});
|
||||
},
|
||||
|
||||
async updateRule(args: UpdateRuleArgs): Promise<RuleAlertType> {
|
||||
async updateRule(args: UpdateRuleArgs): Promise<RuleResponse> {
|
||||
return withSecuritySpan('DetectionRulesClient.updateRule', async () => {
|
||||
return updateRule(rulesClient, args, mlAuthz);
|
||||
});
|
||||
},
|
||||
|
||||
async patchRule(args: PatchRuleArgs): Promise<RuleAlertType> {
|
||||
async patchRule(args: PatchRuleArgs): Promise<RuleResponse> {
|
||||
return withSecuritySpan('DetectionRulesClient.patchRule', async () => {
|
||||
return patchRule(rulesClient, args, mlAuthz);
|
||||
});
|
||||
|
@ -65,13 +64,13 @@ export const createDetectionRulesClient = (
|
|||
});
|
||||
},
|
||||
|
||||
async upgradePrebuiltRule(args: UpgradePrebuiltRuleArgs): Promise<RuleAlertType> {
|
||||
async upgradePrebuiltRule(args: UpgradePrebuiltRuleArgs): Promise<RuleResponse> {
|
||||
return withSecuritySpan('DetectionRulesClient.upgradePrebuiltRule', async () => {
|
||||
return upgradePrebuiltRule(rulesClient, args, mlAuthz);
|
||||
});
|
||||
},
|
||||
|
||||
async importRule(args: ImportRuleArgs): Promise<RuleAlertType> {
|
||||
async importRule(args: ImportRuleArgs): Promise<RuleResponse> {
|
||||
return withSecuritySpan('DetectionRulesClient.importRule', async () => {
|
||||
return importRule(rulesClient, args, mlAuthz);
|
||||
});
|
||||
|
|
|
@ -99,10 +99,12 @@ describe('DetectionRulesClient.upgradePrebuiltRule', () => {
|
|||
ruleId: 'rule-id',
|
||||
});
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
rulesClient.create.mockResolvedValue(getRuleMock(getQueryRuleParams()));
|
||||
(readRules as jest.Mock).mockResolvedValue(installedRule);
|
||||
});
|
||||
|
||||
it('deletes the old rule ', async () => {
|
||||
it('deletes the old rule', async () => {
|
||||
await detectionRulesClient.upgradePrebuiltRule({ ruleAsset });
|
||||
expect(rulesClient.delete).toHaveBeenCalled();
|
||||
});
|
||||
|
@ -153,6 +155,8 @@ describe('DetectionRulesClient.upgradePrebuiltRule', () => {
|
|||
});
|
||||
|
||||
it('patches the existing rule with the new params from the rule asset', async () => {
|
||||
rulesClient.update.mockResolvedValue(getRuleMock(getEqlRuleParams()));
|
||||
|
||||
await detectionRulesClient.upgradePrebuiltRule({ ruleAsset });
|
||||
expect(rulesClient.update).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
|
|
|
@ -13,17 +13,16 @@ import type {
|
|||
RuleToImport,
|
||||
RuleResponse,
|
||||
} from '../../../../../../common/api/detection_engine';
|
||||
import type { RuleAlertType } from '../../../rule_schema';
|
||||
import type { PrebuiltRuleAsset } from '../../../prebuilt_rules';
|
||||
|
||||
export interface IDetectionRulesClient {
|
||||
createCustomRule: (args: CreateCustomRuleArgs) => Promise<RuleResponse>;
|
||||
createPrebuiltRule: (args: CreatePrebuiltRuleArgs) => Promise<RuleResponse>;
|
||||
updateRule: (args: UpdateRuleArgs) => Promise<RuleAlertType>;
|
||||
patchRule: (args: PatchRuleArgs) => Promise<RuleAlertType>;
|
||||
updateRule: (args: UpdateRuleArgs) => Promise<RuleResponse>;
|
||||
patchRule: (args: PatchRuleArgs) => Promise<RuleResponse>;
|
||||
deleteRule: (args: DeleteRuleArgs) => Promise<void>;
|
||||
upgradePrebuiltRule: (args: UpgradePrebuiltRuleArgs) => Promise<RuleAlertType>;
|
||||
importRule: (args: ImportRuleArgs) => Promise<RuleAlertType>;
|
||||
upgradePrebuiltRule: (args: UpgradePrebuiltRuleArgs) => Promise<RuleResponse>;
|
||||
importRule: (args: ImportRuleArgs) => Promise<RuleResponse>;
|
||||
}
|
||||
|
||||
export interface CreateCustomRuleArgs {
|
||||
|
|
|
@ -11,8 +11,10 @@ import type { CreateCustomRuleArgs } from '../detection_rules_client_interface';
|
|||
import type { MlAuthz } from '../../../../../machine_learning/authz';
|
||||
import type { RuleParams } from '../../../../rule_schema';
|
||||
import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema';
|
||||
import { convertCreateAPIToInternalSchema } from '../../../normalization/rule_converters';
|
||||
import { transform } from '../../../utils/utils';
|
||||
import {
|
||||
convertCreateAPIToInternalSchema,
|
||||
internalRuleToAPIResponse,
|
||||
} from '../../../normalization/rule_converters';
|
||||
import { validateMlAuth, RuleResponseValidationError } from '../utils';
|
||||
|
||||
export const createCustomRule = async (
|
||||
|
@ -29,7 +31,7 @@ export const createCustomRule = async (
|
|||
});
|
||||
|
||||
/* Trying to convert the rule to a RuleResponse object */
|
||||
const parseResult = RuleResponse.safeParse(transform(rule));
|
||||
const parseResult = RuleResponse.safeParse(internalRuleToAPIResponse(rule));
|
||||
|
||||
if (!parseResult.success) {
|
||||
throw new RuleResponseValidationError({
|
||||
|
|
|
@ -11,8 +11,10 @@ import type { CreatePrebuiltRuleArgs } from '../detection_rules_client_interface
|
|||
import type { MlAuthz } from '../../../../../machine_learning/authz';
|
||||
import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema';
|
||||
import type { RuleParams } from '../../../../rule_schema';
|
||||
import { convertCreateAPIToInternalSchema } from '../../../normalization/rule_converters';
|
||||
import { transform } from '../../../utils/utils';
|
||||
import {
|
||||
convertCreateAPIToInternalSchema,
|
||||
internalRuleToAPIResponse,
|
||||
} from '../../../normalization/rule_converters';
|
||||
import { validateMlAuth, RuleResponseValidationError } from '../utils';
|
||||
|
||||
export const createPrebuiltRule = async (
|
||||
|
@ -34,7 +36,7 @@ export const createPrebuiltRule = async (
|
|||
});
|
||||
|
||||
/* Trying to convert the rule to a RuleResponse object */
|
||||
const parseResult = RuleResponse.safeParse(transform(rule));
|
||||
const parseResult = RuleResponse.safeParse(internalRuleToAPIResponse(rule));
|
||||
|
||||
if (!parseResult.success) {
|
||||
throw new RuleResponseValidationError({
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
|
||||
import type { RulesClient } from '@kbn/alerting-plugin/server';
|
||||
import { stringifyZodError } from '@kbn/zod-helpers';
|
||||
import type { MlAuthz } from '../../../../../machine_learning/authz';
|
||||
import type { ImportRuleArgs } from '../detection_rules_client_interface';
|
||||
import type { RuleAlertType, RuleParams } from '../../../../rule_schema';
|
||||
|
@ -13,9 +14,11 @@ import { createBulkErrorObject } from '../../../../routes/utils';
|
|||
import {
|
||||
convertCreateAPIToInternalSchema,
|
||||
convertUpdateAPIToInternalSchema,
|
||||
internalRuleToAPIResponse,
|
||||
} from '../../../normalization/rule_converters';
|
||||
import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema';
|
||||
|
||||
import { validateMlAuth } from '../utils';
|
||||
import { validateMlAuth, RuleResponseValidationError } from '../utils';
|
||||
|
||||
import { readRules } from '../read_rules';
|
||||
|
||||
|
@ -23,7 +26,7 @@ export const importRule = async (
|
|||
rulesClient: RulesClient,
|
||||
importRulePayload: ImportRuleArgs,
|
||||
mlAuthz: MlAuthz
|
||||
): Promise<RuleAlertType> => {
|
||||
): Promise<RuleResponse> => {
|
||||
const { ruleToImport, overwriteRules, allowMissingConnectorSecrets } = importRulePayload;
|
||||
|
||||
await validateMlAuth(mlAuthz, ruleToImport.type);
|
||||
|
@ -34,30 +37,47 @@ export const importRule = async (
|
|||
id: undefined,
|
||||
});
|
||||
|
||||
if (!existingRule) {
|
||||
const internalRule = convertCreateAPIToInternalSchema(ruleToImport, {
|
||||
immutable: false,
|
||||
});
|
||||
|
||||
return rulesClient.create<RuleParams>({
|
||||
data: internalRule,
|
||||
allowMissingConnectorSecrets,
|
||||
});
|
||||
} else if (existingRule && overwriteRules) {
|
||||
const newInternalRule = convertUpdateAPIToInternalSchema({
|
||||
existingRule,
|
||||
ruleUpdate: ruleToImport,
|
||||
});
|
||||
|
||||
return rulesClient.update({
|
||||
id: existingRule.id,
|
||||
data: newInternalRule,
|
||||
});
|
||||
} else {
|
||||
if (existingRule && !overwriteRules) {
|
||||
throw createBulkErrorObject({
|
||||
ruleId: existingRule.params.ruleId,
|
||||
statusCode: 409,
|
||||
message: `rule_id: "${existingRule.params.ruleId}" already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
let importedInternalRule: RuleAlertType;
|
||||
|
||||
if (existingRule && overwriteRules) {
|
||||
const ruleUpdateParams = convertUpdateAPIToInternalSchema({
|
||||
existingRule,
|
||||
ruleUpdate: ruleToImport,
|
||||
});
|
||||
|
||||
importedInternalRule = await rulesClient.update({
|
||||
id: existingRule.id,
|
||||
data: ruleUpdateParams,
|
||||
});
|
||||
} else {
|
||||
/* Rule does not exist, so we'll create it */
|
||||
const ruleCreateParams = convertCreateAPIToInternalSchema(ruleToImport, {
|
||||
immutable: false,
|
||||
});
|
||||
|
||||
importedInternalRule = await rulesClient.create<RuleParams>({
|
||||
data: ruleCreateParams,
|
||||
allowMissingConnectorSecrets,
|
||||
});
|
||||
}
|
||||
|
||||
/* Trying to convert an internal rule to a RuleResponse object */
|
||||
const parseResult = RuleResponse.safeParse(internalRuleToAPIResponse(importedInternalRule));
|
||||
|
||||
if (!parseResult.success) {
|
||||
throw new RuleResponseValidationError({
|
||||
message: stringifyZodError(parseResult.error),
|
||||
ruleId: importedInternalRule.params.ruleId,
|
||||
});
|
||||
}
|
||||
|
||||
return parseResult.data;
|
||||
};
|
||||
|
|
|
@ -6,13 +6,22 @@
|
|||
*/
|
||||
|
||||
import type { RulesClient } from '@kbn/alerting-plugin/server';
|
||||
import { stringifyZodError } from '@kbn/zod-helpers';
|
||||
import type { MlAuthz } from '../../../../../machine_learning/authz';
|
||||
import type { PatchRuleArgs } from '../detection_rules_client_interface';
|
||||
import type { RuleAlertType } from '../../../../rule_schema';
|
||||
import { getIdError } from '../../../utils/utils';
|
||||
import { convertPatchAPIToInternalSchema } from '../../../normalization/rule_converters';
|
||||
import {
|
||||
convertPatchAPIToInternalSchema,
|
||||
internalRuleToAPIResponse,
|
||||
} from '../../../normalization/rule_converters';
|
||||
import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema';
|
||||
|
||||
import { validateMlAuth, ClientError, toggleRuleEnabledOnUpdate } from '../utils';
|
||||
import {
|
||||
validateMlAuth,
|
||||
ClientError,
|
||||
toggleRuleEnabledOnUpdate,
|
||||
RuleResponseValidationError,
|
||||
} from '../utils';
|
||||
|
||||
import { readRules } from '../read_rules';
|
||||
|
||||
|
@ -20,7 +29,7 @@ export const patchRule = async (
|
|||
rulesClient: RulesClient,
|
||||
args: PatchRuleArgs,
|
||||
mlAuthz: MlAuthz
|
||||
): Promise<RuleAlertType> => {
|
||||
): Promise<RuleResponse> => {
|
||||
const { nextParams } = args;
|
||||
const { rule_id: ruleId, id } = nextParams;
|
||||
|
||||
|
@ -39,16 +48,28 @@ export const patchRule = async (
|
|||
|
||||
const patchedRule = convertPatchAPIToInternalSchema(nextParams, existingRule);
|
||||
|
||||
const update = await rulesClient.update({
|
||||
const patchedInternalRule = await rulesClient.update({
|
||||
id: existingRule.id,
|
||||
data: patchedRule,
|
||||
});
|
||||
|
||||
await toggleRuleEnabledOnUpdate(rulesClient, existingRule, nextParams.enabled);
|
||||
const { enabled } = await toggleRuleEnabledOnUpdate(
|
||||
rulesClient,
|
||||
existingRule,
|
||||
nextParams.enabled
|
||||
);
|
||||
|
||||
if (nextParams.enabled != null) {
|
||||
return { ...update, enabled: nextParams.enabled };
|
||||
} else {
|
||||
return update;
|
||||
/* Trying to convert the internal rule to a RuleResponse object */
|
||||
const parseResult = RuleResponse.safeParse(
|
||||
internalRuleToAPIResponse({ ...patchedInternalRule, enabled })
|
||||
);
|
||||
|
||||
if (!parseResult.success) {
|
||||
throw new RuleResponseValidationError({
|
||||
message: stringifyZodError(parseResult.error),
|
||||
ruleId: patchedInternalRule.params.ruleId,
|
||||
});
|
||||
}
|
||||
|
||||
return parseResult.data;
|
||||
};
|
||||
|
|
|
@ -6,13 +6,22 @@
|
|||
*/
|
||||
|
||||
import type { RulesClient } from '@kbn/alerting-plugin/server';
|
||||
import { stringifyZodError } from '@kbn/zod-helpers';
|
||||
import type { MlAuthz } from '../../../../../machine_learning/authz';
|
||||
import type { RuleAlertType } from '../../../../rule_schema';
|
||||
import type { UpdateRuleArgs } from '../detection_rules_client_interface';
|
||||
import { getIdError } from '../../../utils/utils';
|
||||
import { convertUpdateAPIToInternalSchema } from '../../../normalization/rule_converters';
|
||||
import {
|
||||
convertUpdateAPIToInternalSchema,
|
||||
internalRuleToAPIResponse,
|
||||
} from '../../../normalization/rule_converters';
|
||||
import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema';
|
||||
|
||||
import { validateMlAuth, ClientError, toggleRuleEnabledOnUpdate } from '../utils';
|
||||
import {
|
||||
validateMlAuth,
|
||||
ClientError,
|
||||
toggleRuleEnabledOnUpdate,
|
||||
RuleResponseValidationError,
|
||||
} from '../utils';
|
||||
|
||||
import { readRules } from '../read_rules';
|
||||
|
||||
|
@ -20,7 +29,7 @@ export const updateRule = async (
|
|||
rulesClient: RulesClient,
|
||||
args: UpdateRuleArgs,
|
||||
mlAuthz: MlAuthz
|
||||
): Promise<RuleAlertType> => {
|
||||
): Promise<RuleResponse> => {
|
||||
const { ruleUpdate } = args;
|
||||
const { rule_id: ruleId, id } = ruleUpdate;
|
||||
|
||||
|
@ -42,12 +51,28 @@ export const updateRule = async (
|
|||
ruleUpdate,
|
||||
});
|
||||
|
||||
const update = await rulesClient.update({
|
||||
const updatedInternalRule = await rulesClient.update({
|
||||
id: existingRule.id,
|
||||
data: newInternalRule,
|
||||
});
|
||||
|
||||
await toggleRuleEnabledOnUpdate(rulesClient, existingRule, ruleUpdate.enabled);
|
||||
const { enabled } = await toggleRuleEnabledOnUpdate(
|
||||
rulesClient,
|
||||
existingRule,
|
||||
ruleUpdate.enabled
|
||||
);
|
||||
|
||||
return { ...update, enabled: ruleUpdate.enabled ?? existingRule.enabled };
|
||||
/* Trying to convert the internal rule to a RuleResponse object */
|
||||
const parseResult = RuleResponse.safeParse(
|
||||
internalRuleToAPIResponse({ ...updatedInternalRule, enabled })
|
||||
);
|
||||
|
||||
if (!parseResult.success) {
|
||||
throw new RuleResponseValidationError({
|
||||
message: stringifyZodError(parseResult.error),
|
||||
ruleId: updatedInternalRule.params.ruleId,
|
||||
});
|
||||
}
|
||||
|
||||
return parseResult.data;
|
||||
};
|
||||
|
|
|
@ -6,16 +6,19 @@
|
|||
*/
|
||||
|
||||
import type { RulesClient } from '@kbn/alerting-plugin/server';
|
||||
import { stringifyZodError } from '@kbn/zod-helpers';
|
||||
import type { MlAuthz } from '../../../../../machine_learning/authz';
|
||||
import type { RuleAlertType, RuleParams } from '../../../../rule_schema';
|
||||
import type { RuleParams } from '../../../../rule_schema';
|
||||
import type { UpgradePrebuiltRuleArgs } from '../detection_rules_client_interface';
|
||||
import {
|
||||
convertPatchAPIToInternalSchema,
|
||||
convertCreateAPIToInternalSchema,
|
||||
internalRuleToAPIResponse,
|
||||
} from '../../../normalization/rule_converters';
|
||||
import { transformAlertToRuleAction } from '../../../../../../../common/detection_engine/transform_actions';
|
||||
import { RuleResponse } from '../../../../../../../common/api/detection_engine/model/rule_schema';
|
||||
|
||||
import { validateMlAuth, ClientError } from '../utils';
|
||||
import { validateMlAuth, ClientError, RuleResponseValidationError } from '../utils';
|
||||
|
||||
import { readRules } from '../read_rules';
|
||||
|
||||
|
@ -23,7 +26,7 @@ export const upgradePrebuiltRule = async (
|
|||
rulesClient: RulesClient,
|
||||
upgradePrebuiltRulePayload: UpgradePrebuiltRuleArgs,
|
||||
mlAuthz: MlAuthz
|
||||
): Promise<RuleAlertType> => {
|
||||
): Promise<RuleResponse> => {
|
||||
const { ruleAsset } = upgradePrebuiltRulePayload;
|
||||
|
||||
await validateMlAuth(mlAuthz, ruleAsset.type);
|
||||
|
@ -56,29 +59,41 @@ export const upgradePrebuiltRule = async (
|
|||
{ immutable: true, defaultEnabled: existingRule.enabled }
|
||||
);
|
||||
|
||||
return rulesClient.create<RuleParams>({
|
||||
const createdRule = await rulesClient.create<RuleParams>({
|
||||
data: internalRule,
|
||||
options: { id: existingRule.id },
|
||||
});
|
||||
|
||||
/* Trying to convert the rule to a RuleResponse object */
|
||||
const parseResult = RuleResponse.safeParse(internalRuleToAPIResponse(createdRule));
|
||||
|
||||
if (!parseResult.success) {
|
||||
throw new RuleResponseValidationError({
|
||||
message: stringifyZodError(parseResult.error),
|
||||
ruleId: createdRule.params.ruleId,
|
||||
});
|
||||
}
|
||||
|
||||
return parseResult.data;
|
||||
}
|
||||
|
||||
// Else, simply patch it.
|
||||
const patchedRule = convertPatchAPIToInternalSchema(ruleAsset, existingRule);
|
||||
|
||||
await rulesClient.update({
|
||||
const patchedInternalRule = await rulesClient.update({
|
||||
id: existingRule.id,
|
||||
data: patchedRule,
|
||||
});
|
||||
|
||||
const updatedRule = await readRules({
|
||||
rulesClient,
|
||||
ruleId: ruleAsset.rule_id,
|
||||
id: undefined,
|
||||
});
|
||||
/* Trying to convert the internal rule to a RuleResponse object */
|
||||
const parseResult = RuleResponse.safeParse(internalRuleToAPIResponse(patchedInternalRule));
|
||||
|
||||
if (!updatedRule) {
|
||||
throw new ClientError(`Rule ${ruleAsset.rule_id} not found after upgrade`, 500);
|
||||
if (!parseResult.success) {
|
||||
throw new RuleResponseValidationError({
|
||||
message: stringifyZodError(parseResult.error),
|
||||
ruleId: patchedInternalRule.params.ruleId,
|
||||
});
|
||||
}
|
||||
|
||||
return updatedRule;
|
||||
return parseResult.data;
|
||||
};
|
||||
|
|
|
@ -20,12 +20,18 @@ export const toggleRuleEnabledOnUpdate = async (
|
|||
rulesClient: RulesClient,
|
||||
existingRule: RuleAlertType,
|
||||
updatedRuleEnabled?: boolean
|
||||
) => {
|
||||
): Promise<{ enabled: boolean }> => {
|
||||
if (existingRule.enabled && updatedRuleEnabled === false) {
|
||||
await rulesClient.disable({ id: existingRule.id });
|
||||
} else if (!existingRule.enabled && updatedRuleEnabled === true) {
|
||||
await rulesClient.enable({ id: existingRule.id });
|
||||
return { enabled: false };
|
||||
}
|
||||
|
||||
if (!existingRule.enabled && updatedRuleEnabled === true) {
|
||||
await rulesClient.enable({ id: existingRule.id });
|
||||
return { enabled: true };
|
||||
}
|
||||
|
||||
return { enabled: existingRule.enabled };
|
||||
};
|
||||
|
||||
export const validateMlAuth = async (mlAuthz: MlAuthz, ruleType: Type) => {
|
||||
|
|
|
@ -6,28 +6,21 @@
|
|||
*/
|
||||
|
||||
import { getImportRulesSchemaMock } from '../../../../../../common/api/detection_engine/rule_management/mocks';
|
||||
import { getQueryRuleParams } from '../../../rule_schema/mocks';
|
||||
|
||||
import { getRulesSchemaMock } from '../../../../../../common/api/detection_engine/model/rule_schema/rule_response_schema.mock';
|
||||
import { requestContextMock } from '../../../routes/__mocks__';
|
||||
import { getRuleMock, getEmptyFindResult } from '../../../routes/__mocks__/request_responses';
|
||||
|
||||
import { importRules } from './import_rules_utils';
|
||||
import { createBulkErrorObject } from '../../../routes/utils';
|
||||
|
||||
describe('importRules', () => {
|
||||
const { clients, context } = requestContextMock.createTools();
|
||||
const importedRule = getRuleMock(getQueryRuleParams());
|
||||
const ruleToImport = getImportRulesSchemaMock();
|
||||
|
||||
beforeEach(() => {
|
||||
clients.rulesClient.find.mockResolvedValue(getEmptyFindResult());
|
||||
clients.rulesClient.update.mockResolvedValue(importedRule);
|
||||
clients.detectionRulesClient.importRule.mockResolvedValue(importedRule);
|
||||
clients.actionsClient.getAll.mockResolvedValue([]);
|
||||
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
it('returns rules response if no rules to import', async () => {
|
||||
it('returns an empty rules response if no rules to import', async () => {
|
||||
const result = await importRules({
|
||||
ruleChunks: [],
|
||||
rulesResponseAcc: [],
|
||||
|
@ -62,12 +55,13 @@ describe('importRules', () => {
|
|||
it('returns 409 error if DetectionRulesClient throws with 409 - existing rule', async () => {
|
||||
clients.detectionRulesClient.importRule.mockImplementationOnce(async () => {
|
||||
throw createBulkErrorObject({
|
||||
ruleId: importedRule.params.ruleId,
|
||||
ruleId: ruleToImport.rule_id,
|
||||
statusCode: 409,
|
||||
message: `rule_id: "${importedRule.params.ruleId}" already exists`,
|
||||
message: `rule_id: "${ruleToImport.rule_id}" already exists`,
|
||||
});
|
||||
});
|
||||
const ruleChunk = [getImportRulesSchemaMock({ rule_id: importedRule.params.ruleId })];
|
||||
|
||||
const ruleChunk = [ruleToImport];
|
||||
const result = await importRules({
|
||||
ruleChunks: [ruleChunk],
|
||||
rulesResponseAcc: [],
|
||||
|
@ -79,15 +73,21 @@ describe('importRules', () => {
|
|||
expect(result).toEqual([
|
||||
{
|
||||
error: {
|
||||
message: `rule_id: "${importedRule.params.ruleId}" already exists`,
|
||||
message: `rule_id: "${ruleToImport.rule_id}" already exists`,
|
||||
status_code: 409,
|
||||
},
|
||||
rule_id: importedRule.params.ruleId,
|
||||
rule_id: ruleToImport.rule_id,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('creates rule if no matching existing rule found', async () => {
|
||||
const ruleChunk = [getImportRulesSchemaMock({ rule_id: importedRule.params.ruleId })];
|
||||
clients.detectionRulesClient.importRule.mockResolvedValue({
|
||||
...getRulesSchemaMock(),
|
||||
rule_id: ruleToImport.rule_id,
|
||||
});
|
||||
|
||||
const ruleChunk = [ruleToImport];
|
||||
const result = await importRules({
|
||||
ruleChunks: [ruleChunk],
|
||||
rulesResponseAcc: [],
|
||||
|
@ -96,6 +96,6 @@ describe('importRules', () => {
|
|||
existingLists: {},
|
||||
});
|
||||
|
||||
expect(result).toEqual([{ rule_id: importedRule.params.ruleId, status_code: 200 }]);
|
||||
expect(result).toEqual([{ rule_id: ruleToImport.rule_id, status_code: 200 }]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -98,7 +98,7 @@ export const importRules = async ({
|
|||
});
|
||||
|
||||
resolve({
|
||||
rule_id: importedRule.params.ruleId,
|
||||
rule_id: importedRule.rule_id,
|
||||
status_code: 200,
|
||||
});
|
||||
} catch (err) {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import { transformValidate, transformValidateBulkError } from './validate';
|
||||
import { transformValidateBulkError } from './validate';
|
||||
import type { BulkError } from '../../routes/utils';
|
||||
import { getRuleMock } from '../../routes/__mocks__/request_responses';
|
||||
import { getListArrayMock } from '../../../../../common/detection_engine/schemas/types/lists.mock';
|
||||
|
@ -82,23 +82,6 @@ export const ruleOutput = (): RuleResponse => ({
|
|||
});
|
||||
|
||||
describe('validate', () => {
|
||||
describe('transformValidate', () => {
|
||||
test('it should do a validation correctly of a partial alert', () => {
|
||||
const ruleAlert = getRuleMock(getQueryRuleParams());
|
||||
const validated = transformValidate(ruleAlert);
|
||||
expect(validated).toEqual(ruleOutput());
|
||||
});
|
||||
|
||||
test('it should do an in-validation correctly of a partial alert', () => {
|
||||
const ruleAlert = getRuleMock(getQueryRuleParams());
|
||||
// @ts-expect-error
|
||||
delete ruleAlert.name;
|
||||
expect(() => {
|
||||
transformValidate(ruleAlert);
|
||||
}).toThrowError('Required');
|
||||
});
|
||||
});
|
||||
|
||||
describe('transformValidateBulkError', () => {
|
||||
test('it should do a validation correctly of a rule id', () => {
|
||||
const ruleAlert = getRuleMock(getQueryRuleParams());
|
||||
|
|
|
@ -31,14 +31,8 @@ import {
|
|||
type UnifiedQueryRuleParams,
|
||||
} from '../../rule_schema';
|
||||
import { type BulkError, createBulkErrorObject } from '../../routes/utils';
|
||||
import { transform } from './utils';
|
||||
import { internalRuleToAPIResponse } from '../normalization/rule_converters';
|
||||
|
||||
export const transformValidate = (rule: PartialRule<RuleParams>): RuleResponse => {
|
||||
const transformed = transform(rule);
|
||||
return RuleResponse.parse(transformed);
|
||||
};
|
||||
|
||||
export const transformValidateBulkError = (
|
||||
ruleId: string,
|
||||
rule: PartialRule<RuleParams>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue