You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UpdateNotification.tsx 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import { Banner, Variant } from 'design-system';
  21. import { groupBy, isEmpty, mapValues } from 'lodash';
  22. import * as React from 'react';
  23. import DismissableAlert from '../../../components/ui/DismissableAlert';
  24. import SystemUpgradeButton from '../../../components/upgrade/SystemUpgradeButton';
  25. import { UpdateUseCase } from '../../../components/upgrade/utils';
  26. import { translate } from '../../../helpers/l10n';
  27. import { hasGlobalPermission } from '../../../helpers/users';
  28. import { useSystemUpgrades } from '../../../queries/system';
  29. import { AppState } from '../../../types/appstate';
  30. import { Permissions } from '../../../types/permissions';
  31. import { Dict } from '../../../types/types';
  32. import { CurrentUser, isLoggedIn } from '../../../types/users';
  33. import withAppStateContext from '../app-state/withAppStateContext';
  34. import withCurrentUserContext from '../current-user/withCurrentUserContext';
  35. import { isMinorUpdate, isPatchUpdate, isPreLTSUpdate, isPreviousLTSUpdate } from './helpers';
  36. const MAP_VARIANT: Dict<Variant> = {
  37. [UpdateUseCase.NewMinorVersion]: 'info',
  38. [UpdateUseCase.NewPatch]: 'warning',
  39. [UpdateUseCase.PreLTS]: 'warning',
  40. [UpdateUseCase.PreviousLTS]: 'error',
  41. };
  42. interface Props {
  43. dismissable: boolean;
  44. appState: AppState;
  45. currentUser: CurrentUser;
  46. }
  47. const VERSION_PARSER = /^(\d+)\.(\d+)(\.(\d+))?/;
  48. export function UpdateNotification({ dismissable, appState, currentUser }: Readonly<Props>) {
  49. const canUserSeeNotification =
  50. isLoggedIn(currentUser) && hasGlobalPermission(currentUser, Permissions.Admin);
  51. const regExpParsedVersion = VERSION_PARSER.exec(appState.version);
  52. const { data } = useSystemUpgrades({
  53. enabled: canUserSeeNotification && regExpParsedVersion !== null,
  54. });
  55. if (
  56. !canUserSeeNotification ||
  57. regExpParsedVersion === null ||
  58. data === undefined ||
  59. isEmpty(data.upgrades)
  60. ) {
  61. return null;
  62. }
  63. const { upgrades, latestLTS } = data;
  64. const parsedVersion = regExpParsedVersion
  65. .slice(1)
  66. .map(Number)
  67. .map((n) => (isNaN(n) ? 0 : n));
  68. const systemUpgrades = mapValues(
  69. groupBy(upgrades, (upgrade) => {
  70. const [major] = upgrade.version.split('.');
  71. return major;
  72. }),
  73. (upgrades) =>
  74. groupBy(upgrades, (upgrade) => {
  75. const [, minor] = upgrade.version.split('.');
  76. return minor;
  77. }),
  78. );
  79. let useCase = UpdateUseCase.NewMinorVersion;
  80. if (isPreviousLTSUpdate(parsedVersion, latestLTS, systemUpgrades)) {
  81. useCase = UpdateUseCase.PreviousLTS;
  82. } else if (isPreLTSUpdate(parsedVersion, latestLTS)) {
  83. useCase = UpdateUseCase.PreLTS;
  84. } else if (isPatchUpdate(parsedVersion, systemUpgrades)) {
  85. useCase = UpdateUseCase.NewPatch;
  86. } else if (isMinorUpdate(parsedVersion, systemUpgrades)) {
  87. useCase = UpdateUseCase.NewMinorVersion;
  88. }
  89. const latest = [...upgrades].sort(
  90. (upgrade1, upgrade2) =>
  91. new Date(upgrade2.releaseDate ?? '').getTime() -
  92. new Date(upgrade1.releaseDate ?? '').getTime(),
  93. )[0];
  94. const dismissKey = useCase + latest.version;
  95. return dismissable ? (
  96. <DismissableAlert
  97. alertKey={dismissKey}
  98. variant={MAP_VARIANT[useCase]}
  99. className={`it__promote-update-notification it__upgrade-prompt-${useCase}`}
  100. >
  101. {translate('admin_notification.update', useCase)}
  102. <SystemUpgradeButton
  103. systemUpgrades={upgrades}
  104. updateUseCase={useCase}
  105. latestLTS={latestLTS}
  106. />
  107. </DismissableAlert>
  108. ) : (
  109. <Banner variant={MAP_VARIANT[useCase]} className={`it__upgrade-prompt-${useCase}`}>
  110. {translate('admin_notification.update', useCase)}
  111. <SystemUpgradeButton
  112. systemUpgrades={upgrades}
  113. updateUseCase={useCase}
  114. latestLTS={latestLTS}
  115. />
  116. </Banner>
  117. );
  118. }
  119. export default withCurrentUserContext(withAppStateContext(UpdateNotification));