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

[Actionable Observability] Add rule details page #130330

Merged
merged 73 commits into from
May 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
63f17af
Add rule details page
fkanout Apr 14, 2022
d060c21
Fix route
fkanout Apr 14, 2022
916f1eb
Fix route
fkanout Apr 14, 2022
92d674c
Add useBreadcrumbs
fkanout Apr 14, 2022
a0a1143
Add rule summary
fkanout Apr 15, 2022
7d13f2c
Complete rule data summary
fkanout Apr 20, 2022
4869e1b
Update styling
fkanout Apr 20, 2022
7aa6278
Add update rule
fkanout Apr 20, 2022
d91d20b
Add edit role
fkanout Apr 20, 2022
7543fb5
Update desgin
fkanout Apr 21, 2022
e448c45
Add conditions
fkanout Apr 21, 2022
3c64051
Add connectors icons
fkanout Apr 21, 2022
ee62107
Fix more button
fkanout Apr 25, 2022
ef50ff3
Remove unused FelxBox
fkanout Apr 25, 2022
b25ab69
Add fetch alerts
fkanout Apr 25, 2022
75b0163
Move to items to components folder
fkanout Apr 25, 2022
02f7ce9
Format dates
fkanout Apr 25, 2022
d9c8d97
Add tabs
fkanout Apr 25, 2022
8847e6c
Merge branch 'main' into rule-detail-page
fkanout Apr 25, 2022
f890c77
Merge branch 'main' into rule-detail-page
kibanamachine Apr 25, 2022
049cf3e
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Apr 25, 2022
715c3ac
Use the shared getRuleStatusDropdown
fkanout Apr 25, 2022
c00544c
Add permissions
fkanout Apr 26, 2022
c154c71
Better handling errors
fkanout Apr 27, 2022
1c91182
Fix styling
fkanout Apr 27, 2022
91c67d2
Fix tag style
fkanout Apr 27, 2022
17e3de9
Add tags component
fkanout Apr 27, 2022
6e3f7de
Merge branch 'main' into rule-detail-page
kibanamachine Apr 28, 2022
aafe700
Use tags component from triggers aciton ui
fkanout Apr 29, 2022
dfc9b74
Add last24hAlerts hook
fkanout Apr 29, 2022
f4ee490
Merge branch 'main' into rule-detail-page
fkanout Apr 29, 2022
b56f59d
Merge branch 'main' into rule-detail-page
fkanout May 10, 2022
28404e4
Merge branch 'main' into rule-detail-page
kibanamachine May 10, 2022
0171d6e
Fix last 24h alerts count hook
fkanout May 10, 2022
8a63dd5
Fix large font
fkanout May 10, 2022
18684ed
Fix font size
fkanout May 10, 2022
f59d13b
Fix size Actions
fkanout May 10, 2022
367670f
Fix fontsize page header
fkanout May 10, 2022
4cf5873
Fix conditions size
fkanout May 10, 2022
b3d83ee
Fix text move vertically on small screen
fkanout May 10, 2022
35159ae
Update style
fkanout May 10, 2022
03b1a08
Update alerts counts style
fkanout May 10, 2022
6b4545a
Cleanup
fkanout May 11, 2022
8f4ee12
Add formatter for the interval
fkanout May 11, 2022
9c82aa1
Add edit button on the definition section
fkanout May 11, 2022
dc40930
Add delete modal
fkanout May 11, 2022
f41ca35
Add loader
fkanout May 12, 2022
cad2366
Fix conditions panctuation
fkanout May 12, 2022
8df5101
Fix size
fkanout May 12, 2022
31711de
Use the healthColor function from rule component
fkanout May 12, 2022
17f762e
Add loading while deleting a rule
fkanout May 12, 2022
5902eae
Use connectors name to show actions
fkanout May 12, 2022
79a21f7
Fix type
fkanout May 12, 2022
9ddb636
Fix rule page
fkanout May 12, 2022
43a0b2d
Merge branch 'main' into rule-detail-page
kibanamachine May 12, 2022
2be30ff
Fix types
fkanout May 12, 2022
35dec6c
Use common RULES_PAGE_LINK var
fkanout May 12, 2022
488279d
Fix checks
fkanout May 12, 2022
e4cc2e2
Better error handling
fkanout May 12, 2022
954e4d5
Better i18n
fkanout May 12, 2022
0913468
Code review
fkanout May 12, 2022
7ec2adc
Fix checks i18n
fkanout May 12, 2022
4a5c57a
Use abort signal
fkanout May 12, 2022
a9e9704
Merge branch 'main' into rule-detail-page
fkanout May 12, 2022
e8fa815
Revert signal for loadRule as there is no tests
fkanout May 12, 2022
888721b
Fix style
fkanout May 12, 2022
8391aaf
Fixing tests
fkanout May 16, 2022
080b723
Merge branch 'main' into rule-detail-page
kibanamachine May 16, 2022
23259b2
Reduce bundle size
fkanout May 16, 2022
f10c901
Fix i18n
fkanout May 16, 2022
4c7ddb3
Bump limits
fkanout May 16, 2022
32b3c6e
Bump limit
fkanout May 16, 2022
471a73f
Add tl;dr limit comment
fkanout May 16, 2022
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
2 changes: 1 addition & 1 deletion packages/kbn-optimizer/limits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ pageLoadAssetSize:
telemetry: 51957
telemetryManagementSection: 38586
transform: 41007
triggersActionsUi: 104400
triggersActionsUi: 105800 #This is temporary. Check https://github.com/elastic/kibana/pull/130710#issuecomment-1119843458 & https://github.com/elastic/kibana/issues/130728
upgradeAssistant: 81241
urlForwarding: 32579
usageCollection: 39762
Expand Down
159 changes: 159 additions & 0 deletions x-pack/plugins/observability/public/hooks/use_fetch_last24h_alerts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useEffect, useState, useCallback, useRef } from 'react';
import { AsApiContract } from '@kbn/actions-plugin/common';
import { HttpSetup } from '@kbn/core/public';
import { BASE_RAC_ALERTS_API_PATH } from '@kbn/rule-registry-plugin/common/constants';
import { RULE_LOAD_ERROR } from '../pages/rule_details/translations';

