From fde088bc4d7045c4baeb58f15aaea971d983cb7a Mon Sep 17 00:00:00 2001 From: stanislavh Date: Wed, 27 Mar 2024 17:03:03 +0100 Subject: [PATCH] SONAR-21909 Update Banner and Dialog with new LTA wording --- server/sonar-web/src/main/js/api/system.ts | 3 +- .../app-state/withAppStateContext.tsx | 4 ++ .../UpdateNotification.tsx | 57 ++++++++----------- .../components/update-notification/helpers.ts | 42 ++++---------- .../js/apps/system/components/SystemApp.tsx | 2 +- .../upgrade/SystemUpgradeButton.tsx | 8 +-- .../components/upgrade/SystemUpgradeForm.tsx | 37 +++++------- .../components/upgrade/SystemUpgradeItem.tsx | 8 +-- .../upgrade/__tests__/SystemUpgrade-test.tsx | 11 ++-- .../src/main/js/components/upgrade/utils.ts | 5 +- .../resources/org/sonar/l10n/core.properties | 7 +-- 11 files changed, 77 insertions(+), 107 deletions(-) diff --git a/server/sonar-web/src/main/js/api/system.ts b/server/sonar-web/src/main/js/api/system.ts index 235adc00217..21202786a95 100644 --- a/server/sonar-web/src/main/js/api/system.ts +++ b/server/sonar-web/src/main/js/api/system.ts @@ -36,7 +36,8 @@ export function getSystemStatus(): Promise<{ id: string; version: string; status export function getSystemUpgrades(): Promise<{ upgrades: SystemUpgrade[]; - latestLTS: string; + latestLTA: string; + installedVersionActive: boolean; updateCenterRefresh: string; }> { return getJSON('/api/system/upgrades'); diff --git a/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx b/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx index cb1ca7e2f93..b1236d9bc1e 100644 --- a/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx +++ b/server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx @@ -43,3 +43,7 @@ export default function withAppStateContext

( } }; } + +export function useAppState() { + return React.useContext(AppStateContext); +} diff --git a/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx b/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx index 03cb3bbc19c..5a8bb6a20f7 100644 --- a/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx +++ b/server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { Banner, Variant } from 'design-system'; +import { Banner } from 'design-system'; import { groupBy, isEmpty, mapValues } from 'lodash'; import * as React from 'react'; import DismissableAlert from '../../../components/ui/DismissableAlert'; @@ -26,33 +26,26 @@ import { UpdateUseCase } from '../../../components/upgrade/utils'; import { translate } from '../../../helpers/l10n'; import { hasGlobalPermission } from '../../../helpers/users'; import { useSystemUpgrades } from '../../../queries/system'; -import { AppState } from '../../../types/appstate'; import { Permissions } from '../../../types/permissions'; -import { Dict } from '../../../types/types'; -import { CurrentUser, isLoggedIn } from '../../../types/users'; -import withAppStateContext from '../app-state/withAppStateContext'; -import withCurrentUserContext from '../current-user/withCurrentUserContext'; -import { isMinorUpdate, isPatchUpdate, isPreLTSUpdate, isPreviousLTSUpdate } from './helpers'; - -const MAP_VARIANT: Dict = { - [UpdateUseCase.NewMinorVersion]: 'info', - [UpdateUseCase.NewPatch]: 'warning', - [UpdateUseCase.PreLTS]: 'warning', - [UpdateUseCase.PreviousLTS]: 'error', -}; +import { isLoggedIn } from '../../../types/users'; +import { useAppState } from '../app-state/withAppStateContext'; +import { useCurrentUser } from '../current-user/CurrentUserContext'; +import { BANNER_VARIANT, isCurrentVersionLTA, isMinorUpdate, isPatchUpdate } from './helpers'; interface Props { - dismissable: boolean; - appState: AppState; - currentUser: CurrentUser; + dismissable?: boolean; } const VERSION_PARSER = /^(\d+)\.(\d+)(\.(\d+))?/; -export function UpdateNotification({ dismissable, appState, currentUser }: Readonly) { +export default function UpdateNotification({ dismissable }: Readonly) { + const appState = useAppState(); + const { currentUser } = useCurrentUser(); + const canUserSeeNotification = isLoggedIn(currentUser) && hasGlobalPermission(currentUser, Permissions.Admin); const regExpParsedVersion = VERSION_PARSER.exec(appState.version); + const { data } = useSystemUpgrades({ enabled: canUserSeeNotification && regExpParsedVersion !== null, }); @@ -66,7 +59,8 @@ export function UpdateNotification({ dismissable, appState, currentUser }: Reado return null; } - const { upgrades, latestLTS } = data; + const { upgrades, installedVersionActive, latestLTA } = data; + const parsedVersion = regExpParsedVersion .slice(1) .map(Number) @@ -84,16 +78,15 @@ export function UpdateNotification({ dismissable, appState, currentUser }: Reado }), ); - let useCase = UpdateUseCase.NewMinorVersion; + let useCase = UpdateUseCase.NewVersion; - if (isPreviousLTSUpdate(parsedVersion, latestLTS, systemUpgrades)) { - useCase = UpdateUseCase.PreviousLTS; - } else if (isPreLTSUpdate(parsedVersion, latestLTS)) { - useCase = UpdateUseCase.PreLTS; - } else if (isPatchUpdate(parsedVersion, systemUpgrades)) { + if (!installedVersionActive) { + useCase = UpdateUseCase.CurrentVersionInactive; + } else if ( + isPatchUpdate(parsedVersion, systemUpgrades) && + (isCurrentVersionLTA(parsedVersion, latestLTA) || !isMinorUpdate(parsedVersion, systemUpgrades)) + ) { useCase = UpdateUseCase.NewPatch; - } else if (isMinorUpdate(parsedVersion, systemUpgrades)) { - useCase = UpdateUseCase.NewMinorVersion; } const latest = [...upgrades].sort( @@ -107,26 +100,24 @@ export function UpdateNotification({ dismissable, appState, currentUser }: Reado return dismissable ? ( {translate('admin_notification.update', useCase)} ) : ( - + {translate('admin_notification.update', useCase)} ); } - -export default withCurrentUserContext(withAppStateContext(UpdateNotification)); diff --git a/server/sonar-web/src/main/js/app/components/update-notification/helpers.ts b/server/sonar-web/src/main/js/app/components/update-notification/helpers.ts index 0442139d263..89d700f8a5d 100644 --- a/server/sonar-web/src/main/js/app/components/update-notification/helpers.ts +++ b/server/sonar-web/src/main/js/app/components/update-notification/helpers.ts @@ -18,44 +18,20 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { Variant } from 'design-system'; import { isEmpty } from 'lodash'; -import { sortUpgrades } from '../../../components/upgrade/utils'; +import { UpdateUseCase, sortUpgrades } from '../../../components/upgrade/utils'; import { SystemUpgrade } from '../../../types/system'; - -const MONTH_BEFOR_PREVIOUS_LTS_NOTIFICATION = 6; +import { Dict } from '../../../types/types'; type GroupedSystemUpdate = { [x: string]: Record; }; -export const isPreLTSUpdate = (parsedVersion: number[], latestLTS: string) => { +export const isCurrentVersionLTA = (parsedVersion: number[], latestLTS: string) => { const [currentMajor, currentMinor] = parsedVersion; const [ltsMajor, ltsMinor] = latestLTS.split('.').map(Number); - return currentMajor < ltsMajor || (currentMajor === ltsMajor && currentMinor < ltsMinor); -}; - -export const isPreviousLTSUpdate = ( - parsedVersion: number[], - latestLTS: string, - systemUpgrades: GroupedSystemUpdate, -) => { - const [ltsMajor, ltsMinor] = latestLTS.split('.').map(Number); - let ltsOlderThan6Month = false; - const beforeLts = isPreLTSUpdate(parsedVersion, latestLTS); - if (beforeLts) { - const allLTS = sortUpgrades(systemUpgrades[ltsMajor][ltsMinor]); - const ltsReleaseDate = new Date(allLTS[allLTS.length - 1]?.releaseDate ?? ''); - if (isNaN(ltsReleaseDate.getTime())) { - // We can not parse the LTS date. - // It is unlikly that this could happen but consider LTS to be old. - return true; - } - ltsOlderThan6Month = - ltsReleaseDate.setMonth(ltsReleaseDate.getMonth() + MONTH_BEFOR_PREVIOUS_LTS_NOTIFICATION) - - Date.now() < - 0; - } - return ltsOlderThan6Month && beforeLts; + return currentMajor === ltsMajor && currentMinor === ltsMinor; }; export const isMinorUpdate = (parsedVersion: number[], systemUpgrades: GroupedSystemUpdate) => { @@ -69,7 +45,7 @@ export const isMinorUpdate = (parsedVersion: number[], systemUpgrades: GroupedSy export const isPatchUpdate = (parsedVersion: number[], systemUpgrades: GroupedSystemUpdate) => { const [currentMajor, currentMinor, currentPatch] = parsedVersion; const allMinor = systemUpgrades[currentMajor]; - const allPatch = sortUpgrades(allMinor[currentMinor] || []); + const allPatch = sortUpgrades(allMinor?.[currentMinor] ?? []); if (!isEmpty(allPatch)) { const [, , latestPatch] = allPatch[0].version.split('.').map(Number); @@ -79,3 +55,9 @@ export const isPatchUpdate = (parsedVersion: number[], systemUpgrades: GroupedSy } return false; }; + +export const BANNER_VARIANT: Dict = { + [UpdateUseCase.NewVersion]: 'info', + [UpdateUseCase.CurrentVersionInactive]: 'error', + [UpdateUseCase.NewPatch]: 'warning', +}; diff --git a/server/sonar-web/src/main/js/apps/system/components/SystemApp.tsx b/server/sonar-web/src/main/js/apps/system/components/SystemApp.tsx index 2d8b6493349..6afb506ca26 100644 --- a/server/sonar-web/src/main/js/apps/system/components/SystemApp.tsx +++ b/server/sonar-web/src/main/js/apps/system/components/SystemApp.tsx @@ -129,7 +129,7 @@ class SystemApp extends React.PureComponent {

- +
{sysInfoData && ( ) { - const { latestLTS, systemUpgrades, updateUseCase } = props; + const { latestLTA, systemUpgrades, updateUseCase } = props; const [isSystemUpgradeFormOpen, setSystemUpgradeFormOpen] = React.useState(false); @@ -52,7 +52,7 @@ export default function SystemUpgradeButton(props: Readonly) { )} diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx index 7d65a0dc3aa..c9772ff86fa 100644 --- a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx +++ b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx @@ -17,35 +17,30 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { FlagMessage, Link, Modal, Variant } from 'design-system'; +import { FlagMessage, Link, Modal } from 'design-system'; import { filter, flatMap, isEmpty, negate } from 'lodash'; import * as React from 'react'; -import withAppStateContext from '../../app/components/app-state/withAppStateContext'; +import { useAppState } from '../../app/components/app-state/withAppStateContext'; +import { BANNER_VARIANT } from '../../app/components/update-notification/helpers'; import { translate } from '../../helpers/l10n'; -import { AppState } from '../../types/appstate'; import { SystemUpgrade } from '../../types/system'; import SystemUpgradeItem from './SystemUpgradeItem'; import { SYSTEM_VERSION_REGEXP, UpdateUseCase } from './utils'; interface Props { - appState: AppState; onClose: () => void; systemUpgrades: SystemUpgrade[][]; - latestLTS: string; - updateUseCase?: UpdateUseCase; + latestLTA: string; + updateUseCase: UpdateUseCase; } -const MAP_ALERT: { [key in UpdateUseCase]?: Variant } = { - [UpdateUseCase.NewPatch]: 'warning', - [UpdateUseCase.PreLTS]: 'warning', - [UpdateUseCase.PreviousLTS]: 'error', -}; - -export function SystemUpgradeForm(props: Readonly) { - const { appState, latestLTS, onClose, updateUseCase, systemUpgrades } = props; +export default function SystemUpgradeForm(props: Readonly) { + const appState = useAppState(); + const { latestLTA, onClose, updateUseCase, systemUpgrades } = props; let systemUpgradesWithPatch: SystemUpgrade[][] = []; - const alertVariant = updateUseCase ? MAP_ALERT[updateUseCase] : undefined; + const alertVariant = + updateUseCase !== UpdateUseCase.NewVersion ? BANNER_VARIANT[updateUseCase] : undefined; const header = translate('system.system_upgrade'); const parsedVersion = SYSTEM_VERSION_REGEXP.exec(appState.version); let patches: SystemUpgrade[] = []; @@ -65,12 +60,12 @@ export function SystemUpgradeForm(props: Readonly) { systemUpgradesWithPatch.push(patches); } else { - let untilLTS = false; + let untilLTA = false; for (const upgrades of systemUpgrades) { - if (untilLTS === false) { + if (untilLTA === false) { systemUpgradesWithPatch.push(upgrades); - untilLTS = upgrades.some((upgrade) => upgrade.version.startsWith(latestLTS)); + untilLTA = upgrades.some((upgrade) => upgrade.version.startsWith(latestLTA)); } } } @@ -81,7 +76,7 @@ export function SystemUpgradeForm(props: Readonly) { onClose={onClose} body={ <> - {alertVariant && updateUseCase && ( + {alertVariant && ( {translate('admin_notification.update', updateUseCase)} @@ -92,7 +87,7 @@ export function SystemUpgradeForm(props: Readonly) { key={upgrades[upgrades.length - 1].version} systemUpgrades={upgrades} isPatch={upgrades === patches} - isLTSVersion={upgrades.some((upgrade) => upgrade.version.startsWith(latestLTS))} + isLTAVersion={upgrades.some((upgrade) => upgrade.version.startsWith(latestLTA))} /> ))} @@ -106,5 +101,3 @@ export function SystemUpgradeForm(props: Readonly) { /> ); } - -export default withAppStateContext(SystemUpgradeForm); diff --git a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx index 5fbb2353f22..ba5e7f64a80 100644 --- a/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx +++ b/server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx @@ -34,21 +34,21 @@ import SystemUpgradeIntermediate from './SystemUpgradeIntermediate'; export interface SystemUpgradeItemProps { edition: EditionKey | undefined; - isLTSVersion: boolean; + isLTAVersion: boolean; isPatch: boolean; systemUpgrades: SystemUpgrade[]; } export default function SystemUpgradeItem(props: SystemUpgradeItemProps) { - const { edition, isPatch, isLTSVersion, systemUpgrades } = props; + const { edition, isPatch, isLTAVersion, systemUpgrades } = props; const lastUpgrade = systemUpgrades[0]; const downloadUrl = getEditionDownloadUrl( getEdition(edition || EditionKey.community), lastUpgrade, ); let header = translate('system.latest_version'); - if (isLTSVersion) { - header = translate('system.lts_version'); + if (isLTAVersion) { + header = translate('system.lta_version'); } else if (isPatch) { header = translate('system.latest_patch'); } diff --git a/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgrade-test.tsx b/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgrade-test.tsx index 29a1c4d35f2..55bd3e211e9 100644 --- a/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgrade-test.tsx +++ b/server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgrade-test.tsx @@ -31,7 +31,7 @@ const ui = { header: byRole('heading', { name: 'system.system_upgrade' }), downloadLink: byRole('link', { name: /system.see_sonarqube_downloads/ }), - ltsVersionHeader: byRole('heading', { name: /system.lts_version/ }), + ltaVersionHeader: byRole('heading', { name: /system.lta_version/ }), newPatchWarning: byText(/admin_notification.update/), }; @@ -44,7 +44,7 @@ it('should render properly', async () => { await user.click(ui.learnMoreButton.get()); expect(ui.header.get()).toBeInTheDocument(); - expect(ui.ltsVersionHeader.get()).toBeInTheDocument(); + expect(ui.ltaVersionHeader.get()).toBeInTheDocument(); expect(ui.downloadLink.get()).toBeInTheDocument(); }); @@ -54,7 +54,7 @@ it('should render properly for new patch', async () => { renderSystemUpgradeButton( { updateUseCase: UpdateUseCase.NewPatch, - latestLTS: '9.9', + latestLTA: '9.9', systemUpgrades: [{ downloadUrl: '', version: '9.9.1' }], }, '9.9', @@ -64,7 +64,7 @@ it('should render properly for new patch', async () => { expect(ui.header.get()).toBeInTheDocument(); expect(ui.newPatchWarning.get()).toBeInTheDocument(); - expect(ui.ltsVersionHeader.get()).toBeInTheDocument(); + expect(ui.ltaVersionHeader.get()).toBeInTheDocument(); expect(ui.downloadLink.get()).toBeInTheDocument(); }); @@ -74,7 +74,8 @@ function renderSystemUpgradeButton( ) { renderComponent(