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

Support for CDB lists beginning with quotes #7171

Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
6101ac9
CDB list (list-editor.tsx): Change split to match
yenienserrano Nov 22, 2024
074639e
Changelog: Entry added
yenienserrano Nov 22, 2024
54ef41f
Add validation to cdb list inputs
yenienserrano Nov 25, 2024
04dd010
Test(list-editor): Add some test
yenienserrano Nov 25, 2024
684f902
Validation(list-editor.tsx): start and end with quotes
yenienserrano Nov 26, 2024
9fa0830
Test(list-editor): Add case with ipv6
yenienserrano Nov 26, 2024
310743b
Request change(list-editor): Applying requested changes
yenienserrano Nov 26, 2024
4f4211e
Test(error cases): Add test to error cases to key or value validation
yenienserrano Nov 26, 2024
96bc819
Validation(list-editor): Add colon validation.
yenienserrano Nov 26, 2024
82787a9
Validation (list-editor): Apply comments
yenienserrano Nov 27, 2024
16303cd
Validation(list-editor): Fix error found on test cration
yenienserrano Nov 27, 2024
aedff74
Test jest(list-editor): Add test to add and remove
yenienserrano Nov 27, 2024
b7d2702
Test jest(list-editor): Add test to save CDB list
yenienserrano Nov 27, 2024
69ca767
Test jest(list-editor): Add test to edit and cancel edit
yenienserrano Nov 27, 2024
dd339bd
Validations(list-editor): Refactor validations functions
yenienserrano Nov 28, 2024
97fb4a0
Validation(list-editor): Fix warning in console
yenienserrano Nov 28, 2024
cdbde72
Test jest:(list-editor): Add new cases
yenienserrano Nov 28, 2024
058a983
Test jest:(list-editor): Add new tests
yenienserrano Nov 29, 2024
32faa94
Test jest(list-editor): Add test name and path exist
yenienserrano Nov 29, 2024
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ All notable changes to the Wazuh app project will be documented in this file.

### Fixed

