Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,6 @@ export const transformUpdateRuleBody: RewriteResponseCase<UpdateRuleBody> = ({
...(uuid && { uuid }),
};
}),
...(alertDelay ? { alert_delay: alertDelay } : {}),
...(alertDelay !== undefined ? { alert_delay: alertDelay } : {}),
...(flapping !== undefined ? { flapping: transformUpdateRuleFlapping(flapping) } : {}),
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@
*/

// Feature flag for frontend rule specific flapping in rule flyout
export const IS_RULE_SPECIFIC_FLAPPING_ENABLED = false;
export const IS_RULE_SPECIFIC_FLAPPING_ENABLED = true;
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
import { useMutation } from '@tanstack/react-query';
import type { HttpStart, IHttpFetchError } from '@kbn/core-http-browser';
import { createRule, CreateRuleBody } from '../apis/create_rule';
import { Rule } from '../types';

export interface UseCreateRuleProps {
http: HttpStart;
onSuccess?: (formData: CreateRuleBody) => void;
onSuccess?: (rule: Rule) => void;
onError?: (error: IHttpFetchError<{ message: string }>) => void;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ export interface UseLoadConnectorsProps {
http: HttpStart;
includeSystemActions?: boolean;
enabled?: boolean;
cacheTime?: number;
}

export const useLoadConnectors = (props: UseLoadConnectorsProps) => {
const { http, includeSystemActions = false, enabled = true } = props;
const { http, includeSystemActions = false, enabled = true, cacheTime } = props;

const queryFn = () => {
return fetchConnectors({ http, includeSystemActions });
Expand All @@ -27,6 +28,7 @@ export const useLoadConnectors = (props: UseLoadConnectorsProps) => {
const { data, isLoading, isFetching, isInitialLoading } = useQuery({
queryKey: ['useLoadConnectors', includeSystemActions],
queryFn,
cacheTime,
refetchOnWindowFocus: false,
enabled,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ export interface UseLoadRuleTypeAadTemplateFieldProps {
http: HttpStart;
ruleTypeId?: string;
enabled: boolean;
cacheTime?: number;
}

export const useLoadRuleTypeAadTemplateField = (props: UseLoadRuleTypeAadTemplateFieldProps) => {
const { http, ruleTypeId, enabled } = props;
const { http, ruleTypeId, enabled, cacheTime } = props;

const queryFn = () => {
if (!ruleTypeId) {
Expand All @@ -43,6 +44,7 @@ export const useLoadRuleTypeAadTemplateField = (props: UseLoadRuleTypeAadTemplat
description: getDescription(d.name, EcsFlat),
}));
},
cacheTime,
refetchOnWindowFocus: false,
enabled,
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import { RuleFormData } from '../../rule_form';
export interface UseResolveProps {
http: HttpStart;
id?: string;
cacheTime?: number;
}

export const useResolveRule = (props: UseResolveProps) => {
const { id, http } = props;
const { id, http, cacheTime } = props;

const queryFn = () => {
if (id) {
Expand All @@ -30,6 +31,7 @@ export const useResolveRule = (props: UseResolveProps) => {
queryKey: ['useResolveRule', id],
queryFn,
enabled: !!id,
cacheTime,
select: (rule): RuleFormData | null => {
if (!rule) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@
import { useMutation } from '@tanstack/react-query';
import type { HttpStart, IHttpFetchError } from '@kbn/core-http-browser';
import { updateRule, UpdateRuleBody } from '../apis/update_rule';
import { Rule } from '../types';

export interface UseUpdateRuleProps {
http: HttpStart;
onSuccess?: (formData: UpdateRuleBody) => void;
onSuccess?: (rule: Rule) => void;
onError?: (error: IHttpFetchError<{ message: string }>) => void;
}

Expand Down
2 changes: 0 additions & 2 deletions packages/kbn-alerts-ui-shared/src/common/types/rule_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ import { TypeRegistry } from '../type_registry';

export type { SanitizedRuleAction as RuleAction } from '@kbn/alerting-types';

export type { Flapping } from '@kbn/alerting-types';

export type RuleTypeWithDescription = RuleType<string, string> & { description?: string };

export type RuleTypeIndexWithDescriptions = Map<string, RuleTypeWithDescription>;
Expand Down
3 changes: 2 additions & 1 deletion packages/kbn-alerts-ui-shared/src/rule_form/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const DEFAULT_FREQUENCY = {
summary: false,
};

export const GET_DEFAULT_FORM_DATA = ({
export const getDefaultFormData = ({
ruleTypeId,
name,
consumer,
Expand All @@ -50,6 +50,7 @@ export const GET_DEFAULT_FORM_DATA = ({
ruleTypeId,
name,
actions,
alertDelay: { active: 1 },
};
};

Expand Down
22 changes: 16 additions & 6 deletions packages/kbn-alerts-ui-shared/src/rule_form/create_rule_form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { EuiLoadingElastic } from '@elastic/eui';
import { toMountPoint } from '@kbn/react-kibana-mount';
import { type RuleCreationValidConsumer } from '@kbn/rule-data-utils';
import type { RuleFormData, RuleFormPlugins } from './types';
import { DEFAULT_VALID_CONSUMERS, GET_DEFAULT_FORM_DATA } from './constants';
import { DEFAULT_VALID_CONSUMERS, getDefaultFormData } from './constants';
import { RuleFormStateProvider } from './rule_form_state';
import { useCreateRule } from '../common/hooks';
import { RulePage } from './rule_page';
Expand All @@ -24,6 +24,7 @@ import {
} from './rule_form_errors';
import { useLoadDependencies } from './hooks/use_load_dependencies';
import {
getAvailableRuleTypes,
getInitialConsumer,
getInitialMultiConsumer,
getInitialSchedule,
Expand All @@ -42,7 +43,8 @@ export interface CreateRuleFormProps {
shouldUseRuleProducer?: boolean;
canShowConsumerSelection?: boolean;
showMustacheAutocompleteSwitch?: boolean;
returnUrl: string;
onCancel?: () => void;
onSubmit?: (ruleId: string) => void;
}

export const CreateRuleForm = (props: CreateRuleFormProps) => {
Expand All @@ -56,16 +58,18 @@ export const CreateRuleForm = (props: CreateRuleFormProps) => {
shouldUseRuleProducer = false,
canShowConsumerSelection = true,
showMustacheAutocompleteSwitch = false,
returnUrl,
onCancel,
onSubmit,
} = props;

const { http, docLinks, notifications, ruleTypeRegistry, i18n, theme } = plugins;
const { toasts } = notifications;

const { mutate, isLoading: isSaving } = useCreateRule({
http,
onSuccess: ({ name }) => {
onSuccess: ({ name, id }) => {
toasts.addSuccess(RULE_CREATE_SUCCESS_TEXT(name));
onSubmit?.(id);
},
onError: (error) => {
const message = parseRuleCircuitBreakerErrorMessage(
Expand All @@ -86,6 +90,7 @@ export const CreateRuleForm = (props: CreateRuleFormProps) => {
const {
isInitialLoading,
ruleType,
ruleTypes,
ruleTypeModel,
uiConfig,
healthCheckError,
Expand Down Expand Up @@ -153,7 +158,7 @@ export const CreateRuleForm = (props: CreateRuleFormProps) => {
<div data-test-subj="createRuleForm">
<RuleFormStateProvider
initialRuleFormState={{
formData: GET_DEFAULT_FORM_DATA({
formData: getDefaultFormData({
ruleTypeId,
name: `${ruleType.name} rule`,
consumer: getInitialConsumer({
Expand All @@ -174,6 +179,11 @@ export const CreateRuleForm = (props: CreateRuleFormProps) => {
minimumScheduleInterval: uiConfig?.minimumScheduleInterval,
selectedRuleTypeModel: ruleTypeModel,
selectedRuleType: ruleType,
availableRuleTypes: getAvailableRuleTypes({
consumer,
ruleTypes,
ruleTypeRegistry,
}).map(({ ruleType: rt }) => rt),
validConsumers,
flappingSettings,
canShowConsumerSelection,
Expand All @@ -185,7 +195,7 @@ export const CreateRuleForm = (props: CreateRuleFormProps) => {
}),
}}
>
<RulePage isEdit={false} isSaving={isSaving} returnUrl={returnUrl} onSave={onSave} />
<RulePage isEdit={false} isSaving={isSaving} onCancel={onCancel} onSave={onSave} />
</RuleFormStateProvider>
</div>
);
Expand Down
28 changes: 23 additions & 5 deletions packages/kbn-alerts-ui-shared/src/rule_form/edit_rule_form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,27 @@ import {
RuleFormRuleTypeError,
} from './rule_form_errors';
import { RULE_EDIT_ERROR_TEXT, RULE_EDIT_SUCCESS_TEXT } from './translations';
import { parseRuleCircuitBreakerErrorMessage } from './utils';
import { getAvailableRuleTypes, parseRuleCircuitBreakerErrorMessage } from './utils';
import { DEFAULT_VALID_CONSUMERS, getDefaultFormData } from './constants';

export interface EditRuleFormProps {
id: string;
plugins: RuleFormPlugins;
showMustacheAutocompleteSwitch?: boolean;
returnUrl: string;
onCancel?: () => void;
onSubmit?: (ruleId: string) => void;
}

export const EditRuleForm = (props: EditRuleFormProps) => {
const { id, plugins, returnUrl, showMustacheAutocompleteSwitch = false } = props;
const { id, plugins, showMustacheAutocompleteSwitch = false, onCancel, onSubmit } = props;
const { http, notifications, docLinks, ruleTypeRegistry, i18n, theme, application } = plugins;
const { toasts } = notifications;

const { mutate, isLoading: isSaving } = useUpdateRule({
http,
onSuccess: ({ name }) => {
toasts.addSuccess(RULE_EDIT_SUCCESS_TEXT(name));
onSubmit?.(id);
},
onError: (error) => {
const message = parseRuleCircuitBreakerErrorMessage(
Expand All @@ -62,6 +65,7 @@ export const EditRuleForm = (props: EditRuleFormProps) => {
const {
isInitialLoading,
ruleType,
ruleTypes,
ruleTypeModel,
uiConfig,
healthCheckError,
Expand Down Expand Up @@ -156,17 +160,31 @@ export const EditRuleForm = (props: EditRuleFormProps) => {
connectors,
connectorTypes,
aadTemplateFields,
formData: fetchedFormData,
formData: {
...getDefaultFormData({
ruleTypeId: fetchedFormData.ruleTypeId,
name: fetchedFormData.name,
consumer: fetchedFormData.consumer,
actions: fetchedFormData.actions,
}),
...fetchedFormData,
},
id,
plugins,
minimumScheduleInterval: uiConfig?.minimumScheduleInterval,
selectedRuleType: ruleType,
selectedRuleTypeModel: ruleTypeModel,
availableRuleTypes: getAvailableRuleTypes({
consumer: fetchedFormData.consumer,
ruleTypes,
ruleTypeRegistry,
}).map(({ ruleType: rt }) => rt),
flappingSettings,
validConsumers: DEFAULT_VALID_CONSUMERS,
showMustacheAutocompleteSwitch,
}}
>
<RulePage isEdit={true} isSaving={isSaving} returnUrl={returnUrl} onSave={onSave} />
<RulePage isEdit={true} isSaving={isSaving} onSave={onSave} onCancel={onCancel} />
</RuleFormStateProvider>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,6 @@ jest.mock('../../common/hooks/use_load_rule_type_aad_template_fields', () => ({
useLoadRuleTypeAadTemplateField: jest.fn(),
}));

jest.mock('../utils/get_authorized_rule_types', () => ({
getAvailableRuleTypes: jest.fn(),
}));

jest.mock('../../common/hooks/use_fetch_flapping_settings', () => ({
useFetchFlappingSettings: jest.fn(),
}));
Expand All @@ -63,7 +59,6 @@ const { useLoadRuleTypeAadTemplateField } = jest.requireMock(
'../../common/hooks/use_load_rule_type_aad_template_fields'
);
const { useLoadRuleTypesQuery } = jest.requireMock('../../common/hooks/use_load_rule_types_query');
const { getAvailableRuleTypes } = jest.requireMock('../utils/get_authorized_rule_types');
const { useFetchFlappingSettings } = jest.requireMock(
'../../common/hooks/use_fetch_flapping_settings'
);
Expand Down Expand Up @@ -168,13 +163,6 @@ useLoadRuleTypesQuery.mockReturnValue({
},
});

getAvailableRuleTypes.mockReturnValue([
{
ruleType: indexThresholdRuleType,
ruleTypeModel: indexThresholdRuleTypeModel,
},
]);

const mockConnector = {
id: 'test-connector',
name: 'Test',
Expand Down Expand Up @@ -236,7 +224,7 @@ const toastsMock = jest.fn();
const ruleTypeRegistryMock: RuleTypeRegistryContract = {
has: jest.fn(),
register: jest.fn(),
get: jest.fn(),
get: jest.fn().mockReturnValue(indexThresholdRuleTypeModel),
list: jest.fn(),
};

Expand Down Expand Up @@ -272,6 +260,7 @@ describe('useLoadDependencies', () => {
isLoading: false,
isInitialLoading: false,
ruleType: indexThresholdRuleType,
ruleTypes: [...ruleTypeIndex.values()],
ruleTypeModel: indexThresholdRuleTypeModel,
uiConfig: uiConfigMock,
healthCheckError: null,
Expand Down Expand Up @@ -317,39 +306,6 @@ describe('useLoadDependencies', () => {
});
});

test('should call getAvailableRuleTypes with the correct params', async () => {
const { result } = renderHook(
() => {
return useLoadDependencies({
http: httpMock as unknown as HttpStart,
toasts: toastsMock as unknown as ToastsStart,
ruleTypeRegistry: ruleTypeRegistryMock,
validConsumers: ['stackAlerts', 'logs'],
consumer: 'logs',
capabilities: {
actions: {
show: true,
save: true,
execute: true,
},
} as unknown as ApplicationStart['capabilities'],
});
},
{ wrapper }
);

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

expect(getAvailableRuleTypes).toBeCalledWith({
consumer: 'logs',
ruleTypeRegistry: ruleTypeRegistryMock,
ruleTypes: [indexThresholdRuleType],
validConsumers: ['stackAlerts', 'logs'],
});
});

test('should call resolve rule with the correct params', async () => {
const { result } = renderHook(
() => {
Expand Down Expand Up @@ -377,6 +333,7 @@ describe('useLoadDependencies', () => {
expect(useResolveRule).toBeCalledWith({
http: httpMock,
id: 'test-rule-id',
cacheTime: 0,
});
});

Expand Down
Loading