Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Add rule specific flapping to new rule form page #2

2 changes: 1 addition & 1 deletion packages/kbn-alerting-types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ export * from './r_rule_types';
export * from './rule_notify_when_type';
export * from './rule_type_types';
export * from './rule_types';
export * from './rule_flapping';
export * from './rule_settings';
export * from './search_strategy_types';
51 changes: 51 additions & 0 deletions packages/kbn-alerting-types/rule_settings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export interface RulesSettingsModificationMetadata {
createdBy: string | null;
updatedBy: string | null;
createdAt: string;
updatedAt: string;
}

export interface RulesSettingsFlappingProperties {
enabled: boolean;
lookBackWindow: number;
statusChangeThreshold: number;
}

export interface RuleSpecificFlappingProperties {
lookBackWindow: number;
statusChangeThreshold: number;
}

export type RulesSettingsFlapping = RulesSettingsFlappingProperties &
RulesSettingsModificationMetadata;

export interface RulesSettingsQueryDelayProperties {
delay: number;
}

export type RulesSettingsQueryDelay = RulesSettingsQueryDelayProperties &
RulesSettingsModificationMetadata;

export interface RulesSettingsProperties {
flapping?: RulesSettingsFlappingProperties;
queryDelay?: RulesSettingsQueryDelayProperties;
}

export interface RulesSettings {
flapping?: RulesSettingsFlapping;
queryDelay?: RulesSettingsQueryDelay;
}

export const MIN_LOOK_BACK_WINDOW = 2;
export const MAX_LOOK_BACK_WINDOW = 20;
export const MIN_STATUS_CHANGE_THRESHOLD = 2;
export const MAX_STATUS_CHANGE_THRESHOLD = 20;
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { httpServiceMock } from '@kbn/core/public/mocks';
import { fetchFlappingSettings } from './fetch_flapping_settings';

const http = httpServiceMock.createStartContract();

