Skip to content

Commit 7f24b18

Browse files
Improved field mapping UX (#330) (#337)
* fixed issues; improved UX Signed-off-by: Amardeepsingh Siglani <[email protected]> * removed unwanted change Signed-off-by: Amardeepsingh Siglani <[email protected]> * override default mapping with created Signed-off-by: Amardeepsingh Siglani <[email protected]> Signed-off-by: Amardeepsingh Siglani <[email protected]> (cherry picked from commit d0e184d) Co-authored-by: Amardeepsingh Siglani <[email protected]>
1 parent ddc1422 commit 7f24b18

File tree

2 files changed

+76
-37
lines changed

2 files changed

+76
-37
lines changed

public/pages/CreateDetector/components/ConfigureFieldMapping/components/RequiredFieldMapping/FieldMappingsTable.tsx

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import React, { Component } from 'react';
77
import { RouteComponentProps } from 'react-router-dom';
88
import {
9+
CriteriaWithPagination,
910
EuiBasicTableColumn,
1011
EuiEmptyPrompt,
1112
EuiIcon,
@@ -42,12 +43,27 @@ interface FieldMappingsTableProps<T extends MappingViewType> extends RouteCompon
4243
mappingProps: MappingProps[T];
4344
}
4445

45-
interface FieldMappingsTableState {}
46+
interface FieldMappingsTableState {
47+
pageIndex: number;
48+
}
4649

4750
export default class FieldMappingsTable<T extends MappingViewType> extends Component<
4851
FieldMappingsTableProps<T>,
4952
FieldMappingsTableState
5053
> {
54+
constructor(props: FieldMappingsTableProps<T>) {
55+
super(props);
56+
this.state = {
57+
pageIndex: 0,
58+
};
59+
}
60+
61+
private onTableChange = (nextValues: CriteriaWithPagination<FieldMappingsTableItem>) => {
62+
this.setState({
63+
pageIndex: nextValues.page.index,
64+
});
65+
};
66+
5167
render() {
5268
const { loading, indexFields, ruleFields } = this.props;
5369
let items: FieldMappingsTableItem[];
@@ -70,7 +86,6 @@ export default class FieldMappingsTable<T extends MappingViewType> extends Compo
7086
{
7187
field: 'ruleFieldName',
7288
name: 'Rule field name',
73-
sortable: true,
7489
dataType: 'string',
7590
width: '25%',
7691
render: (ruleFieldName: string) => ruleFieldName || DEFAULT_EMPTY_DATA,
@@ -85,7 +100,6 @@ export default class FieldMappingsTable<T extends MappingViewType> extends Compo
85100
{
86101
field: 'logFieldName',
87102
name: 'Log field name',
88-
sortable: true,
89103
dataType: 'string',
90104
width: '45%',
91105
render: (logFieldName: string, entry: FieldMappingsTableItem) => {
@@ -150,9 +164,12 @@ export default class FieldMappingsTable<T extends MappingViewType> extends Compo
150164
loading={loading}
151165
items={items}
152166
columns={columns}
153-
pagination={true}
167+
pagination={{
168+
pageIndex: this.state.pageIndex,
169+
}}
154170
sorting={sorting}
155171
isSelectable={false}
172+
onTableChange={this.onTableChange}
156173
message={
157174
<EuiEmptyPrompt
158175
style={{ maxWidth: '45em' }}

public/pages/CreateDetector/components/ConfigureFieldMapping/containers/ConfigureFieldMapping.tsx

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,13 @@ export default class ConfigureFieldMapping extends Component<
7676
if (mappingsView.ok) {
7777
const existingMappings = { ...this.state.createdMappings };
7878
Object.keys(mappingsView.response.properties).forEach((ruleFieldName) => {
79-
existingMappings[ruleFieldName] = mappingsView.response.properties[ruleFieldName].path;
79+
existingMappings[ruleFieldName] =
80+
this.state.createdMappings[ruleFieldName] ||
81+
mappingsView.response.properties[ruleFieldName].path;
8082
});
8183
this.setState({
8284
createdMappings: existingMappings,
83-
mappingsData: {
84-
...mappingsView.response,
85-
unmapped_field_aliases: [
86-
'timestamp',
87-
...(mappingsView.response.unmapped_field_aliases || []),
88-
],
89-
},
85+
mappingsData: mappingsView.response,
9086
});
9187
this.updateMappingSharedState(existingMappings);
9288
}
@@ -111,11 +107,16 @@ export default class ConfigureFieldMapping extends Component<
111107
return invalidFields;
112108
}
113109

114-
onMappingCreation = (ruleFieldName: string, indxFieldName: string): void => {
110+
onMappingCreation = (ruleFieldName: string, indexFieldName: string): void => {
115111
const newMappings: ruleFieldToIndexFieldMap = {
116112
...this.state.createdMappings,
117-
[ruleFieldName]: indxFieldName,
113+
[ruleFieldName]: indexFieldName,
118114
};
115+
116+
if (!indexFieldName) {
117+
delete newMappings[ruleFieldName];
118+
}
119+
119120
const invalidMappingFieldNames = this.getInvalidMappingFieldNames(newMappings);
120121
this.setState({
121122
createdMappings: newMappings,
@@ -142,17 +143,28 @@ export default class ConfigureFieldMapping extends Component<
142143
...createdMappings,
143144
};
144145

145-
// read only data
146146
const mappedRuleFields: string[] = [];
147-
const mappedLogFields: string[] = [];
147+
const logFields: Set<string> = new Set(mappingsData.unmapped_index_fields || []);
148+
let pendingCount = mappingsData.unmapped_field_aliases?.length || 0;
149+
const unmappedRuleFields = [...(mappingsData.unmapped_field_aliases || [])];
150+
148151
Object.keys(mappingsData.properties).forEach((ruleFieldName) => {
149152
mappedRuleFields.unshift(ruleFieldName);
150-
mappedLogFields.unshift(mappingsData.properties[ruleFieldName].path);
153+
154+
// Need this check to avoid adding undefined value
155+
// When user removes existing mapping for default mapped values, the mapping will be undefined
156+
if (existingMappings[ruleFieldName]) {
157+
logFields.add(existingMappings[ruleFieldName]);
158+
}
151159
});
152160

153-
// edit data
154-
const ruleFields = [...(mappingsData.unmapped_field_aliases || [])];
155-
const indexFields = [...(mappingsData.unmapped_index_fields || [])];
161+
Object.keys(existingMappings).forEach((mappedRuleField) => {
162+
if (unmappedRuleFields.includes(mappedRuleField)) {
163+
pendingCount--;
164+
}
165+
});
166+
167+
const indexFieldOptions = Array.from(logFields);
156168

157169
return (
158170
<div>
@@ -168,24 +180,31 @@ export default class ConfigureFieldMapping extends Component<
168180

169181
<EuiSpacer size={'m'} />
170182

171-
{ruleFields.length > 0 ? (
183+
{unmappedRuleFields.length > 0 ? (
172184
<>
173-
<EuiCallOut
174-
title={`${ruleFields.length} rule fields may need manual mapping`}
175-
color={'warning'}
176-
>
177-
<p>
178-
To generate accurate findings, we recommend mapping the following security rules
179-
fields with the log field from your data source.
180-
</p>
181-
</EuiCallOut>
185+
{pendingCount > 0 ? (
186+
<EuiCallOut
187+
title={`${pendingCount} rule fields may need manual mapping`}
188+
color={'warning'}
189+
>
190+
<p>
191+
To generate accurate findings, we recommend mapping the following security rules
192+
fields with the log fields in your data source.
193+
</p>
194+
</EuiCallOut>
195+
) : (
196+
<EuiCallOut title={`All rule fields have been mapped`} color={'success'}>
197+
<p>Your data source have been mapped with all security rule fields.</p>
198+
</EuiCallOut>
199+
)}
200+
182201
<EuiSpacer size={'m'} />
183-
<ContentPanel title={`Manual field mappings (${ruleFields.length})`} titleSize={'m'}>
202+
<ContentPanel title={`Pending field mappings`} titleSize={'m'}>
184203
<FieldMappingsTable<MappingViewType.Edit>
185204
{...this.props}
186205
loading={loading}
187-
ruleFields={ruleFields}
188-
indexFields={indexFields}
206+
ruleFields={unmappedRuleFields}
207+
indexFields={indexFieldOptions}
189208
mappingProps={{
190209
type: MappingViewType.Edit,
191210
existingMappings,
@@ -213,7 +232,7 @@ export default class ConfigureFieldMapping extends Component<
213232
buttonContent={
214233
<div data-test-subj="mapped-fields-btn">
215234
<EuiTitle>
216-
<h4>{`View mapped fields (${mappedRuleFields.length})`}</h4>
235+
<h4>{`Default mapped fields (${mappedRuleFields.length})`}</h4>
217236
</EuiTitle>
218237
</div>
219238
}
@@ -222,13 +241,16 @@ export default class ConfigureFieldMapping extends Component<
222241
initialIsOpen={false}
223242
>
224243
<EuiHorizontalRule margin={'xs'} />
225-
<FieldMappingsTable<MappingViewType.Readonly>
244+
<FieldMappingsTable<MappingViewType.Edit>
226245
{...this.props}
227246
loading={loading}
228247
ruleFields={mappedRuleFields}
229-
indexFields={mappedLogFields}
248+
indexFields={indexFieldOptions}
230249
mappingProps={{
231-
type: MappingViewType.Readonly,
250+
type: MappingViewType.Edit,
251+
existingMappings,
252+
invalidMappingFieldNames,
253+
onMappingCreation: this.onMappingCreation,
232254
}}
233255
/>
234256
</EuiAccordion>

0 commit comments

Comments
 (0)