Skip to content

Commit

Permalink
Agent version warning (#1259)
Browse files Browse the repository at this point in the history
* Make HealthIcon size configurable

* Add HealthIcon storybook

* Add Pill storybook

* Use the Pill component for the agent version in HostsList

* Add semver library for frontend

* Add conditional agent version warning banner on host view

* Add agent version warning state in the hosts list view

* Add tooltip message to warning agent version

* Correct warning message text

* Move agent version function to new agent lib library
  • Loading branch information
arbulu89 authored Mar 16, 2023
1 parent 219214a commit 03c28eb
Show file tree
Hide file tree
Showing 15 changed files with 2,062 additions and 218 deletions.
9 changes: 5 additions & 4 deletions assets/js/components/Health/HealthIcon.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ function HealthIcon({
health = undefined,
centered = false,
hoverOpacity = true,
size = 'l',
}) {
const hoverOpacityClass = {
'hover:opacity-75': hoverOpacity,
Expand All @@ -24,7 +25,7 @@ function HealthIcon({
case 'passing':
return (
<EOS_CHECK_CIRCLE_OUTLINED
size="l"
size={size}
className={classNames(
hoverOpacityClass,
computedIconCssClass('fill-jungle-green-500', centered)
Expand All @@ -34,7 +35,7 @@ function HealthIcon({
case 'warning':
return (
<EOS_WARNING_OUTLINED
size="l"
size={size}
className={classNames(
hoverOpacityClass,
computedIconCssClass('fill-yellow-500', centered)
Expand All @@ -44,7 +45,7 @@ function HealthIcon({
case 'critical':
return (
<EOS_ERROR_OUTLINED
size="l"
size={size}
className={classNames(
hoverOpacityClass,
computedIconCssClass('fill-red-500', centered)
Expand All @@ -56,7 +57,7 @@ function HealthIcon({
default:
return (
<EOS_LENS_FILLED
size="l"
size={size}
className={classNames(
hoverOpacityClass,
computedIconCssClass('fill-gray-500', centered)
Expand Down
31 changes: 31 additions & 0 deletions assets/js/components/Health/HealthIcon.stories.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import HealthIcon from '.';

export default {
title: 'HealthIcon',
component: HealthIcon,
};

export function Passing() {
return <HealthIcon health="passing" />;
}

export function Warning() {
return <HealthIcon health="warning" />;
}

export function Critical() {
return <HealthIcon health="critical" />;
}

export function Pending() {
return <HealthIcon health="pending" />;
}

export function Default() {
return <HealthIcon health="unknown" />;
}

export function ExtraLarge() {
return <HealthIcon health="passing" size="xl" />;
}
6 changes: 6 additions & 0 deletions assets/js/components/Health/HealthIcon.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ describe('HealthIcon', () => {
expect(svgEl.classList.toString()).toContain(
'hover:opacity-75 fill-jungle-green-500'
);
expect(svgEl).toHaveAttribute('width', '24');
});
it('should display a yellow svg when the health is warning', () => {
const { container } = render(<HealthIcon health="warning" />);
Expand All @@ -32,4 +33,9 @@ describe('HealthIcon', () => {
const svgEl = container.querySelector("[data-testid='eos-svg-component']");
expect(svgEl.classList.toString()).toContain('hover:opacity-100');
});
it('should display the icon with the applied size', () => {
const { container } = render(<HealthIcon health="passing" size="m" />);
const svgEl = container.querySelector("[data-testid='eos-svg-component']");
expect(svgEl).toHaveAttribute('width', '18');
});
});
7 changes: 7 additions & 0 deletions assets/js/components/HostDetails/HostDetails.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import { useParams } from 'react-router-dom';
import { useSelector } from 'react-redux';

import { networkClient } from '@lib/network';
import { agentVersionWarning } from '@lib/agent';

import ListView from '@components/ListView';
import Table from '@components/Table';

import PageHeader from '@components/PageHeader';
import BackButton from '@components/BackButton';
import ClusterLink from '@components/ClusterLink';
import WarningBanner from '@components/Banners/WarningBanner';
import SuseLogo from '@static/suse_logo.svg';
import {
getInstancesOnHost,
Expand Down Expand Up @@ -50,6 +52,8 @@ function HostDetails() {
return <div>Not Found</div>;
}

const versionWarningMessage = agentVersionWarning(host.agent_version);

return (
<div>
<BackButton url="/hosts">Back to Hosts</BackButton>
Expand All @@ -76,6 +80,9 @@ function HostDetails() {
)
)}
</div>
{versionWarningMessage && (
<WarningBanner>{versionWarningMessage}</WarningBanner>
)}
<div className="mt-4 bg-white shadow rounded-lg py-4 px-8">
<ListView
orientation="vertical"
Expand Down
69 changes: 69 additions & 0 deletions assets/js/components/HostDetails/HostDetails.test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react';
import { screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import MockAdapter from 'axios-mock-adapter';

import {
withState,
defaultInitialState,
renderWithRouterMatch,
} from '@lib/test-utils';
import { hostFactory } from '@lib/test-utils/factories';
import { networkClient } from '@lib/network';

import HostDetails from './HostDetails';

// delayResponse is need to avoid component updates on axios requests
const axiosMock = new MockAdapter(networkClient, { delayResponse: 2000 });

describe('HostDetails component', () => {
beforeEach(() => {
axiosMock.reset();
});

it('should not show any warning message if the agent version is correct', () => {
const hosts = hostFactory.buildList(1, { agent_version: '2.0.0' });
const { id: hostID } = hosts[0];
const state = {
...defaultInitialState,
hostsList: {
hosts,
},
};
const [StatefulHostDetails] = withState(<HostDetails />, state);

renderWithRouterMatch(StatefulHostDetails, {
path: '/hosts/:hostID',
route: `/hosts/${hostID}`,
});

expect(
screen.queryByText(
'Agent version 2.0.0 or greater is required for the new checks engine.'
)
).not.toBeInTheDocument();
});

it('should show 2.0.0 version required warning message', () => {
const hosts = hostFactory.buildList(1, { agent_version: '1.0.0' });
const { id: hostID } = hosts[0];
const state = {
...defaultInitialState,
hostsList: {
hosts,
},
};
const [StatefulHostDetails] = withState(<HostDetails />, state);

renderWithRouterMatch(StatefulHostDetails, {
path: '/hosts/:hostID',
route: `/hosts/${hostID}`,
});

expect(
screen.getByText(
'Agent version 2.0.0 or greater is required for the new checks engine.'
)
).toBeInTheDocument();
});
});
50 changes: 39 additions & 11 deletions assets/js/components/HostsList.jsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
import React, { Fragment } from 'react';
import React from 'react';

import { useSearchParams } from 'react-router-dom';
import { useSelector, useDispatch } from 'react-redux';
import { EOS_WARNING_OUTLINED } from 'eos-icons-react';

import Table from '@components/Table';
import HealthIcon from '@components/Health/HealthIcon';
import { useSearchParams } from 'react-router-dom';
import Tags from '@components/Tags';
import { addTagToHost, removeTagFromHost } from '@state/hosts';
import HostLink from '@components/HostLink';
import ClusterLink from '@components/ClusterLink';
import SapSystemLink from '@components/SapSystemLink';
import PageHeader from '@components/PageHeader';
import { useSelector, useDispatch } from 'react-redux';

import { post, del } from '@lib/network';
import Pill from '@components/Pill';
import HealthSummary from '@components/HealthSummary/HealthSummary';
import { getCounters } from '@components/HealthSummary/summarySelection';
import ProviderLabel from '@components/ProviderLabel';
import Tooltip from '@components/Tooltip';

import { addTagToHost, removeTagFromHost } from '@state/hosts';
import { post, del } from '@lib/network';
import { agentVersionWarning } from '@lib/agent';

const getInstancesByHost = (applicationInstances, databaseInstances, hostId) =>
applicationInstances
Expand Down Expand Up @@ -118,11 +124,33 @@ function HostsList() {
{
title: 'Agent version',
key: 'agent_version',
render: (content) => (
<span className="px-2 text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800 truncate w-32 inline-block">
{content}
</span>
),
render: (content) => {
const warning = agentVersionWarning(content);
if (warning) {
return (
<Pill
size="xs"
className="bg-yellow-100 text-yellow-800 group flex items-center relative"
>
<EOS_WARNING_OUTLINED
size="base"
className="centered fill-yellow-800"
/>
<span className="ml-1 truncate max-w-[100px]">{content}</span>
<Tooltip tooltipText={warning} width="w-52 -translate-x-1/3" />
</Pill>
);
}
return (
<Pill
size="xs"
display="inline-block"
className="bg-green-100 text-green-800 truncate max-w-[112px]"
>
{content}
</Pill>
);
},
},
{
title: 'Tags',
Expand Down
49 changes: 48 additions & 1 deletion assets/js/components/HostsList.test.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
import React from 'react';
import { screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import 'intersection-observer';
import '@testing-library/jest-dom';
import { hostFactory } from '@lib/test-utils/factories';

import { renderWithRouter, withDefaultState, withState } from '@lib/test-utils';
import {
renderWithRouter,
withDefaultState,
withState,
defaultInitialState,
} from '@lib/test-utils';

import { filterTable, clearFilter } from '@components/Table/Table.test';

Expand Down Expand Up @@ -50,6 +56,47 @@ describe('HostsLists component', () => {
);
});
});

it('should show a warning state if the agent version is not compatible', () => {
const user = userEvent.setup();

const host1 = hostFactory.build({ agent_version: '1.0.0' });
const host2 = hostFactory.build({ agent_version: '2.0.0' });
const state = {
...defaultInitialState,
hostsList: {
hosts: [].concat(host1, host2),
},
};

const [StatefulHostsList] = withState(<HostsList />, state);

renderWithRouter(StatefulHostsList);
const table = screen.getByRole('table');
const host1VersionCell = table.querySelector(
'tr:nth-child(1) > td:nth-child(7)'
);
expect(host1VersionCell).toHaveTextContent('1.0.0');
const icon1 = host1VersionCell.querySelector(
"[data-testid='eos-svg-component']"
);
expect(icon1.classList.toString()).toContain('fill-yellow-800');

const host2VersionCell = table.querySelector(
'tr:nth-child(2) > td:nth-child(7)'
);
expect(host2VersionCell).toHaveTextContent('2.0.0');
expect(
host2VersionCell.querySelector("[data-testid='eos-svg-component']")
).toBeNull();

user.hover(host2VersionCell);
expect(
screen.queryByText(
'Agent version 2.0.0 or greater is required for the new checks engine.'
)
).toBeInTheDocument();
});
});

describe('filtering', () => {
Expand Down
4 changes: 3 additions & 1 deletion assets/js/components/Pill/Pill.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,19 @@ function Pill({
onClick = () => {},
size = 'sm',
roundedMode = 'rounded-full',
display = 'inline-flex',
}) {
return (
<span
className={classNames(
`inline-flex leading-5 font-semibold`,
`leading-5 font-semibold`,
{
'bg-green-100': !className,
'text-green-800': !className,
},
roundedMode,
sizeClasses[size],
display,
className
)}
aria-hidden="true"
Expand Down
Loading

0 comments on commit 03c28eb

Please sign in to comment.