Skip to content

Commit

Permalink
Fixing up store initialization on sidecars page. (#21020)
Browse files Browse the repository at this point in the history
* Fixing up store initialization on sidecars page.

* Handle missing `getInitialState` in `useStore`, add tests.

* Fixing handling of empty operating system.

* Binding instance function, adding types, adding tests.

* Improving typing.

* Adding license headers.

* Handle missing variable in configuration form.
  • Loading branch information
dennisoelkers authored Nov 22, 2024
1 parent 0e4b6a0 commit 6b98484
Show file tree
Hide file tree
Showing 14 changed files with 233 additions and 51 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
import * as React from 'react';
import { render, screen } from 'wrappedTestingLibrary';

import OperatingSystemIcon from './OperatingSystemIcon';

describe('OperatingSystemIcon', () => {
it('returns default icon when empty operating system is passed', async () => {
render(<OperatingSystemIcon />);
await screen.findByText('help');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,18 @@ type Props = {
operatingSystem?: string
};

const matchIcon = (os: string) => {
const defaultIcon = {
iconName: 'help',
iconType: 'default',
} as const;

const matchIcon = (_os: string) => {
if (!_os) {
return defaultIcon;
}

const os = _os.trim().toLowerCase();

if (os.includes('darwin') || os.includes('mac os')) {
return {
iconName: 'apple',
Expand Down Expand Up @@ -60,14 +71,11 @@ const matchIcon = (os: string) => {
} as const;
}

return {
iconName: 'help',
iconType: 'default',
} as const;
return defaultIcon;
};

const OperatingSystemIcon = ({ operatingSystem }: Props) => {
const { iconName, iconType } = matchIcon(operatingSystem.trim().toLowerCase());
const OperatingSystemIcon = ({ operatingSystem = undefined }: Props) => {
const { iconName, iconType } = matchIcon(operatingSystem);

return (
<Container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,26 +248,26 @@ const ConfigurationForm = ({
const collector = _collectors ? _collectors.find((c) => c.id === collectorId) : undefined;

return (
<span>
<>
<FormControl.Static>{_formatCollector(collector)}</FormControl.Static>
<HelpBlock bsClass="warning">
<b>Note:</b> Log Collector cannot change while the Configuration is in use. Clone the Configuration
to test it using another Collector.
</HelpBlock>
</span>
</>
);
}

return (
<span>
<>
<Select inputId="collector_id"
options={_formatCollectorOptions()}
value={collectorId}
onChange={_onCollectorChange}
placeholder="Collector"
required />
<HelpBlock>Choose the log collector this configuration is meant for.</HelpBlock>
</span>
</>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ class ConfigurationVariablesHelper extends React.Component<ConfigurationVariable
title="Delete Configuration Variable?"
onConfirm={this._handleDeleteConfirm}
onCancel={this._closeErrorModal}>
<p>Are you sure you want to remove the configuration variable <strong>{variableToDelete.name}</strong>?</p>
<p>Are you sure you want to remove the configuration variable <strong>{variableToDelete?.name}</strong>?</p>
</BootstrapModalConfirm>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright (C) 2020 Graylog, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*/
import * as React from 'react';
import { render, screen, waitFor } from 'wrappedTestingLibrary';
import userEvent from '@testing-library/user-event';

import type { Collector } from 'components/sidecars/types';

import CollectorList from './CollectorList';

const collectors: Array<Collector> = [
{
id: '659bce1c00e5295c3218f4b4',
name: 'auditbeat',
service_type: 'exec',
node_operating_system: 'linux',
},
{
id: '659bb9b5a5d6ba4b611c7170',
name: 'filebeat',
service_type: 'exec',
node_operating_system: 'linux',
},
{
id: '659bb9b5a5d6ba4b611c7171',
name: 'filebeat',
service_type: 'exec',
node_operating_system: 'darwin',
},
{
id: '659bb9b6a5d6ba4b611c7172',
name: 'filebeat',
service_type: 'exec',
node_operating_system: 'freebsd',
},
{
id: '659bb9b6a5d6ba4b611c7176',
name: 'filebeat',
service_type: 'svc',
node_operating_system: 'windows',
},
{
id: '659bb9b6a5d6ba4b611c7174',
name: 'nxlog',
service_type: 'exec',
node_operating_system: 'linux',
},
{
id: '659bb9b6a5d6ba4b611c7175',
name: 'nxlog',
service_type: 'svc',
node_operating_system: 'windows',
},
{
id: '659bb9b6a5d6ba4b611c7173',
name: 'winlogbeat',
service_type: 'svc',
node_operating_system: 'windows',
},
];

const SUT = (props: Partial<React.ComponentProps<typeof CollectorList>>) => (
<CollectorList collectors={collectors}
onClone={jest.fn()}
onDelete={jest.fn()}
onPageChange={jest.fn()}
onQueryChange={jest.fn()}
validateCollector={jest.fn()}
pagination={{ page: 1, pageSize: 10, total: 1 }}
query=""
total={collectors.length}
{...props} />
);

describe('CollectorList', () => {
it('renders list of collectors', async () => {
render(<SUT />);

await screen.findByText('auditbeat');
await screen.findAllByText('filebeat');
await screen.findAllByText('nxlog');
await screen.findAllByText('winlogbeat');
});

it('triggers `onQueryChange` when query is changed', async () => {
const onQueryChange = jest.fn();
render(<SUT onQueryChange={onQueryChange} />);

const queryInput = await screen.findByPlaceholderText('Find collectors');
userEvent.type(queryInput, 'newquery{enter}');

await waitFor(() => {
expect(onQueryChange).toHaveBeenCalledWith('newquery', expect.any(Function));
});
});

it('triggers `onPageChange` when page size is changed', async () => {
const onPageChange = jest.fn();
render(<SUT onPageChange={onPageChange} />);

const pageDropdown = await screen.findByRole('button', { name: /configure page size/i });
userEvent.click(pageDropdown);

userEvent.click(await screen.findByRole('menuitem', { name: '25' }));

await waitFor(() => {
expect(onPageChange).toHaveBeenCalledWith(1, 25);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,21 @@ type CollectorListProps = {
},
query: string,
total: number,
onClone: (...args: any[]) => void,
onDelete: (...args: any[]) => void,
onPageChange: (...args: any[]) => void,
onQueryChange: (...args: any[]) => void,
onClone: (collector: string, name: string, callback: () => void) => void,
onDelete: (collector: Collector) => void,
onPageChange: (page: number, pageSize: number) => void,
onQueryChange: () => void,
validateCollector: (collector: Collector) => Promise<{ errors: { name: string[] } }>,
}

class CollectorList extends React.Component<CollectorListProps> {
headerCellFormatter(header) {
const className = (header === 'Actions' ? style.actionsColumn : '');
const headerCellFormatter = (header: React.ReactNode) => {
const className = (header === 'Actions' ? style.actionsColumn : '');

return <th className={className}>{header}</th>;
}
return <th className={className}>{header}</th>;
};

collectorFormatter(collector) {
class CollectorList extends React.Component<CollectorListProps> {
collectorFormatter = (collector: Collector) => {
const { onClone, onDelete, validateCollector } = this.props;

return (
Expand All @@ -57,7 +57,7 @@ class CollectorList extends React.Component<CollectorListProps> {
onDelete={onDelete}
validateCollector={validateCollector} />
);
}
};

render() {
const { collectors, pagination, query, total, onPageChange, onQueryChange } = this.props;
Expand Down Expand Up @@ -100,7 +100,7 @@ class CollectorList extends React.Component<CollectorListProps> {
<DataTable id="collector-list"
className="table-hover"
headers={headers}
headerCellFormatter={this.headerCellFormatter}
headerCellFormatter={headerCellFormatter}
rows={collectors}
dataRowFormatter={this.collectorFormatter}
noDataText="There are no log collectors to display, why don't you create one?"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class CollectorListContainer extends React.Component<CollectorListContainerProps
loadCollectors();
}

handleClone = (collector, name, callback) => {
handleClone = (collector: string, name: string, callback: () => void) => {
const { sendTelemetry } = this.props;

sendTelemetry(TELEMETRY_EVENT_TYPE.SIDECARS.LOG_COLLECTOR_CLONED, {
Expand All @@ -73,7 +73,7 @@ class CollectorListContainer extends React.Component<CollectorListContainerProps
await CollectorsActions.delete(collector);
};

handlePageChange = (page, pageSize) => {
handlePageChange = (page: number, pageSize: number) => {
const { query } = this.props.collectors;

CollectorsActions.list({ query: query, page: page, pageSize: pageSize });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import CopyCollectorModal from './CopyCollectorModal';

type Props = {
collector: Collector,
onClone: () => void,
onClone: (collector: string, name: string, callback: () => void) => void,
onDelete: (collector: Collector) => void,
validateCollector: (collector: Collector) => Promise<{ errors: { name: string[] } }>,
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,46 +62,46 @@ const SidecarListContainer = ({ paginationQueryParameter }: Props) => {
options.page = effectivePage;

return SidecarsActions.listPaginated(options);
}, [paginationQueryParameter?.page, paginationQueryParameter?.pageSize]);
}, [paginationQueryParameter.page, paginationQueryParameter.pageSize, sidecars.onlyActive, sidecars.query, sidecars.sort]);

useEffect(() => {
_reloadSidecars();
const interval = setInterval(() => _reloadSidecars({}), SIDECAR_DATA_REFRESH);

return () => clearInterval(interval);
}, []);
}, [_reloadSidecars]);

const handleSortChange = useCallback((field) => () => {
_reloadSidecars({
sortField: field,
// eslint-disable-next-line no-nested-ternary
order: (sidecars.sort.field === field ? (sidecars.sort.order === 'asc' ? 'desc' : 'asc') : 'asc'),
});
}, []);
}, [_reloadSidecars, sidecars.sort.field, sidecars.sort.order]);

const handlePageChange = useCallback((page, pageSize) => {
_reloadSidecars({ page: page, pageSize: pageSize });
}, []);
}, [_reloadSidecars]);

const handleQueryChange = useCallback((query = '', callback = () => {}) => {
const { resetPage } = paginationQueryParameter;

resetPage();

_reloadSidecars({ query: query }).finally(callback);
}, []);
}, [_reloadSidecars, paginationQueryParameter]);

const toggleShowInactive = useCallback(() => {
const { resetPage } = paginationQueryParameter;

resetPage();

_reloadSidecars({ onlyActive: !sidecars.onlyActive });
}, []);
}, [_reloadSidecars, paginationQueryParameter, sidecars.onlyActive]);

const { sidecars: sidecarsList, onlyActive, pagination, query, sort } = sidecars;

const isLoading = !sidecars;
const isLoading = !sidecarsList;

if (isLoading) {
return <Spinner />;
Expand Down
8 changes: 4 additions & 4 deletions graylog2-web-interface/src/components/sidecars/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ export type Collector = {
service_type: string;
node_operating_system: string;
name: string;
validation_parameters: string;
executable_path: string;
execute_parameters: string;
default_template: string;
validation_parameters?: string;
executable_path?: string;
execute_parameters?: string;
default_template?: string;
id: string;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,7 @@ type SidecarEditConfigurationPageProps = {
params: any;
};

const SidecarEditConfigurationPage = ({
params,
}: SidecarEditConfigurationPageProps) => {
const SidecarEditConfigurationPage = ({ params }: SidecarEditConfigurationPageProps) => {
const [configuration, setConfiguration] = useState<Configuration>(null);
const [configurationSidecars, setConfigurationSidecars] = useState<ConfigurationSidecarsResponse>(null);
const history = useHistory();
Expand Down
Loading

0 comments on commit 6b98484

Please sign in to comment.