describe('fetchFlappingSettings', () => {
beforeEach(() => {
jest.resetAllMocks();
});

test('should call fetch rule flapping API', async () => {
const now = new Date().toISOString();
http.get.mockResolvedValue({
created_by: 'test',
updated_by: 'test',
created_at: now,
updated_at: now,
enabled: true,
look_back_window: 20,
status_change_threshold: 20,
});

const result = await fetchFlappingSettings({ http });

expect(result).toEqual({
createdBy: 'test',
updatedBy: 'test',
createdAt: now,
updatedAt: now,
enabled: true,
lookBackWindow: 20,
statusChangeThreshold: 20,
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { HttpSetup } from '@kbn/core/public';
import { AsApiContract } from '@kbn/actions-types';
import { RulesSettingsFlapping } from '@kbn/alerting-types';
import { INTERNAL_BASE_ALERTING_API_PATH } from '../../constants';
import { transformFlappingSettingsResponse } from './transform_flapping_settings_response';

export const fetchFlappingSettings = async ({ http }: { http: HttpSetup }) => {
const res = await http.get<AsApiContract<RulesSettingsFlapping>>(
`${INTERNAL_BASE_ALERTING_API_PATH}/rules/settings/_flapping`
);
return transformFlappingSettingsResponse(res);
};
Original file line number Diff line number Diff line change
@@ -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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export * from './fetch_flapping_settings';
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { transformFlappingSettingsResponse } from './transform_flapping_settings_response';

describe('transformFlappingSettingsResponse', () => {
test('should transform flapping settings response', () => {
const now = new Date().toISOString();

const result = transformFlappingSettingsResponse({
created_by: 'test',
updated_by: 'test',
created_at: now,
updated_at: now,
enabled: true,
look_back_window: 20,
status_change_threshold: 20,
});

expect(result).toEqual({
createdBy: 'test',
updatedBy: 'test',
createdAt: now,
updatedAt: now,
enabled: true,
lookBackWindow: 20,
statusChangeThreshold: 20,
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { AsApiContract } from '@kbn/actions-types';
import { RulesSettingsFlapping } from '@kbn/alerting-types';

export const transformFlappingSettingsResponse = ({
look_back_window: lookBackWindow,
status_change_threshold: statusChangeThreshold,
created_at: createdAt,
created_by: createdBy,
updated_at: updatedAt,
updated_by: updatedBy,
...rest
}: AsApiContract<RulesSettingsFlapping>): RulesSettingsFlapping => ({
...rest,
lookBackWindow,
statusChangeThreshold,
createdAt,
createdBy,
updatedAt,
updatedBy,
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,5 @@
* License v3.0 only", or the "Server Side Public License, v 1".
*/

export const MIN_LOOK_BACK_WINDOW = 2;
export const MAX_LOOK_BACK_WINDOW = 20;
export const MIN_STATUS_CHANGE_THRESHOLD = 2;
export const MAX_STATUS_CHANGE_THRESHOLD = 20;
// Feature flag for frontend rule specific flapping in rule flyout
export const IS_RULE_SPECIFIC_FLAPPING_ENABLED = false;
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import React, { FunctionComponent } from 'react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { renderHook } from '@testing-library/react-hooks';
import { testQueryClientConfig } from '../test_utils/test_query_client_config';
import { useFetchFlappingSettings } from './use_fetch_flapping_settings';
import { httpServiceMock } from '@kbn/core-http-browser-mocks';

const queryClient = new QueryClient(testQueryClientConfig);

const wrapper: FunctionComponent<React.PropsWithChildren<{}>> = ({ children }) => (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);

const http = httpServiceMock.createStartContract();

const now = new Date().toISOString();

describe('useFetchFlappingSettings', () => {
beforeEach(() => {
http.get.mockResolvedValue({
created_by: 'test',
updated_by: 'test',
created_at: now,
updated_at: now,
enabled: true,
look_back_window: 20,
status_change_threshold: 20,
});
});

afterEach(() => {
jest.resetAllMocks();
queryClient.clear();
});

test('should call fetchFlappingSettings with the correct parameters', async () => {
const { result, waitFor } = renderHook(
() => useFetchFlappingSettings({ http, enabled: true }),
{
wrapper,
}
);

await waitFor(() => {
return expect(result.current.isInitialLoading).toEqual(false);
});

expect(result.current.data).toEqual({
createdAt: now,
createdBy: 'test',
updatedAt: now,
updatedBy: 'test',
enabled: true,
lookBackWindow: 20,
statusChangeThreshold: 20,
});
});

test('should not call fetchFlappingSettings if enabled is false', async () => {
const { result, waitFor } = renderHook(
() => useFetchFlappingSettings({ http, enabled: false }),
{
wrapper,
}
);

await waitFor(() => {
return expect(result.current.isInitialLoading).toEqual(false);
});

expect(http.get).not.toHaveBeenCalled();
});

test('should call onSuccess when the fetching was successful', async () => {
const onSuccessMock = jest.fn();
const { result, waitFor } = renderHook(
() => useFetchFlappingSettings({ http, enabled: true, onSuccess: onSuccessMock }),
{
wrapper,
}
);

await waitFor(() => {
return expect(result.current.isInitialLoading).toEqual(false);
});

expect(onSuccessMock).toHaveBeenCalledWith({
createdAt: now,
createdBy: 'test',
updatedAt: now,
updatedBy: 'test',
enabled: true,
lookBackWindow: 20,
statusChangeThreshold: 20,
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* 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", the "GNU Affero General Public License v3.0 only", 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", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

import { useQuery } from '@tanstack/react-query';
import { HttpStart } from '@kbn/core-http-browser';
import { RulesSettingsFlapping } from '@kbn/alerting-types/rule_settings';
import { fetchFlappingSettings } from '../apis/fetch_flapping_settings';

interface UseFetchFlappingSettingsProps {
http: HttpStart;
enabled: boolean;
onSuccess?: (settings: RulesSettingsFlapping) => void;
}

export const useFetchFlappingSettings = (props: UseFetchFlappingSettingsProps) => {
const { http, enabled, onSuccess } = props;

const queryFn = () => {
return fetchFlappingSettings({ http });
};

const { data, isFetching, isError, isLoadingError, isLoading, isInitialLoading } = useQuery({
queryKey: ['fetchFlappingSettings'],
queryFn,
onSuccess,
enabled,
refetchOnWindowFocus: false,
retry: false,
});

return {
isInitialLoading,
isLoading: isLoading || isFetching,
isError: isError || isLoadingError,
data,
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ export const CreateRuleForm = (props: CreateRuleFormProps) => {
connectors,
connectorTypes,
aadTemplateFields,
flappingSettings,
} = useLoadDependencies({
http,
toasts: notifications.toasts,
Expand All @@ -117,6 +118,7 @@ export const CreateRuleForm = (props: CreateRuleFormProps) => {
actions: newFormData.actions,
notifyWhen: newFormData.notifyWhen,
alertDelay: newFormData.alertDelay,
flapping: newFormData.flapping,
},
});
},
Expand Down Expand Up @@ -173,6 +175,7 @@ export const CreateRuleForm = (props: CreateRuleFormProps) => {
selectedRuleTypeModel: ruleTypeModel,
selectedRuleType: ruleType,
validConsumers,
flappingSettings,
canShowConsumerSelection,
showMustacheAutocompleteSwitch,
multiConsumerSelection: getInitialMultiConsumer({
Expand Down
Loading