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

PMM-13386 Link to update page from update panel #775

Merged
merged 3 commits into from
Oct 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions jest.percona.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
const grafanaConfig = require('./jest.config');

module.exports = {
...grafanaConfig,
roots: [
'<rootDir>/public/app/percona',
'<rootDir>/public/app/plugins/datasource/pmm-pt-summary-datasource',
'<rootDir>/public/app/plugins/panel/pmm-check',
'<rootDir>/public/app/plugins/panel/pmm-pt-summary-panel',
'<rootDir>/public/app/plugins/panel/pmm-update',
],
};
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"lint:fix": "yarn lint:ts --fix",
"jest": "jest --notify --watch",
"jest-ci": "mkdir -p reports/junit && export JEST_JUNIT_OUTPUT_DIR=reports/junit && jest --ci --reporters=default --reporters=jest-junit -w ${TEST_MAX_WORKERS:-100%}",
"jest-percona-ci": "mkdir -p reports/junit && JEST_JUNIT_OUTPUT_DIR=reports/junit jest --ci --reporters=default --reporters=jest-junit -w ${TEST_MAX_WORKERS:-100%} --roots public/app/percona",
"jest-percona-ci": "mkdir -p reports/junit && JEST_JUNIT_OUTPUT_DIR=reports/junit jest --ci --reporters=default --reporters=jest-junit -w ${TEST_MAX_WORKERS:-100%} --config jest.percona.config.js",
"packages:build": "nx run-many -t build --projects='@grafana/*'",
"packages:clean": "rimraf ./npm-artifacts && lerna run clean --parallel",
"packages:prepare": "lerna version --no-push --no-git-tag-version --force-publish --exact",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,21 @@ import { render, screen, waitFor } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';

import { config } from '@grafana/runtime';
import { CheckService } from 'app/percona/check/Check.service';
import { configureStore } from 'app/store/configureStore';
import { StoreState } from 'app/types';
import { OrgRole, StoreState } from 'app/types';

import { Failed } from './Failed';

jest.mock('app/percona/check/Check.service');

