]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-21909 Update Banner and Dialog with new LTA wording
authorstanislavh <stanislav.honcharov@sonarsource.com>
Wed, 27 Mar 2024 16:03:03 +0000 (17:03 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 3 Apr 2024 20:02:41 +0000 (20:02 +0000)
server/sonar-web/src/main/js/api/system.ts
server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx
server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx
server/sonar-web/src/main/js/app/components/update-notification/helpers.ts
server/sonar-web/src/main/js/apps/system/components/SystemApp.tsx
server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx
server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx
server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx
server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgrade-test.tsx
server/sonar-web/src/main/js/components/upgrade/utils.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 235adc00217f3605398ea3e5c9ad6cf71d9a6ae8..21202786a953a3ddce87e5578a368b20c68bf805 100644 (file)
@@ -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');
index cb1ca7e2f933a6d6f5c5f6cbfd381ce6d9a5b7a5..b1236d9bc1efe3f2968700a10ae176825ff2cca6 100644 (file)
@@ -43,3 +43,7 @@ export default function withAppStateContext<P>(
     }
   };
 }
+
+export function useAppState() {
+  return React.useContext(AppStateContext);
+}
index 03cb3bbc19c87a75b687cd00bcc2f6e086d7246d..5a8bb6a20f7d0e72d94e7de3b6645605450f13a2 100644 (file)
@@ -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<Variant> = {
-  [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<Props>) {
+export default function UpdateNotification({ dismissable }: Readonly<Props>) {
+  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 ? (
     <DismissableAlert
       alertKey={dismissKey}
-      variant={MAP_VARIANT[useCase]}
+      variant={BANNER_VARIANT[useCase]}
       className={`it__promote-update-notification it__upgrade-prompt-${useCase}`}
     >
       {translate('admin_notification.update', useCase)}
       <SystemUpgradeButton
         systemUpgrades={upgrades}
         updateUseCase={useCase}
-        latestLTS={latestLTS}
+        latestLTA={latestLTA}
       />
     </DismissableAlert>
   ) : (
-    <Banner variant={MAP_VARIANT[useCase]} className={`it__upgrade-prompt-${useCase}`}>
+    <Banner variant={BANNER_VARIANT[useCase]} className={`it__upgrade-prompt-${useCase}`}>
       {translate('admin_notification.update', useCase)}
       <SystemUpgradeButton
         systemUpgrades={upgrades}
         updateUseCase={useCase}
-        latestLTS={latestLTS}
+        latestLTA={latestLTA}
       />
     </Banner>
   );
 }
-
-export default withCurrentUserContext(withAppStateContext(UpdateNotification));
index 0442139d263c134214594c5c3bc3aa10524909cc..89d700f8a5d96dfe0ae1d07e440924ae80361e92 100644 (file)
  * 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<string, SystemUpgrade[]>;
 };
 
-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<Variant> = {
+  [UpdateUseCase.NewVersion]: 'info',
+  [UpdateUseCase.CurrentVersionInactive]: 'error',
+  [UpdateUseCase.NewPatch]: 'warning',
+};
index 2d8b64933490dc7cee85ebfcce7f015d84e158f2..6afb506ca263f65735804cac4bf0f33e729e5d4c 100644 (file)
@@ -129,7 +129,7 @@ class SystemApp extends React.PureComponent<Props, State> {
         <Helmet defer={false} title={translate('system_info.page')} />
         <PageContentFontWrapper className="sw-body-sm sw-pb-8">
           <div>
-            <UpdateNotification dismissable={false} />
+            <UpdateNotification />
           </div>
           {sysInfoData && (
             <PageHeader
index 211a546eb5c7d7686bb5eba8b33ba0be5b7464cb..9607fa5e7bbf41dad67b0ea2f9453fd166951f94 100644 (file)
@@ -25,13 +25,13 @@ import SystemUpgradeForm from './SystemUpgradeForm';
 import { groupUpgrades, sortUpgrades, UpdateUseCase } from './utils';
 
 interface Props {
-  latestLTS: string;
+  latestLTA: string;
   systemUpgrades: SystemUpgrade[];
-  updateUseCase?: UpdateUseCase;
+  updateUseCase: UpdateUseCase;
 }
 
 export default function SystemUpgradeButton(props: Readonly<Props>) {
-  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<Props>) {
         <SystemUpgradeForm
           onClose={closeSystemUpgradeForm}
           systemUpgrades={groupUpgrades(sortUpgrades(systemUpgrades))}
-          latestLTS={latestLTS}
+          latestLTA={latestLTA}
           updateUseCase={updateUseCase}
         />
       )}
index 7d65a0dc3aa4323f08254dca92441311110d863f..c9772ff86fa1969468def99abdf90869410e4583 100644 (file)
  * 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<Props>) {
-  const { appState, latestLTS, onClose, updateUseCase, systemUpgrades } = props;
+export default function SystemUpgradeForm(props: Readonly<Props>) {
+  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<Props>) {
 
     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<Props>) {
       onClose={onClose}
       body={
         <>
-          {alertVariant && updateUseCase && (
+          {alertVariant && (
             <FlagMessage variant={alertVariant} className={`it__upgrade-alert-${updateUseCase}`}>
               {translate('admin_notification.update', updateUseCase)}
             </FlagMessage>
@@ -92,7 +87,7 @@ export function SystemUpgradeForm(props: Readonly<Props>) {
               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<Props>) {
     />
   );
 }
-
-export default withAppStateContext(SystemUpgradeForm);
index 5fbb2353f222cb9b1ad7bcaf6040f7ceb6aaf22f..ba5e7f64a80df2102b8a63832ca59785fbda4ba4 100644 (file)
@@ -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');
   }
index 29a1c4d35f283ac10f69b18edde886b62fd1d942..55bd3e211e9658fe42eeb69c673c7b0514152239 100644 (file)
@@ -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(
     <SystemUpgradeButton
-      latestLTS="9.9"
+      updateUseCase={UpdateUseCase.NewVersion}
+      latestLTA="9.9"
       systemUpgrades={[
         { downloadUrl: 'eight', version: '9.8' },
         { downloadUrl: 'lts', version: '9.9' },
index 05bba9b092f89ebe43c6e63178ebba6a0857c946..625dc4a1e1278de7244b527cab3378c6ca07b533 100644 (file)
@@ -21,10 +21,9 @@ import { groupBy, sortBy } from 'lodash';
 import { SystemUpgrade } from '../../types/system';
 
 export enum UpdateUseCase {
-  NewMinorVersion = 'new_minor_version',
+  NewVersion = 'new_version',
+  CurrentVersionInactive = 'current_version_inactive',
   NewPatch = 'new_patch',
-  PreLTS = 'pre_lts',
-  PreviousLTS = 'previous_lts',
 }
 
 export const SYSTEM_VERSION_REGEXP = /^(\d+)\.(\d+)(\.(\d+))?/;
index 39fffc2a5b73a10e05941674f35ade0f19b926cf..f5c5d4cabd1849f73317496ae08e589b5e693090 100644 (file)
@@ -494,10 +494,9 @@ qualifier.description.APP=Single-level aggregation with a technical focus and a
 # Admin notification
 #
 #------------------------------------------------------------------------------
-admin_notification.update.new_minor_version=There’s a new version of SonarQube available. Update to enjoy the latest updates and features.
 admin_notification.update.new_patch=There’s an update available for your SonarQube instance. Please update to make sure you benefit from the latest security and bug fixes.
-admin_notification.update.pre_lts=You’re running a version of SonarQube that has reached end of life. Please upgrade to a supported version at your earliest convenience.
-admin_notification.update.previous_lts=You’re running a version of SonarQube that is past end of life. Please upgrade to a supported version immediately.
+admin_notification.update.new_version=There’s a new version of SonarQube available. Upgrade to the latest active version to access new updates and features.
+admin_notification.update.current_version_inactive=You’re running a version of SonarQube that is no longer active. Please upgrade to an active version immediately.
 
 #------------------------------------------------------------------------------
 #
@@ -3837,7 +3836,7 @@ system.hide_intermediate_versions=Hide intermediate versions
 system.how_to_upgrade=How to upgrade?
 system.latest_version=Latest Version
 system.latest_patch=Patch Release
-system.lts_version=Latest LTS Version
+system.lta_version=Latest LTA Version
 system.log_level.warning=This level has performance impacts, please make sure to get back to INFO level once your investigation is done. Please note that when the server is restarted, logging will revert to the level configured in sonar.properties.
 system.log_level.warning.short=Current logs level has performance impacts, get back to INFO level.
 system.log_level.info=Your selection does not affect the Search Engine.