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

Improvements for field mappings #432

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
f78f8dd
[FEATURE] Detector must have at least one alert set #288
jovancvetkovic3006 Jan 9, 2023
1ea796c
Merge branch 'main' of https://github.com/opensearch-project/security…
jovancvetkovic3006 Jan 11, 2023
0d514d8
Merge branch 'main' of https://github.com/opensearch-project/security…
jovancvetkovic3006 Jan 16, 2023
ff8117e
[BUG] Create detector | Interval field can be empty #378
jovancvetkovic3006 Jan 23, 2023
b308059
Adjust styling for Finding details flyout #369
jovancvetkovic3006 Jan 24, 2023
73fae1f
Merge branch 'main' of https://github.com/opensearch-project/security…
jovancvetkovic3006 Jan 24, 2023
7a41744
unit tests
jovancvetkovic3006 Jan 24, 2023
06be7da
detector unit tests
jovancvetkovic3006 Jan 25, 2023
f0ccd00
Merge branch 'main' of https://github.com/opensearch-project/security…
jovancvetkovic3006 Jan 25, 2023
6bf765e
detector unit tests
jovancvetkovic3006 Jan 26, 2023
36b6390
detector unit tests
jovancvetkovic3006 Jan 26, 2023
c23a669
detector unit tests
jovancvetkovic3006 Jan 26, 2023
ce0944b
detector unit tests
jovancvetkovic3006 Jan 26, 2023
d37fb41
detector unit tests
jovancvetkovic3006 Jan 26, 2023
d523098
detector unit tests
jovancvetkovic3006 Jan 26, 2023
8ef0bd0
detector unit tests
jovancvetkovic3006 Jan 26, 2023
9c71aa8
detector unit tests
jovancvetkovic3006 Jan 27, 2023
bc50e0a
Merge branch 'main' of https://github.com/opensearch-project/security…
jovancvetkovic3006 Jan 31, 2023
31c02b0
Merge branch 'main' of https://github.com/opensearch-project/security…
jovancvetkovic3006 Jan 31, 2023
460e7d2
Merge branch 'main' of https://github.com/opensearch-project/security…
jovancvetkovic3006 Feb 1, 2023
482b2c7
unit tests review
jovancvetkovic3006 Feb 1, 2023
b4ed7fa
unit tests review
jovancvetkovic3006 Feb 3, 2023
8381d32
unit tests review
jovancvetkovic3006 Feb 3, 2023
2933d63
unit tests review
jovancvetkovic3006 Feb 3, 2023
378ea69
unit tests review
jovancvetkovic3006 Feb 3, 2023
84b225a
unit tests review
jovancvetkovic3006 Feb 3, 2023
9fa4df8
unit tests review
jovancvetkovic3006 Feb 3, 2023
7e03bfd
unit tests review
jovancvetkovic3006 Feb 3, 2023
237325a
Feature/update vertical domain #372
jovancvetkovic3006 Feb 3, 2023
5422508
Unit tests for public components #383
jovancvetkovic3006 Feb 10, 2023
d54ce17
Unit tests for public components #383
jovancvetkovic3006 Feb 10, 2023
926e249
Unit tests for public components #383
jovancvetkovic3006 Feb 10, 2023
092efff
Unit tests for public components #383
jovancvetkovic3006 Feb 10, 2023
8a2246b
Unit tests for public components #383
jovancvetkovic3006 Feb 11, 2023
fa436c4
Merge branch 'main' of https://github.com/opensearch-project/security…
jovancvetkovic3006 Feb 16, 2023
95c6db8
Unit tests for public components #383
jovancvetkovic3006 Feb 16, 2023
6dab0ee
PR code review
jovancvetkovic3006 Feb 16, 2023
239150f
PR code review
jovancvetkovic3006 Feb 16, 2023
94a8788
Merge branch 'main' of https://github.com/opensearch-project/security…
jovancvetkovic3006 Feb 19, 2023
c86731e
PR code review
jovancvetkovic3006 Feb 20, 2023
b2c6bc8
[FEATURE] Create detector | Make data source multi-select field #419
jovancvetkovic3006 Feb 20, 2023
758c38c
[FEATURE] Create detector | Make data source multi-select field #419
jovancvetkovic3006 Feb 21, 2023
44bdd50
[FEATURE] Create detector | Make data source multi-select field #419
jovancvetkovic3006 Feb 21, 2023
8f8ad02
[FEATURE] Create detector | Make data source multi-select field #419
jovancvetkovic3006 Feb 21, 2023
2ddea79
Merge branch 'unit_tests_for_public_components' of https://github.com…
jovancvetkovic3006 Feb 21, 2023
25c97cb
[FEATURE] Create detector | Make data source multi-select field #419
jovancvetkovic3006 Feb 21, 2023
d7cf62f
[FEATURE] Create detector | Make data source multi-select field #419
jovancvetkovic3006 Feb 21, 2023
b2b15da
[FEATURE] Create detector | Make data source multi-select field #419
jovancvetkovic3006 Feb 21, 2023
a004ec3
[FEATURE] Create detector | Make data source multi-select field #419
jovancvetkovic3006 Feb 21, 2023
2c38c77
[FEATURE] Create detector | Make data source multi-select field #419
jovancvetkovic3006 Feb 21, 2023
5673bfb
unit tests fix
jovancvetkovic3006 Feb 21, 2023
3e4db63
Code review
jovancvetkovic3006 Feb 21, 2023
e846f2b
Code review
jovancvetkovic3006 Feb 21, 2023
4259c1b
Merge branch 'main' of https://github.com/opensearch-project/security…
jovancvetkovic3006 Feb 21, 2023
964c560
Code review
jovancvetkovic3006 Feb 21, 2023
b9ab9ff
Code review
jovancvetkovic3006 Feb 21, 2023
5402219
Code review
jovancvetkovic3006 Feb 22, 2023
0075e93
Merge branch 'main' of https://github.com/opensearch-project/security…
jovancvetkovic3006 Feb 22, 2023
9cfa754
Code review
jovancvetkovic3006 Feb 22, 2023
ee2075e
snapshot fix
jovancvetkovic3006 Feb 22, 2023
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
35 changes: 17 additions & 18 deletions cypress/integration/1_detectors.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,17 @@ const testMappings = {
const cypressDNSRule = dns_rule_data.title;

describe('Detectors', () => {
const indexName = 'cypress-test-dns';
const cypressIndexDns = 'cypress-index-dns';
const cypressIndexWindows = 'cypress-index-windows';
const detectorName = 'test detector';

before(() => {
cy.cleanUpTests();

cy.createIndex(cypressIndexWindows, sample_index_settings);

// Create test index
cy.createIndex(indexName, sample_index_settings).then(() =>
cy.createIndex(cypressIndexDns, sample_index_settings).then(() =>
cy
.request('POST', '_plugins/_security_analytics/rules/_search?prePackaged=true', {
from: 0,
Expand Down Expand Up @@ -58,11 +61,6 @@ describe('Detectors', () => {
});

it('...should show mappings warning', () => {
const indexName = 'cypress-index-windows';
const dnsName = 'cypress-index-dns';
cy.createIndex(indexName, sample_index_settings);
cy.createIndex(dnsName, sample_dns_settings);

// Locate Create detector button click to start
cy.get('.euiButton').filter(':contains("Create detector")').click({ force: true });

Expand All @@ -71,23 +69,25 @@ describe('Detectors', () => {
contains: 'Define detector',
});

// Select our pre-seeded data source (check indexName)
// Select our pre-seeded data source (check cypressIndexDns)
cy.get(`[data-test-subj="define-detector-select-data-source"]`)
.find('input')
.focus()
.realType(indexName);
.realType(cypressIndexDns);

// Select threat detector type (Windows logs)
cy.get(`input[id="dns"]`).click({ force: true });

// Select our pre-seeded data source (check indexName)
// Select our pre-seeded data source (check cypressIndexDns)
cy.get(`[data-test-subj="define-detector-select-data-source"]`)
.find('input')
.focus()
.realType(dnsName)
.realType(cypressIndexWindows)
.realPress('Enter');

cy.get('.euiCallOut').should('be.visible').contains('Detector configuration warning');
cy.get('.euiCallOut')
.should('be.visible')
.contains('The selected log sources contain different types of logs');
});

it('...can be created', () => {
Expand All @@ -102,11 +102,11 @@ describe('Detectors', () => {
// Enter a name for the detector in the appropriate input
cy.get(`input[placeholder="Enter a name for the detector."]`).focus().realType('test detector');

// Select our pre-seeded data source (check indexName)
// Select our pre-seeded data source (check cypressIndexDns)
cy.get(`[data-test-subj="define-detector-select-data-source"]`)
.find('input')
.focus()
.realType(indexName);
.realType(cypressIndexDns);

cy.intercept({
pathname: '/_plugins/_security_analytics/rules/_search',
Expand Down Expand Up @@ -198,7 +198,7 @@ describe('Detectors', () => {
cy.contains('Detector details');
cy.contains(detectorName);
cy.contains('dns');
cy.contains(indexName);
cy.contains(cypressIndexDns);
cy.contains('Alert on test_trigger');

// Create the detector
Expand Down Expand Up @@ -252,8 +252,7 @@ describe('Detectors', () => {
.find('input')
.ospClear()
.focus()
.realType('.opensearch-notifications-config')
.realPress('Enter');
.realType(cypressIndexWindows);

// Change detector scheduling
cy.get(`[data-test-subj="detector-schedule-number-select"]`).ospClear().focus().realType('10');
Expand All @@ -271,7 +270,7 @@ describe('Detectors', () => {
cy.contains('test detector edited');
cy.contains('Every 10 hours');
cy.contains('Edited description');
cy.contains('.opensearch-notifications-config');
cy.contains(cypressIndexWindows);
});

it('...rules can be edited', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export default class FieldMappingsTable<T extends MappingViewType> extends Compo
const columns: EuiBasicTableColumn<FieldMappingsTableItem>[] = [
{
field: 'ruleFieldName',
name: 'Rule field name',
name: 'Detector field name',
dataType: 'string',
width: '25%',
render: (ruleFieldName: string) => ruleFieldName || DEFAULT_EMPTY_DATA,
Expand All @@ -100,7 +100,7 @@ export default class FieldMappingsTable<T extends MappingViewType> extends Compo
},
{
field: 'logFieldName',
name: 'Log field name',
name: 'Log source field name',
dataType: 'string',
width: '45%',
render: (logFieldName: string, entry: FieldMappingsTableItem) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,37 @@ export default class ConfigureFieldMapping extends Component<

<EuiSpacer size={'m'} />

<EuiPanel>
<EuiAccordion
buttonContent={
<div data-test-subj="mapped-fields-btn">
<EuiTitle>
<h4>{`Automatically mapped fields (${mappedRuleFields.length})`}</h4>
</EuiTitle>
</div>
}
buttonProps={{ style: { paddingLeft: '10px', paddingRight: '10px' } }}
id={'mappedFieldsAccordion'}
initialIsOpen={false}
>
<EuiHorizontalRule margin={'xs'} />
<FieldMappingsTable<MappingViewType.Edit>
{...this.props}
loading={loading}
ruleFields={mappedRuleFields}
indexFields={indexFieldOptions}
mappingProps={{
type: MappingViewType.Edit,
existingMappings,
invalidMappingFieldNames,
onMappingCreation: this.onMappingCreation,
}}
/>
</EuiAccordion>
</EuiPanel>

<EuiSpacer size={'m'} />

{unmappedRuleFields.length > 0 ? (
<>
{pendingCount > 0 ? (
Expand Down Expand Up @@ -227,34 +258,6 @@ export default class ConfigureFieldMapping extends Component<
</>
)}

<EuiPanel>
<EuiAccordion
buttonContent={
<div data-test-subj="mapped-fields-btn">
<EuiTitle>
<h4>{`Default mapped fields (${mappedRuleFields.length})`}</h4>
</EuiTitle>
</div>
}
buttonProps={{ style: { paddingLeft: '10px', paddingRight: '10px' } }}
id={'mappedFieldsAccordion'}
initialIsOpen={false}
>
<EuiHorizontalRule margin={'xs'} />
<FieldMappingsTable<MappingViewType.Edit>
{...this.props}
loading={loading}
ruleFields={mappedRuleFields}
indexFields={indexFieldOptions}
mappingProps={{
type: MappingViewType.Edit,
existingMappings,
invalidMappingFieldNames,
onMappingCreation: this.onMappingCreation,
}}
/>
</EuiAccordion>
</EuiPanel>
<EuiSpacer size={'m'} />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,54 @@

import React, { Component } from 'react';
import { ContentPanel } from '../../../../../../components/ContentPanel';
import { EuiComboBox, EuiComboBoxOptionOption, EuiFormRow, EuiSpacer } from '@elastic/eui';
import {
EuiComboBox,
EuiComboBoxOptionOption,
EuiFormRow,
EuiSpacer,
EuiCallOut,
EuiTextColor,
} from '@elastic/eui';
import { FormFieldHeader } from '../../../../../../components/FormFieldHeader/FormFieldHeader';
import { IndexOption } from '../../../../../Detectors/models/interfaces';
import { MIN_NUM_DATA_SOURCES } from '../../../../../Detectors/utils/constants';
import IndexService from '../../../../../../services/IndexService';
import { NotificationsStart } from 'opensearch-dashboards/public';
import { errorNotificationToast } from '../../../../../../utils/helpers';
import _ from 'lodash';
import { FieldMappingService } from '../../../../../../services';

interface DetectorDataSourceProps {
detectorIndices: string[];
indexService: IndexService;
filedMappingService: FieldMappingService;
isEdit: boolean;
onDetectorInputIndicesChange: (selectedOptions: EuiComboBoxOptionOption<string>[]) => void;
notifications: NotificationsStart;
detector_type: string;
}

interface DetectorDataSourceState {
loading: boolean;
fieldTouched: boolean;
indexOptions: IndexOption[];
errorMessage?: string;
message: string[];
}

export default class DetectorDataSource extends Component<
DetectorDataSourceProps,
DetectorDataSourceState
> {
private indicesMappings: any = {};

constructor(props: DetectorDataSourceProps) {
super(props);
this.state = {
loading: true,
fieldTouched: props.isEdit,
indexOptions: [],
message: [],
};
}

Expand Down Expand Up @@ -82,17 +97,75 @@ export default class DetectorDataSource extends Component<
this.onSelectionChange(parsedOptions);
};

onSelectionChange = (options: EuiComboBoxOptionOption<string>[]) => {
onSelectionChange = async (options: EuiComboBoxOptionOption<string>[]) => {
const allIndices = _.map(options, 'label');
for (let indexName in this.indicesMappings) {
if (allIndices.indexOf(indexName) === -1) {
// cleanup removed indexes
delete this.indicesMappings[indexName];
}
}

for (const indexName of allIndices) {
if (!this.indicesMappings[indexName]) {
const detectorType = this.props.detector_type.toLowerCase();
const result = await this.props.filedMappingService.getMappingsView(
indexName,
detectorType
);
result.ok && (this.indicesMappings[indexName] = result.response.unmapped_field_aliases);
}
}

if (!_.isEmpty(this.indicesMappings)) {
let firstMapping: string[] = [];
let firstMatchMappingIndex: string = '';
let message: string[] = [];
for (let indexName in this.indicesMappings) {
if (this.indicesMappings.hasOwnProperty(indexName)) {
if (!firstMapping.length) firstMapping = this.indicesMappings[indexName];
!firstMatchMappingIndex.length && (firstMatchMappingIndex = indexName);
if (!_.isEqual(firstMapping, this.indicesMappings[indexName])) {
message = [
`We recommend creating separate detectors for each of the following log sources:`,
firstMatchMappingIndex,
indexName,
];
break;
}
}
}

this.setState({ message });
}

this.props.onDetectorInputIndicesChange(options);
};

render() {
const { detectorIndices } = this.props;
const { loading, fieldTouched, indexOptions, errorMessage } = this.state;
const { loading, fieldTouched, indexOptions, errorMessage, message } = this.state;
const isInvalid = fieldTouched && detectorIndices.length < MIN_NUM_DATA_SOURCES;
return (
<ContentPanel title={'Data source'} titleSize={'m'}>
<EuiSpacer size={'m'} />
{message.length ? (
<>
<EuiCallOut
title="The selected log sources contain different types of logs"
color="warning"
>
{message.map((messageItem: string, index: number) => (
<EuiTextColor color={'default'} key={`callout-message-part-${index}`}>
{index === 0 ? '' : 'ㅤ•ㅤ'}
{messageItem}
<br />
</EuiTextColor>
))}
</EuiCallOut>
<EuiSpacer size={'m'} />
</>
) : null}
<EuiFormRow
label={
<FormFieldHeader headerTitle={'Select or input source indexes or index patterns'} />
Expand Down
Loading