interface UseFetchLast24hAlertsProps {
http: HttpSetup;
features: string;
ruleId: string;
}
interface FetchLast24hAlerts {
isLoadingLast24hAlerts: boolean;
last24hAlerts: number;
errorLast24hAlerts: string | undefined;
}

export function useFetchLast24hAlerts({ http, features, ruleId }: UseFetchLast24hAlertsProps) {
fkanout marked this conversation as resolved.
Show resolved Hide resolved
const [last24hAlerts, setLast24hAlerts] = useState<FetchLast24hAlerts>({
isLoadingLast24hAlerts: true,
last24hAlerts: 0,
errorLast24hAlerts: undefined,
});
const isCancelledRef = useRef(false);
const abortCtrlRef = useRef(new AbortController());
const fetchLast24hAlerts = useCallback(async () => {
isCancelledRef.current = false;
abortCtrlRef.current.abort();
abortCtrlRef.current = new AbortController();
try {
if (!features) return;
const { index } = await fetchIndexNameAPI({
http,
features,
});
const { error, alertsCount } = await fetchLast24hAlertsAPI({
http,
index,
ruleId,
signal: abortCtrlRef.current.signal,
});
if (error) throw error;
if (!isCancelledRef.current) {
setLast24hAlerts((oldState: FetchLast24hAlerts) => ({
...oldState,
last24hAlerts: alertsCount,
isLoading: false,
}));
}
} catch (error) {
if (!isCancelledRef.current) {
if (error.name !== 'AbortError') {
setLast24hAlerts((oldState: FetchLast24hAlerts) => ({
...oldState,
isLoading: false,
errorLast24hAlerts: RULE_LOAD_ERROR(
error instanceof Error ? error.message : typeof error === 'string' ? error : ''
),
}));
}
}
}
}, [http, features, ruleId]);
useEffect(() => {
fetchLast24hAlerts();
}, [fetchLast24hAlerts]);

return last24hAlerts;
}

interface IndexName {
index: string;
}