describe('Failed::', () => {
beforeEach(() => {
config.bootData.user.isGrafanaAdmin = true;
config.bootData.user.orgRole = OrgRole.Admin;
});

it('should render a sum of total failed checks with severity details', async () => {
jest.spyOn(CheckService, 'getAllFailedChecks').mockImplementationOnce(async () => [
{
Expand Down Expand Up @@ -56,6 +62,8 @@ describe('Failed::', () => {
</Provider>
);

await waitFor(() => expect(CheckService.getAllFailedChecks).toHaveBeenCalled());

await waitFor(() => expect(screen.getByTestId('db-check-panel-critical').textContent).toEqual('3'));
await waitFor(() => expect(screen.getByTestId('db-check-panel-error').textContent).toEqual('0'));
await waitFor(() => expect(screen.getByTestId('db-check-panel-warning').textContent).toEqual('2'));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const Messages = {
upgrade: 'Upgrade',
upgradeTo: (version: string) => `Upgrade to ${version}`,
};
17 changes: 0 additions & 17 deletions public/app/plugins/panel/pmm-update/UpdatePanel.service.ts

This file was deleted.

43 changes: 22 additions & 21 deletions public/app/plugins/panel/pmm-update/UpdatePanel.styles.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import { css } from '@emotion/css';

export const panel = css`
display: flex;
flex-direction: column;
position: relative;
height: 100%;
export const styles = {
panel: css`
display: flex;
flex-direction: column;
position: relative;
height: 100%;

p {
margin-bottom: 0;
}

@media (max-width: 1281px) {
#pmm-update-widget h2 {
font-size: 1.55rem;
margin-bottom: 0.1rem;
p {
margin-bottom: 0;
}
}
`;

export const middleSectionWrapper = css`
align-items: center;
display: flex;
flex: 1;
justify-content: center;
`;
@media (max-width: 1281px) {
#pmm-update-widget h2 {
font-size: 1.55rem;
margin-bottom: 0.1rem;
}
}
`,
middleSectionWrapper: css`
align-items: center;
display: flex;
flex: 1;
justify-content: center;
`,
};
81 changes: 31 additions & 50 deletions public/app/plugins/panel/pmm-update/UpdatePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,74 +1,63 @@
import React, { FC, MouseEvent, useEffect, useState } from 'react';
import React, { FC, MouseEvent, useState } from 'react';

import { Button, IconName, Spinner } from '@grafana/ui';
import { getPerconaUser, getPerconaSettings } from 'app/percona/shared/core/selectors';
import { Button, Spinner } from '@grafana/ui';
import { PMM_UPDATES_LINK } from 'app/percona/shared/components/PerconaBootstrapper/PerconaNavigation';
import { checkUpdatesAction } from 'app/percona/shared/core/reducers/updates';
import { getPerconaUser, getPerconaSettings, getUpdatesInfo } from 'app/percona/shared/core/selectors';
import { useAppDispatch } from 'app/store/store';
import { useSelector } from 'app/types';

import { Messages } from './UpdatePanel.messages';
import * as styles from './UpdatePanel.styles';
import { AvailableUpdate, CurrentVersion, InfoBox, LastCheck, ProgressModal } from './components';
import { usePerformUpdate, useVersionDetails } from './hooks';
import { styles } from './UpdatePanel.styles';
import { formatDateWithTime } from './UpdatePanel.utils';
import { AvailableUpdate, CurrentVersion, InfoBox, LastCheck } from './components';

export const UpdatePanel: FC<{}> = () => {
export const UpdatePanel: FC = () => {
const isOnline = navigator.onLine;
const {
isLoading: isLoadingVersionDetails,
installed,
latest,
latestNewsUrl,
updateAvailable,
lastChecked,
} = useSelector(getUpdatesInfo);
const { result: settings, loading: isLoadingSettings } = useSelector(getPerconaSettings);
const dispatch = useAppDispatch();
const [forceUpdate, setForceUpdate] = useState(false);
const [showModal, setShowModal] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const { isAuthorized } = useSelector(getPerconaUser);
const { result: settings, loading: isLoadingSettings } = useSelector(getPerconaSettings);
const [
{ installedVersionDetails, lastCheckDate, nextVersionDetails, isUpdateAvailable },
fetchVersionErrorMessage,
isLoadingVersionDetails,
isDefaultView,
getCurrentVersionDetails,
] = useVersionDetails();
const [output, updateErrorMessage, isUpdated, updateFailed, launchUpdate] = usePerformUpdate();
const isDefaultView = !latest;
const isLoading = isLoadingVersionDetails || isLoadingSettings;

const handleCheckForUpdates = (e: MouseEvent) => {
if (e.altKey) {
setForceUpdate(true);
}

getCurrentVersionDetails({ force: true });
dispatch(checkUpdatesAction());
};

useEffect(() => {
setErrorMessage(fetchVersionErrorMessage || updateErrorMessage);

const timeout = setTimeout(() => {
setErrorMessage('');
}, 5000);

return () => {
clearTimeout(timeout);
};
}, [fetchVersionErrorMessage, updateErrorMessage]);

const handleUpdate = () => {
setShowModal(true);
launchUpdate();
const handleOpenUpdates = () => {
window.location.assign(PMM_UPDATES_LINK.url!);
};

return (
<>
<div className={styles.panel}>
<CurrentVersion installedVersionDetails={installedVersionDetails} />
{isUpdateAvailable && !isDefaultView && settings?.updatesEnabled && isAuthorized && !isLoading && isOnline ? (
<AvailableUpdate nextVersionDetails={nextVersionDetails} />
{!!installed && <CurrentVersion currentVersion={installed} />}
{updateAvailable && !isDefaultView && settings?.updatesEnabled && isAuthorized && !isLoading && isOnline ? (
<AvailableUpdate nextVersion={latest} newsLink={latestNewsUrl} />
) : null}
{isLoading ? (
<div className={styles.middleSectionWrapper}>
<Spinner />
</div>
) : (
<>
{(isUpdateAvailable || forceUpdate) && settings?.updatesEnabled && isAuthorized && isOnline ? (
{(updateAvailable || forceUpdate) && settings?.updatesEnabled && isAuthorized && isOnline ? (
<div className={styles.middleSectionWrapper}>
{/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */}
<Button onClick={handleUpdate} icon={'fa fa-download' as IconName} variant="secondary">
{Messages.upgradeTo(nextVersionDetails?.nextVersion)}
<Button onClick={handleOpenUpdates} icon="download-alt" variant="secondary">
{!!latest?.version ? Messages.upgradeTo(latest.version) : Messages.upgrade}
</Button>
</div>
) : (
Expand All @@ -84,17 +73,9 @@ export const UpdatePanel: FC<{}> = () => {
<LastCheck
disabled={isLoading || !settings?.updatesEnabled || !isOnline}
onCheckForUpdates={handleCheckForUpdates}
lastCheckDate={lastCheckDate}
lastCheckDate={lastChecked ? formatDateWithTime(lastChecked) : ''}
/>
</div>
<ProgressModal
errorMessage={errorMessage}
isOpen={showModal}
isUpdated={isUpdated}
output={output}
updateFailed={updateFailed}
version={nextVersionDetails?.nextVersion}
/>
</>
);
};
17 changes: 8 additions & 9 deletions public/app/plugins/panel/pmm-update/UpdatePanel.utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { formatDateWithYear, formatDateWithTime } from './UpdatePanel.utils';
import { ISOTimestamp } from './types';

describe('UpdatePanel utils', () => {
const timestamp1 = '2020-06-08T19:16:57Z';
Expand All @@ -8,15 +7,15 @@ describe('UpdatePanel utils', () => {
const timestamp4 = '2021-04-06T00:00:00Z';

it('should format an ISO 8601 timestamp correctly as date without time', () => {
expect(formatDateWithYear(timestamp1 as ISOTimestamp)).toBe('June 08, 2020 UTC');
expect(formatDateWithYear(timestamp2 as ISOTimestamp)).toBe('June 08, 2020 UTC');
expect(formatDateWithYear(timestamp3 as ISOTimestamp)).toBe('June 07, 2020 UTC');
expect(formatDateWithYear(timestamp4 as ISOTimestamp)).toBe('April 06, 2021 UTC');
expect(formatDateWithYear(timestamp1)).toBe('June 08, 2020 UTC');
expect(formatDateWithYear(timestamp2)).toBe('June 08, 2020 UTC');
expect(formatDateWithYear(timestamp3)).toBe('June 07, 2020 UTC');
expect(formatDateWithYear(timestamp4)).toBe('April 06, 2021 UTC');
});
it('should format an ISO 8601 timestamp correctly as date with time', () => {
expect(formatDateWithTime(timestamp1 as ISOTimestamp)).toBe('June 08, 19:16 UTC');
expect(formatDateWithTime(timestamp2 as ISOTimestamp)).toBe('June 08, 0:06 UTC');
expect(formatDateWithTime(timestamp3 as ISOTimestamp)).toBe('June 07, 23:36 UTC');
expect(formatDateWithTime(timestamp4 as ISOTimestamp)).toBe('April 06, 0:00 UTC');
expect(formatDateWithTime(timestamp1)).toBe('June 08, 19:16 UTC');
expect(formatDateWithTime(timestamp2)).toBe('June 08, 0:06 UTC');
expect(formatDateWithTime(timestamp3)).toBe('June 07, 23:36 UTC');
expect(formatDateWithTime(timestamp4)).toBe('April 06, 0:00 UTC');
});
});
6 changes: 2 additions & 4 deletions public/app/plugins/panel/pmm-update/UpdatePanel.utils.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import { format } from 'date-fns';
import { enUS } from 'date-fns/locale';

import { ISOTimestamp } from './types';

export const formatDateWithTime = (timestamp: ISOTimestamp) => {
export const formatDateWithTime = (timestamp: string) => {
const date = new Date(timestamp);
return `${format(date.valueOf() + date.getTimezoneOffset() * 60 * 1000, 'MMMM dd, H:mm', { locale: enUS })} UTC`;
};

export const formatDateWithYear = (timestamp: ISOTimestamp) => {
export const formatDateWithYear = (timestamp: string) => {
const date = new Date(timestamp);
return `${format(date.valueOf() + date.getTimezoneOffset() * 60 * 1000, 'MMMM dd, yyyy', { locale: enUS })} UTC`;
};
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { css } from '@emotion/css';

import { GrafanaTheme } from '@grafana/data';
import { GrafanaTheme2 } from '@grafana/data';

export const getStyles = ({ spacing, typography }: GrafanaTheme) => ({
export const getStyles = ({ spacing, typography }: GrafanaTheme2) => ({
availableUpdate: css`
align-items: flex-start;
display: flex;
font-weight: ${typography.weight.bold};
font-weight: ${typography.fontWeightBold};
justify-content: flex-start;
line-height: ${typography.lineHeight.sm};
margin-top: ${spacing.xs};
line-height: ${typography.bodySmall.lineHeight};
margin-top: ${spacing(0.5)};

> div {
display: flex;
Expand All @@ -21,18 +21,18 @@ export const getStyles = ({ spacing, typography }: GrafanaTheme) => ({
`,
whatsNewLink: css`
height: 1em;
margin-top: ${spacing.xs};
margin-top: ${spacing(0.5)};
padding: 0;
`,
releaseDate: css`
font-size: ${typography.size.sm};
font-weight: ${typography.weight.regular};
font-weight: ${typography.fontWeightRegular};
`,
latestVersion: css`
margin-right: ${spacing.xs};
margin-right: ${spacing(0.5)};
`,
infoIcon: css`
margin-left: ${spacing.xs};
margin-right: ${spacing.sm};
margin-left: ${spacing(0.5)};
margin-right: ${spacing(1)};
`,
});
Original file line number Diff line number Diff line change
@@ -1,38 +1,39 @@
import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';

import { LatestInformation } from 'app/percona/shared/core/reducers/updates';

import { AvailableUpdate } from './AvailableUpdate';

const nextFullVersion = 'x.y.z-rc.j+1234567890';
const nextVersion = 'x.y.z';
const version = 'x.y.z';
const tag = 'percona/pmm-server:x.y.z-rc.j+1234567890';
const newsLink = 'https://percona.com';
const nextVersionDate = '23 Jun';
const timestamp = '2024-06-23T00:00:00.000Z';

const nextVersionDetails = {
nextVersionDate,
nextVersion,
nextFullVersion,
newsLink,
const nextVersion: LatestInformation = {
version,
tag,
timestamp,
};

describe('AvailableUpdate::', () => {
it('should show only the short version by default', () => {
render(<AvailableUpdate nextVersionDetails={nextVersionDetails} />);
render(<AvailableUpdate nextVersion={nextVersion} newsLink={newsLink} />);

expect(screen.getByTestId('update-latest-version').textContent).toEqual(nextVersion);
expect(screen.getByTestId('update-latest-version')).toHaveTextContent(version);
});

it('should show the news link if present', () => {
render(<AvailableUpdate nextVersionDetails={nextVersionDetails} />);
render(<AvailableUpdate nextVersion={nextVersion} newsLink={newsLink} />);

expect(screen.getByTestId('update-news-link')).toBeTruthy();
});

it('should show the full version on alt-click', () => {
render(<AvailableUpdate nextVersionDetails={nextVersionDetails} />);
render(<AvailableUpdate nextVersion={nextVersion} newsLink={newsLink} />);

fireEvent.click(screen.getByTestId('update-latest-section'), { altKey: true });

expect(screen.getByTestId('update-latest-version').textContent).toEqual(nextFullVersion);
expect(screen.getByTestId('update-latest-version')).toHaveTextContent(tag);
});
});
Loading
Loading