Skip to content

Commit

Permalink
Add a details button to open the findings flyout from the correlation…
Browse files Browse the repository at this point in the history
…s page. opensearch-project#564

Signed-off-by: Jovan Cvetkovic <jovanca.cvetkovic@gmail.com>
  • Loading branch information
jovancacvetkovic committed May 3, 2023
1 parent f1f366a commit c69f078
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 84 deletions.
4 changes: 4 additions & 0 deletions public/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ $euiTextColor: $euiColorDarkestShade !default;
@return mix($euiColorInk, $color, $percent);
}

.euiLoadingContent__singleLineBackground {
background: linear-gradient(137deg, #d3dae6 45%, #bac0ca 50%, #d3dae6 55%) !important;
}

.refresh-button {
min-width: 0;
.euiButtonContent {
Expand Down
55 changes: 24 additions & 31 deletions public/pages/Alerts/components/AlertFlyout/AlertFlyout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ import {
renderTime,
} from '../../../../utils/helpers';
import { FindingsService, IndexPatternsService, OpenSearchService } from '../../../../services';
import FindingDetailsFlyout from '../../../Findings/components/FindingDetailsFlyout';
import { parseAlertSeverityToOption } from '../../../CreateDetector/components/ConfigureAlerts/utils/helpers';
import { Finding } from '../../../Findings/models/interfaces';
import { NotificationsStart } from 'opensearch-dashboards/public';
Expand All @@ -49,7 +48,6 @@ export interface AlertFlyoutProps {

export interface AlertFlyoutState {
acknowledged: boolean;
findingFlyoutData?: Finding;
findingItems: Finding[];
loading: boolean;
rules: { [key: string]: RuleSource };
Expand All @@ -71,10 +69,6 @@ export class AlertFlyout extends React.Component<AlertFlyoutProps, AlertFlyoutSt
this.getFindings();
}

setFindingFlyoutData(finding?: Finding) {
this.setState({ findingFlyoutData: finding });
}

getFindings = async () => {
this.setState({ loading: true });
const {
Expand Down Expand Up @@ -126,6 +120,18 @@ export class AlertFlyout extends React.Component<AlertFlyoutProps, AlertFlyoutSt
createFindingTableColumns(): EuiBasicTableColumn<Finding>[] {
const { detector } = this.props;
const { rules } = this.state;

const backButton = (
<EuiButtonIcon
iconType="arrowLeft"
aria-label="back"
onClick={() => DataStore.findings.closeFlyout()}
display="base"
size="s"
data-test-subj={'finding-details-flyout-back-button'}
/>
);

return [
{
field: 'timestamp',
Expand All @@ -142,7 +148,16 @@ export class AlertFlyout extends React.Component<AlertFlyoutProps, AlertFlyoutSt
render: (id, finding) =>
(
<EuiLink
onClick={() => this.setFindingFlyoutData(finding)}
onClick={() =>
DataStore.findings.openFlyout(
{
...finding,
detector: { _id: detector.id as string, _index: '', _source: detector },
},
this.state.findingItems,
backButton
)
}
data-test-subj={'finding-details-flyout-button'}
>
{`${(id as string).slice(0, 7)}...`}
Expand Down Expand Up @@ -175,31 +190,9 @@ export class AlertFlyout extends React.Component<AlertFlyoutProps, AlertFlyoutSt
render() {
const { onClose, alertItem, detector, onAcknowledge } = this.props;
const { trigger_name, state, severity, start_time, last_notification_time } = alertItem;
const { acknowledged, findingItems, findingFlyoutData, loading, rules } = this.state;
const { acknowledged, findingItems, loading } = this.state;

return !!this.state.findingFlyoutData ? (
<FindingDetailsFlyout
{...this.props}
finding={{
...(findingFlyoutData as Finding),
detector: { _id: detector.id as string, _index: '', _source: detector },
}}
findings={findingItems}
closeFlyout={onClose}
backButton={
<EuiButtonIcon
iconType="arrowLeft"
aria-label="back"
onClick={() => this.setFindingFlyoutData()}
display="base"
size="s"
data-test-subj={'finding-details-flyout-back-button'}
/>
}
allRules={rules}
indexPatternsService={this.props.indexPatternService}
/>
) : (
return (
<EuiFlyout
onClose={onClose}
hideCloseButton
Expand Down
17 changes: 16 additions & 1 deletion public/pages/Correlations/components/FindingCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import {
getSeverityColor,
getLabelFromLogType,
} from '../utils/constants';
import { DataStore } from '../../../store/DataStore';

export interface FindingCardProps {
id: string;
Expand All @@ -41,6 +42,8 @@ export const FindingCard: React.FC<FindingCardProps> = ({
logType,
timestamp,
detectionRule,
finding,
findings,
}) => {
const correlationHeader = correlationData ? (
<>
Expand All @@ -54,7 +57,7 @@ export const FindingCard: React.FC<FindingCardProps> = ({
aria-label={'View finding details'}
data-test-subj={`view-details-icon`}
iconType={'expand'}
onClick={() => {}}
onClick={() => DataStore.findings.openFlyout(finding, findings)}
/>
</EuiToolTip>
</EuiFlexItem>
Expand Down Expand Up @@ -121,6 +124,18 @@ export const FindingCard: React.FC<FindingCardProps> = ({
<EuiFlexItem grow={1}>
<strong>{getLabelFromLogType(logType)}</strong>
</EuiFlexItem>
{!correlationData && (
<EuiFlexItem grow={false}>
<EuiToolTip content={'View finding details'}>
<EuiButtonIcon
aria-label={'View finding details'}
data-test-subj={`view-details-icon`}
iconType={'expand'}
onClick={() => DataStore.findings.openFlyout(finding, findings)}
/>
</EuiToolTip>
</EuiFlexItem>
)}
</EuiFlexGroup>
{correlationHeader ? <EuiHorizontalRule margin="s" /> : null}
<EuiSpacer size="m" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,10 @@ export class Correlations extends React.Component<CorrelationsProps, Correlation
private async updateState() {
if (this.props.location.state) {
const state = this.props.location.state;

const specificFindingInfo: SpecificFindingCorrelations = {
finding: {
...state.finding,
id: state.finding.id,
logType: state.finding.detector._source.detector_type,
timestamp: new Date(state.finding.timestamp).toLocaleString(),
Expand Down Expand Up @@ -383,6 +385,8 @@ export class Correlations extends React.Component<CorrelationsProps, Correlation
logType={findingCardsData.finding.logType}
timestamp={findingCardsData.finding.timestamp}
detectionRule={findingCardsData.finding.detectionRule}
finding={findingCardsData.finding}
findings={findingCardsData.correlatedFindings}
/>
<EuiSpacer />
<EuiTitle size="xs">
Expand All @@ -404,6 +408,8 @@ export class Correlations extends React.Component<CorrelationsProps, Correlation
score: finding.correlationScore || 0,
onInspect: this.onFindingInspect,
}}
finding={finding}
findings={findingCardsData.correlatedFindings}
/>
);
})}
Expand Down
48 changes: 35 additions & 13 deletions public/pages/Findings/components/FindingDetailsFlyout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
EuiTab,
EuiInMemoryTable,
EuiBasicTableColumn,
EuiLoadingContent,
} from '@elastic/eui';
import { capitalizeFirstLetter, renderTime } from '../../../utils/helpers';
import { DEFAULT_EMPTY_DATA, ROUTES } from '../../../utils/constants';
Expand All @@ -52,11 +53,9 @@ interface FindingDetailsFlyoutProps extends RouteComponentProps {
finding: FindingItemType;
findings: FindingItemType[];
backButton?: React.ReactNode;
allRules: { [id: string]: RuleSource };
opensearchService: OpenSearchService;
indexPatternsService: IndexPatternsService;
correlationService: CorrelationService;
closeFlyout: () => void;
}

interface FindingDetailsFlyoutState {
Expand All @@ -66,6 +65,7 @@ interface FindingDetailsFlyoutState {
isCreateIndexPatternModalVisible: boolean;
selectedTab: { id: string; content: React.ReactNode | null };
correlatedFindings: CorrelationFinding[];
allRules: { [id: string]: RuleSource };
}

export default class FindingDetailsFlyout extends Component<
Expand All @@ -78,8 +78,20 @@ export default class FindingDetailsFlyout extends Component<
loading: false,
ruleViewerFlyoutData: null,
isCreateIndexPatternModalVisible: false,
selectedTab: { id: FindingFlyoutTabId.DETAILS, content: null },
selectedTab: {
id: FindingFlyoutTabId.DETAILS,
content: (
<>
<EuiTitle size={'s'}>
<h3>Rule details</h3>
</EuiTitle>
<EuiSpacer size={'m'} />
<EuiLoadingContent lines={4} />
</>
),
},
correlatedFindings: [],
allRules: {},
};
}

Expand Down Expand Up @@ -108,11 +120,17 @@ export default class FindingDetailsFlyout extends Component<
}
});

this.setState({
selectedTab: {
id: FindingFlyoutTabId.DETAILS,
content: this.getTabContent(FindingFlyoutTabId.DETAILS),
},
DataStore.rules.getAllRules().then((rules) => {
const allRules: { [id: string]: RuleSource } = {};
rules.forEach((hit) => (allRules[hit._id] = hit._source));
this.setState({ allRules }, () => {
this.setState({
selectedTab: {
id: FindingFlyoutTabId.DETAILS,
content: this.getTabContent(FindingFlyoutTabId.DETAILS),
},
});
});
});
}

Expand Down Expand Up @@ -153,7 +171,7 @@ export default class FindingDetailsFlyout extends Component<
};

renderRuleDetails = (rules: Query[] = []) => {
const { allRules } = this.props;
const { allRules = {} } = this.state;
return rules.map((rule, key) => {
const fullRule = allRules[rule.id];
const severity = capitalizeFirstLetter(fullRule.level);
Expand Down Expand Up @@ -375,6 +393,7 @@ export default class FindingDetailsFlyout extends Component<
const { correlatedFindings } = this.state;
const { finding } = this.props;

DataStore.findings.closeFlyout();
this.props.history.push({
pathname: `${ROUTES.CORRELATIONS}`,
state: {
Expand Down Expand Up @@ -467,7 +486,7 @@ export default class FindingDetailsFlyout extends Component<
}

render() {
const { closeFlyout, backButton } = this.props;
const { backButton } = this.props;
const {
finding: {
id,
Expand All @@ -480,7 +499,7 @@ export default class FindingDetailsFlyout extends Component<
} = this.props;
return (
<EuiFlyout
onClose={closeFlyout}
onClose={DataStore.findings.closeFlyout}
ownFocus={true}
size={'m'}
hideCloseButton
Expand Down Expand Up @@ -511,7 +530,7 @@ export default class FindingDetailsFlyout extends Component<
iconType="cross"
display="empty"
iconSize="m"
onClick={closeFlyout}
onClick={DataStore.findings.closeFlyout}
data-test-subj={`close-finding-details-flyout`}
/>
</EuiFlexItem>
Expand Down Expand Up @@ -558,7 +577,10 @@ export default class FindingDetailsFlyout extends Component<
isSelected={tab.id === this.state.selectedTab.id}
onClick={() => {
this.setState({
selectedTab: { id: tab.id, content: this.getTabContent(tab.id) },
selectedTab: {
id: tab.id,
content: this.getTabContent(tab.id),
},
});
}}
>
Expand Down
43 changes: 5 additions & 38 deletions public/pages/Findings/components/FindingsTable/FindingsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ import {
IndexPatternsService,
CorrelationService,
} from '../../../../services';
import FindingDetailsFlyout from '../FindingDetailsFlyout';
import { Finding } from '../../models/interfaces';
import CreateAlertFlyout from '../CreateAlertFlyout';
import { NotificationChannelTypeOptions } from '../../../CreateDetector/components/ConfigureAlerts/models/interfaces';
import { FindingItemType } from '../../containers/Findings/Findings';
import { parseAlertSeverityToOption } from '../../../CreateDetector/components/ConfigureAlerts/utils/helpers';
import { RuleSource } from '../../../../../server/models/interfaces';
import { DataStore } from '../../../../store/DataStore';

interface FindingsTableProps extends RouteComponentProps {
detectorService: DetectorsService;
Expand Down Expand Up @@ -114,41 +114,6 @@ export default class FindingsTable extends Component<FindingsTableProps, Finding
if (refreshPage) this.props.onRefresh();
};

renderFindingDetailsFlyout = (finding: FindingItemType) => {
if (this.state.flyoutOpen) this.closeFlyout();
else {
const { findings, rules } = this.props;
const { findingsFiltered, filteredFindings } = this.state;

const logTypes = new Set<string>();
const severities = new Set<string>();
filteredFindings.forEach((finding) => {
if (finding) {
const queryId = finding.queries[0].id;
logTypes.add(rules[queryId].category);
severities.add(rules[queryId].level);
}
});

this.setState({
flyout: (
<FindingDetailsFlyout
{...this.props}
finding={finding}
findings={findingsFiltered ? filteredFindings : findings}
closeFlyout={this.closeFlyout}
history={this.props.history}
allRules={this.props.rules}
indexPatternsService={this.props.indexPatternsService}
correlationService={this.props.correlationService}
/>
),
flyoutOpen: true,
selectedFinding: finding,
});
}
};

renderCreateAlertFlyout = (finding: Finding) => {
if (this.state.flyoutOpen) this.closeFlyout();
else {
Expand Down Expand Up @@ -206,7 +171,7 @@ export default class FindingsTable extends Component<FindingsTableProps, Finding
render: (id, finding) =>
(
<EuiLink
onClick={() => this.renderFindingDetailsFlyout(finding)}
onClick={() => DataStore.findings.openFlyout(finding, this.state.filteredFindings)}
data-test-subj={'finding-details-flyout-button'}
>
{`${(id as string).slice(0, 7)}...`}
Expand Down Expand Up @@ -253,7 +218,9 @@ export default class FindingsTable extends Component<FindingsTableProps, Finding
aria-label={'View details'}
data-test-subj={`view-details-icon`}
iconType={'expand'}
onClick={() => this.renderFindingDetailsFlyout(finding)}
onClick={() =>
DataStore.findings.openFlyout(finding, this.state.filteredFindings)
}
/>
</EuiToolTip>
),
Expand Down
Loading

0 comments on commit c69f078

Please sign in to comment.