export async function fetchIndexNameAPI({
http,
features,
}: {
http: HttpSetup;
features: string;
}): Promise<IndexName> {
const res = await http.get<{ index_name: string[] }>(`${BASE_RAC_ALERTS_API_PATH}/index`, {
query: { features },
});
return {
index: res.index_name[0],
};
}
export async function fetchLast24hAlertsAPI({
http,
index,
ruleId,
signal,
}: {
http: HttpSetup;
index: string;
ruleId: string;
signal: AbortSignal;
}): Promise<{
error: string | null;
alertsCount: number;
}> {
try {
const res = await http.post<AsApiContract<any>>(`${BASE_RAC_ALERTS_API_PATH}/find`, {
signal,
body: JSON.stringify({
index,
query: {
bool: {
must: [
{
term: {
'kibana.alert.rule.uuid': ruleId,
},
},
{
range: {
'@timestamp': {
gte: 'now-24h',
lt: 'now',
},
},
},
],
},
},
aggs: {
alerts_count: {
cardinality: {
field: 'kibana.alert.uuid',
},
},
},
}),
});
return {
error: null,
alertsCount: res.aggregations.alerts_count.value,
};
} catch (error) {
return {
error,
alertsCount: 0,
};
}
}
46 changes: 46 additions & 0 deletions x-pack/plugins/observability/public/hooks/use_fetch_rule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useEffect, useState, useCallback } from 'react';
import { loadRule } from '@kbn/triggers-actions-ui-plugin/public';
import { FetchRuleProps, FetchRule } from '../pages/rule_details/types';
import { RULE_LOAD_ERROR } from '../pages/rule_details/translations';

export function useFetchRule({ ruleId, http }: FetchRuleProps) {
const [ruleSummary, setRuleSummary] = useState<FetchRule>({
isRuleLoading: true,
rule: undefined,
errorRule: undefined,
});
const fetchRuleSummary = useCallback(async () => {
try {
const rule = await loadRule({
http,
ruleId,
});

setRuleSummary((oldState: FetchRule) => ({
...oldState,
fkanout marked this conversation as resolved.
Show resolved Hide resolved
isRuleLoading: false,
rule,
}));
} catch (error) {
setRuleSummary((oldState: FetchRule) => ({
...oldState,
fkanout marked this conversation as resolved.
Show resolved Hide resolved
isRuleLoading: false,
errorRule: RULE_LOAD_ERROR(
error instanceof Error ? error.message : typeof error === 'string' ? error : ''
),
}));
}
}, [ruleId, http]);
useEffect(() => {
fetchRuleSummary();
}, [fetchRuleSummary]);

return { ...ruleSummary, reloadRule: fetchRuleSummary };
}
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; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useEffect, useState, useCallback } from 'react';
import { ActionConnector, loadAllActions } from '@kbn/triggers-actions-ui-plugin/public';
import { FetchRuleActionsProps } from '../pages/rule_details/types';
import { ACTIONS_LOAD_ERROR } from '../pages/rule_details/translations';

interface FetchActions {
isLoadingActions: boolean;
allActions: Array<ActionConnector<Record<string, unknown>>>;
errorActions: string | undefined;
}