- Added ending quotes to CDB Lists keys [#7159](https://github.com/wazuh/wazuh-dashboard-plugins/pull/7159)
- Fixed rendering of rows in CDB list table when it starts with quotes. [#7171](https://github.com/wazuh/wazuh-dashboard-plugins/issues/7171)

## Wazuh v4.10.1 - OpenSearch Dashboards 2.16.0 - Revision 00

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from 'react';
import '@testing-library/jest-dom';
import { render, fireEvent, screen, waitFor } from '@testing-library/react';
import { Provider } from 'react-redux';
import configureMockStore from 'redux-mock-store';
import WzListEditor from './list-editor';

const mockStore = configureMockStore();
const store = mockStore({
appStateReducers: {
userAccount: {
administrator: true,
},
withUserLogged: true,
userPermissions: {
'lists:read': { '*:*:*': 'allow' },
},
},
});

describe('WzListEditor', () => {
const cdblist = [
{
key: 'test',
value: 'testValue',
},
{
key: '":test"',
value: '":testValue"',
},
{
key: 'key',
value: '":key"',
},
{
key: '":key1"',
value: 'value1',
},
{
key: '"key2"',
value: '"value2"',
},
{
key: 'key3',
value: '',
},
{
key: '"a0:a0:a0:a0:a0:a0"',
value: '',
},
{
key: '"b1:b1:b1:b1:b1:b1"',
value: '"test:6"',
},
];

it('should render the component', () => {
const cdblistMap = cdblist.map(item => {
return `${item.key}:${item.value}`;
});

const listContent = {
content: `${cdblistMap.join('\n')}`,
};

render(
<Provider store={store}>
<WzListEditor listContent={listContent} />
</Provider>,
);

cdblist.forEach(item => {
expect(screen.getByText(item.key)).toBeInTheDocument();
if (!item.value === '') {
expect(screen.getByText(item.value)).toBeInTheDocument();
}
expect(screen.queryByText(`${item.key}:${item.value}`)).toBeFalsy();
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
EuiFieldText,
EuiSpacer,
EuiPanel,
EuiFormRow,
} from '@elastic/eui';

import { connect } from 'react-redux';
Expand All @@ -45,6 +46,9 @@ import { UI_ERROR_SEVERITIES } from '../../../../../../react-services/error-orch
import { UI_LOGGER_LEVELS } from '../../../../../../../common/constants';
import { getErrorOrchestrator } from '../../../../../../react-services/common-services';

type FieldTypes = 'key' | 'value' | 'edit';
type FieldStateTypes = 'addingKey' | 'addingValue' | 'editingValue';

class WzListEditor extends Component {
constructor(props) {
super(props);
Expand All @@ -58,6 +62,7 @@ class WzListEditor extends Component {
editingValue: '',
newListName: '',
showWarningRestart: false,
isInvalid: [],
};
this.items = {};

Expand Down Expand Up @@ -91,16 +96,29 @@ class WzListEditor extends Component {
* Save in the state as object the items for an easy modification by key-value
* @param {String} content
*/
contentToObject(content) {
const items = {};
contentToObject(content: string) {
const items: {
[key: string]: string;
} = {};
const lines = content.split('\n');

const regex = /^((?:[^:"]*|"[^"]*")*):(.*)$/;

lines.forEach(line => {
const split = line.startsWith('"') ? line.split('":') : line.split(':');
// All keys with multiple colons (:) should end with a quotation mark (")
const key = split[0].startsWith('"') ? split[0] + '"' : split[0];
const value = split[1] || '';
if (key) items[key] = value; // Prevent add empty keys
// Regex splitting the first : and ignoring the ones inside quotes
const match = line.match(regex);

if (match) {
const [, key, value] = match;
const trimmedKey = key.trim();
const trimmedValue = value.trim();

if (trimmedKey) {
items[trimmedKey] = trimmedValue;
}
}
});

return items;
}

Expand Down Expand Up @@ -200,18 +218,24 @@ class WzListEditor extends Component {
};

onChangeKey = e => {
this.validateQuotes(e.target.value, 'key');

this.setState({
addingKey: e.target.value,
});
};

onChangeValue = e => {
this.validateQuotes(e.target.value, 'value');

this.setState({
addingValue: e.target.value,
});
};

onChangeEditingValue = e => {
this.validateQuotes(e.target.value, 'edit');

this.setState({
editingValue: e.target.value,
});
Expand Down Expand Up @@ -243,6 +267,37 @@ class WzListEditor extends Component {
];
};

/**
* Validate that if starts with a quote it ends with a quote and vice versa.
* Validate that the value has 0 or 2 quotes.
* @param {String} value
*/
private validateQuotes(value: string, field: FieldTypes): boolean {
const hasMiddleQuotes = value.slice(1, -1).includes('"');
const startsWithQuote = value.startsWith('"');
const endsWithQuote = value.endsWith('"');

const isValid =
(startsWithQuote && endsWithQuote && !hasMiddleQuotes) ||
(!startsWithQuote && !endsWithQuote && !value.includes('"'));
yenienserrano marked this conversation as resolved.
Show resolved Hide resolved
if (!isValid) {
this.setState({
isInvalid: [
...this.state.isInvalid,
{
field,
message: 'Must start and end with quotes or have no quotes at all',
yenienserrano marked this conversation as resolved.
Show resolved Hide resolved
},
],
});
} else {
this.setState(prevState => ({
isInvalid: prevState.isInvalid.filter(error => error.field !== field),
}));
}
return isValid;
}

/**
* Append a key value to this.items and after that if everything works ok re-create the array for the table
*/
Expand All @@ -259,6 +314,15 @@ class WzListEditor extends Component {
);
return;
}
if (this.state.isInvalid.length > 0) {
yenienserrano marked this conversation as resolved.
Show resolved Hide resolved
this.showToast(
'danger',
'Error',
'Key and value must start and end with quotes or have no quotes at all',
yenienserrano marked this conversation as resolved.
Show resolved Hide resolved
3000,
);
return;
}
this.items[addingKey] = addingValue;
const itemsArr = this.contentToArray(this.items);
this.setState({
Expand Down Expand Up @@ -368,6 +432,38 @@ class WzListEditor extends Component {
);
}

inputValidation({
field,
value,
onChange,
placeholder,
}: {
field: FieldTypes;
value: FieldStateTypes;
onChange: (e: any) => void;
placeholder: string;
}) {
return (
<EuiFormRow
fullWidth={true}
isInvalid={this.state.isInvalid.some(error => error.field === field)}
yenienserrano marked this conversation as resolved.
Show resolved Hide resolved
error={
this.state.isInvalid.filter(error => error.field === field)?.[0]
?.message
}
yenienserrano marked this conversation as resolved.
Show resolved Hide resolved
>
<EuiFieldText
fullWidth={true}
placeholder={placeholder}
value={value}
onChange={onChange}
aria-label='Use aria labels when no actual label is in use'
isInvalid={this.state.isInvalid.some(error => error.field === field)}
yenienserrano marked this conversation as resolved.
Show resolved Hide resolved
/>
</EuiFormRow>
);
}

renderAdd() {
const { addingKey, addingValue } = this.state;

Expand All @@ -378,23 +474,21 @@ class WzListEditor extends Component {
<EuiSpacer size='l' />
<EuiFlexGroup>
<EuiFlexItem>
<EuiFieldText
fullWidth={true}
placeholder='Key'
value={addingKey}
onChange={this.onChangeKey}
aria-label='Use aria labels when no actual label is in use'
/>
{this.inputValidation({
field: 'key',
value: addingKey,
onChange: this.onChangeKey,
placeholder: 'Key',
})}
</EuiFlexItem>

<EuiFlexItem>
<EuiFieldText
fullWidth={true}
placeholder='Value'
value={addingValue}
onChange={this.onChangeValue}
aria-label='Use aria labels when no actual label is in use'
/>
{this.inputValidation({
field: 'value',
value: addingValue,
onChange: this.onChangeValue,
placeholder: 'Value',
})}
</EuiFlexItem>
<EuiFlexItem grow={false}>
<EuiFlexGroup>
Expand All @@ -404,6 +498,7 @@ class WzListEditor extends Component {
isDisabled={!addingKey}
fill
onClick={() => this.addItem()}
disabled={this.state.isInvalid.length > 0}
yenienserrano marked this conversation as resolved.
Show resolved Hide resolved
>
Add
</EuiButtonEmpty>
Expand Down Expand Up @@ -470,14 +565,12 @@ class WzListEditor extends Component {
sortable: true,
render: (value, item) => {
if (this.state.editing === item.key) {
return (
<EuiFieldText
placeholder='New value'
value={this.state.editingValue}
onChange={this.onChangeEditingValue}
aria-label='Use aria labels when no actual label is in use'
/>
);
return this.inputValidation({
field: 'edit',
value: this.state.editingValue,
onChange: this.onChangeEditingValue,
placeholder: 'New value',
});
} else {
return <span>{value}</span>;
}
Expand Down
Loading