Skip to content

Commit

Permalink
PMM-13386 Link to update page from update panel
Browse files Browse the repository at this point in the history
  • Loading branch information
matejkubinec committed Sep 25, 2024
1 parent 91fd218 commit 9d46465
Show file tree
Hide file tree
Showing 48 changed files with 192 additions and 1,526 deletions.
4 changes: 2 additions & 2 deletions public/app/percona/shared/services/updates/Updates.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ import { api } from '../../helpers/api';
import { CheckUpdatesBody, CheckUpdatesResponse } from './Updates.types';

export const UpdatesService = {
getCurrentVersion: (body: CheckUpdatesBody = { force: false }) =>
api.post<CheckUpdatesResponse, CheckUpdatesBody>('/v1/Updates/Check', body, true),
getCurrentVersion: (params: CheckUpdatesBody = { force: false }) =>
api.get<CheckUpdatesResponse, CheckUpdatesBody>('/v1/server/updates', true, { params }),
};
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);
});
});
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import React, { FC } from 'react';

import { useStyles, Icon, LinkButton, Tooltip } from '@grafana/ui';
import { Icon, LinkButton, Tooltip, useStyles2 } from '@grafana/ui';

import { useToggleOnAltClick } from '../../hooks';
import { AvailableUpdateProps } from '../../types';

import { Messages } from './AvailableUpdate.messages';
import { getStyles } from './AvailableUpdate.styles';
import { AvailableUpdateProps } from './AvailableUpdate.types';

export const AvailableUpdate: FC<AvailableUpdateProps> = ({ nextVersionDetails }) => {
const styles = useStyles(getStyles);
export const AvailableUpdate: FC<AvailableUpdateProps> = ({ nextVersion, newsLink }) => {
const styles = useStyles2(getStyles);
const [showFullVersion, handleToggleShowFullVersion] = useToggleOnAltClick(false);

const { nextVersionDate, nextVersion, nextFullVersion, newsLink } = nextVersionDetails;

return (
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
<section
Expand All @@ -25,10 +23,10 @@ export const AvailableUpdate: FC<AvailableUpdateProps> = ({ nextVersionDetails }
{Messages.availableVersion}
:&nbsp;
<span data-testid="update-latest-version" className={styles.latestVersion}>
{showFullVersion ? nextFullVersion : nextVersion}
{showFullVersion ? nextVersion?.tag : nextVersion?.version}
</span>
<span data-testid="update-latest-release-date" className={styles.releaseDate}>
({nextVersionDate})
({nextVersion?.timestamp})
<Tooltip content={Messages.tooltip} data-testid="update-published-date-info">
<Icon name="info-circle" className={styles.infoIcon} />
</Tooltip>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { LatestInformation } from 'app/percona/shared/core/reducers/updates';

export interface AvailableUpdateProps {
nextVersion?: LatestInformation;
newsLink?: string;
}
Loading

0 comments on commit 9d46465

Please sign in to comment.