export function useFetchRuleActions({ http }: FetchRuleActionsProps) {
const [ruleActions, setRuleActions] = useState<FetchActions>({
isLoadingActions: true,
allActions: [] as Array<ActionConnector<Record<string, unknown>>>,
errorActions: undefined,
});

const fetchRuleActions = useCallback(async () => {
try {
const response = await loadAllActions({
http,
});
setRuleActions((oldState: FetchActions) => ({
...oldState,
isLoadingActions: false,
allActions: response,
}));
} catch (error) {
setRuleActions((oldState: FetchActions) => ({
...oldState,
isLoadingActions: false,
errorActions: ACTIONS_LOAD_ERROR(
error instanceof Error ? error.message : typeof error === 'string' ? error : ''
),
}));
}
}, [http]);
useEffect(() => {
fetchRuleActions();
}, [fetchRuleActions]);

return { ...ruleActions, reloadRuleActions: fetchRuleActions };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { useEffect, useState, useCallback } from 'react';
import { loadRuleSummary } from '@kbn/triggers-actions-ui-plugin/public';
import { FetchRuleSummaryProps, FetchRuleSummary } from '../pages/rule_details/types';
import { RULE_LOAD_ERROR } from '../pages/rule_details/translations';

export function useFetchRuleSummary({ ruleId, http }: FetchRuleSummaryProps) {
const [ruleSummary, setRuleSummary] = useState<FetchRuleSummary>({
isLoadingRuleSummary: true,
ruleSummary: undefined,
errorRuleSummary: undefined,
});

const fetchRuleSummary = useCallback(async () => {
setRuleSummary((oldState: FetchRuleSummary) => ({ ...oldState, isLoading: true }));

try {
const response = await loadRuleSummary({
http,
ruleId,
});
setRuleSummary((oldState: FetchRuleSummary) => ({
...oldState,
isLoading: false,
ruleSummary: response,
}));
} catch (error) {
setRuleSummary((oldState: FetchRuleSummary) => ({
...oldState,
isLoading: false,
errorRuleSummary: RULE_LOAD_ERROR(
error instanceof Error ? error.message : typeof error === 'string' ? error : ''
),
}));
}
}, [ruleId, http]);
useEffect(() => {
fetchRuleSummary();
}, [fetchRuleSummary]);

return { ...ruleSummary, reloadRuleSummary: fetchRuleSummary };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React from 'react';
import {
EuiText,
EuiSpacer,
EuiFlexGroup,
EuiFlexItem,
EuiIcon,
IconType,
EuiLoadingSpinner,
} from '@elastic/eui';
import { intersectionBy } from 'lodash';
import { ActionsProps } from '../types';
import { useFetchRuleActions } from '../../../hooks/use_fetch_rule_actions';
import { useKibana } from '../../../utils/kibana_react';

interface MapActionTypeIcon {
[key: string]: string | IconType;
}
const mapActionTypeIcon: MapActionTypeIcon = {
/* TODO: Add the rest of the application logs (SVGs ones) */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fkanout I checked https://elastic.github.io/eui/#/display/icons and I tried to find rest logos, but I couldn't. Instead of adding rest application logs, I was thinking maybe we have a more dynamic way to retrieve the logos from existing functionality.

I will check out https://github.com/elastic/kibana/tree/main/x-pack/plugins/triggers_actions_ui/public/application/components/builtin_action_types and see if we can get something from there.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fkanout I just investigated how we can get the icon dynamically and it looks like we can use the actionTypeRegistry from triggers_actions_ui to retrieve the iconClass. I quickly tested it out in your branch and it works. actionRegistry has a list method and we can get the iconClass from there!
Screenshot 2022-05-11 at 14 43 08

Suggested change
/* TODO: Add the rest of the application logs (SVGs ones) */
function getActionIconClass(actionGroupId?: string): string | undefined {
const actionGroup = actionTypeRegistry.list().find((group) => group.id === actionGroupId);
return actionGroup?.iconClass;
}

actionRegistry we can get like this:

 const {
    triggersActionsUi: { ruleTypeRegistry, actionTypeRegistry },
  } = useKibana().services;

This is not a complete code (no Typescript), just a proof of concept that we can dynamically get what we need. Do you want me to wrap it up and push directly to your branch? Or shall I fix it on another PR once this PR is merged? Whatever works for you.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for tracking purposes here's the issue that will fix this #132220

'.server-log': 'logsApp',
'.email': 'email',
'.pagerduty': 'apps',
'.index': 'indexOpen',
'.slack': 'logoSlack',
'.webhook': 'logoWebhook',
};
export function Actions({ ruleActions }: ActionsProps) {
const {
http,
notifications: { toasts },
} = useKibana().services;
const { isLoadingActions, allActions, errorActions } = useFetchRuleActions({ http });
if (ruleActions && ruleActions.length <= 0) return <EuiText size="s">0</EuiText>;
const actions = intersectionBy(allActions, ruleActions, 'actionTypeId');
if (isLoadingActions) return <EuiLoadingSpinner size="s" />;
return (
<EuiFlexGroup direction="column">
{actions.map((action) => (
<>
<EuiFlexGroup alignItems="baseline">
<EuiFlexItem grow={false}>
<EuiIcon size="m" type={mapActionTypeIcon[action.actionTypeId] ?? 'apps'} />
</EuiFlexItem>
<EuiFlexItem style={{ margin: '0px' }}>
<EuiText size="s">{action.name}</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
<EuiSpacer size="s" />
</>
))}
{errorActions && toasts.addDanger({ title: errorActions })}
</EuiFlexGroup>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

export { PageTitle } from './page_title';
export { ItemTitleRuleSummary } from './item_title_rule_summary';
export { ItemValueRuleSummary } from './item_value_rule_summary';
export { Actions } from './actions';
Loading