Browse Source

SONAR-21909 Update Banner and Dialog with new LTA wording

tags/10.5.0.89998
stanislavh 1 month ago
parent
commit
fde088bc4d

+ 2
- 1
server/sonar-web/src/main/js/api/system.ts View 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');

+ 4
- 0
server/sonar-web/src/main/js/app/components/app-state/withAppStateContext.tsx View File

@@ -43,3 +43,7 @@ export default function withAppStateContext<P>(
}
};
}

export function useAppState() {
return React.useContext(AppStateContext);
}

+ 24
- 33
server/sonar-web/src/main/js/app/components/update-notification/UpdateNotification.tsx View 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));

+ 12
- 30
server/sonar-web/src/main/js/app/components/update-notification/helpers.ts View File

@@ -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<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',
};

+ 1
- 1
server/sonar-web/src/main/js/apps/system/components/SystemApp.tsx View 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

+ 4
- 4
server/sonar-web/src/main/js/components/upgrade/SystemUpgradeButton.tsx View 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}
/>
)}

+ 15
- 22
server/sonar-web/src/main/js/components/upgrade/SystemUpgradeForm.tsx View File

@@ -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<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);

+ 4
- 4
server/sonar-web/src/main/js/components/upgrade/SystemUpgradeItem.tsx View 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');
}

+ 6
- 5
server/sonar-web/src/main/js/components/upgrade/__tests__/SystemUpgrade-test.tsx View 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' },

+ 2
- 3
server/sonar-web/src/main/js/components/upgrade/utils.ts View 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+))?/;

+ 3
- 4
sonar-core/src/main/resources/org/sonar/l10n/core.properties View 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.

Loading…
Cancel
Save