From 35ee1a94e9adcb757b656478523edd2df315f05f Mon Sep 17 00:00:00 2001 From: Philippe Perrin Date: Tue, 19 Jul 2022 10:46:34 +0200 Subject: [NO-JIRA] Simplify application initialization --- server/sonar-web/src/main/js/api/nav.ts | 42 -------- server/sonar-web/src/main/js/api/navigation.ts | 47 +++++++++ server/sonar-web/src/main/js/api/users.ts | 2 +- .../src/main/js/app/components/AdminContainer.tsx | 2 +- .../main/js/app/components/ComponentContainer.tsx | 2 +- .../__tests__/ComponentContainer-test.tsx | 4 +- .../app/components/app-state/AppStateContext.tsx | 4 +- server/sonar-web/src/main/js/app/index.ts | 116 +++------------------ .../src/main/js/app/utils/startReactApp.tsx | 9 +- .../src/main/js/apps/marketplace/EditionBoxes.tsx | 2 +- .../marketplace/__tests__/EditionBoxes-test.tsx | 2 +- .../projects/components/ApplicationCreation.tsx | 2 +- .../__tests__/ApplicationCreation-test.tsx | 4 +- .../apps/projectsManagement/ProjectRowActions.tsx | 2 +- .../__tests__/ProjectRowActions-test.tsx | 4 +- 15 files changed, 83 insertions(+), 161 deletions(-) delete mode 100644 server/sonar-web/src/main/js/api/nav.ts create mode 100644 server/sonar-web/src/main/js/api/navigation.ts diff --git a/server/sonar-web/src/main/js/api/nav.ts b/server/sonar-web/src/main/js/api/nav.ts deleted file mode 100644 index bca04cabf98..00000000000 --- a/server/sonar-web/src/main/js/api/nav.ts +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2022 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -import { throwGlobalError } from '../helpers/error'; -import { getJSON } from '../helpers/request'; -import { BranchParameters } from '../types/branch-like'; -import { Component, Extension } from '../types/types'; - -type NavComponent = Omit; - -export function getComponentNavigation( - data: { component: string } & BranchParameters -): Promise { - return getJSON('/api/navigation/component', data).catch(throwGlobalError); -} - -export function getMarketplaceNavigation(): Promise<{ serverId: string; ncloc: number }> { - return getJSON('/api/navigation/marketplace').catch(throwGlobalError); -} - -export function getSettingsNavigation(): Promise<{ - extensions: Extension[]; - showUpdateCenter: boolean; -}> { - return getJSON('/api/navigation/settings').catch(throwGlobalError); -} diff --git a/server/sonar-web/src/main/js/api/navigation.ts b/server/sonar-web/src/main/js/api/navigation.ts new file mode 100644 index 00000000000..69111204f41 --- /dev/null +++ b/server/sonar-web/src/main/js/api/navigation.ts @@ -0,0 +1,47 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { throwGlobalError } from '../helpers/error'; +import { getJSON } from '../helpers/request'; +import { AppState } from '../types/appstate'; +import { BranchParameters } from '../types/branch-like'; +import { Component, Extension } from '../types/types'; + +type NavComponent = Omit; + +export function getComponentNavigation( + data: { component: string } & BranchParameters +): Promise { + return getJSON('/api/navigation/component', data).catch(throwGlobalError); +} + +export function getMarketplaceNavigation(): Promise<{ serverId: string; ncloc: number }> { + return getJSON('/api/navigation/marketplace').catch(throwGlobalError); +} + +export function getSettingsNavigation(): Promise<{ + extensions: Extension[]; + showUpdateCenter: boolean; +}> { + return getJSON('/api/navigation/settings').catch(throwGlobalError); +} + +export function getGlobalNavigation(): Promise { + return getJSON('/api/navigation/global', undefined, true); +} diff --git a/server/sonar-web/src/main/js/api/users.ts b/server/sonar-web/src/main/js/api/users.ts index e512718770e..dae13297e64 100644 --- a/server/sonar-web/src/main/js/api/users.ts +++ b/server/sonar-web/src/main/js/api/users.ts @@ -23,7 +23,7 @@ import { IdentityProvider, Paging } from '../types/types'; import { CurrentUser, HomePage, NoticeType, User } from '../types/users'; export function getCurrentUser(): Promise { - return getJSON('/api/users/current'); + return getJSON('/api/users/current', undefined, true); } export function dismissNotification(notice: NoticeType) { diff --git a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx index de4a84124e4..0b5bcc72ab9 100644 --- a/server/sonar-web/src/main/js/app/components/AdminContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/AdminContainer.tsx @@ -20,7 +20,7 @@ import * as React from 'react'; import { Helmet } from 'react-helmet-async'; import { Outlet } from 'react-router-dom'; -import { getSettingsNavigation } from '../../api/nav'; +import { getSettingsNavigation } from '../../api/navigation'; import { getPendingPlugins } from '../../api/plugins'; import { getSystemStatus, waitSystemUPStatus } from '../../api/system'; import handleRequiredAuthorization from '../../app/utils/handleRequiredAuthorization'; diff --git a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx index 4d6d2f3fac0..2220ec14d6c 100644 --- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx @@ -24,7 +24,7 @@ import { getProjectAlmBinding, validateProjectAlmBinding } from '../../api/alm-s import { getBranches, getPullRequests } from '../../api/branches'; import { getAnalysisStatus, getTasksForComponent } from '../../api/ce'; import { getComponentData } from '../../api/components'; -import { getComponentNavigation } from '../../api/nav'; +import { getComponentNavigation } from '../../api/navigation'; import { Location, Router, withRouter } from '../../components/hoc/withRouter'; import { getBranchLikeQuery, diff --git a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx index 88f82cbab52..e7822a0a90c 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx +++ b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx @@ -23,7 +23,7 @@ import { getProjectAlmBinding, validateProjectAlmBinding } from '../../../api/al import { getBranches, getPullRequests } from '../../../api/branches'; import { getAnalysisStatus, getTasksForComponent } from '../../../api/ce'; import { getComponentData } from '../../../api/components'; -import { getComponentNavigation } from '../../../api/nav'; +import { getComponentNavigation } from '../../../api/navigation'; import { mockProjectAlmBindingConfigurationErrors } from '../../../helpers/mocks/alm-settings'; import { mockBranch, mockMainBranch, mockPullRequest } from '../../../helpers/mocks/branch-like'; import { mockComponent } from '../../../helpers/mocks/component'; @@ -65,7 +65,7 @@ jest.mock('../../../api/components', () => ({ getComponentData: jest.fn().mockResolvedValue({ component: { analysisDate: '2018-07-30' } }) })); -jest.mock('../../../api/nav', () => ({ +jest.mock('../../../api/navigation', () => ({ getComponentNavigation: jest.fn().mockResolvedValue({ breadcrumbs: [{ key: 'portfolioKey', name: 'portfolio', qualifier: 'VW' }], key: 'portfolioKey' diff --git a/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx b/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx index a8e130c84d2..d327bfb3aa1 100644 --- a/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx +++ b/server/sonar-web/src/main/js/app/components/app-state/AppStateContext.tsx @@ -21,7 +21,7 @@ import * as React from 'react'; import { AppState } from '../../../types/appstate'; -const defaultAppState = { +export const DEFAULT_APP_STATE = { authenticationError: false, authorizationError: false, edition: undefined, @@ -30,4 +30,4 @@ const defaultAppState = { settings: {}, version: '' }; -export const AppStateContext = React.createContext(defaultAppState); +export const AppStateContext = React.createContext(DEFAULT_APP_STATE); diff --git a/server/sonar-web/src/main/js/app/index.ts b/server/sonar-web/src/main/js/app/index.ts index a665c02f376..86c72242783 100644 --- a/server/sonar-web/src/main/js/app/index.ts +++ b/server/sonar-web/src/main/js/app/index.ts @@ -17,118 +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 { getGlobalNavigation } from '../api/navigation'; +import { getCurrentUser } from '../api/users'; import { installExtensionsHandler, installWebAnalyticsHandler } from '../helpers/extensionsHandler'; import { loadL10nBundle } from '../helpers/l10nBundle'; -import { HttpStatus, parseJSON, request } from '../helpers/request'; import { getBaseUrl, getSystemStatus } from '../helpers/system'; -import { AppState } from '../types/appstate'; -import { L10nBundle } from '../types/l10nBundle'; -import { CurrentUser } from '../types/users'; import './styles/sonar.ts'; installWebAnalyticsHandler(); +installExtensionsHandler(); +initApplication(); -if (isMainApp()) { - installExtensionsHandler(); - - loadAll(loadAppState, loadUser).then( - ([l10nBundle, user, appState, startReactApp]) => { - startReactApp(l10nBundle.locale, appState, user); - }, - error => { - if (isResponse(error) && error.status === HttpStatus.Unauthorized) { - redirectToLogin(); - } else { - logError(error); - } - } - ); -} else { - // login, maintenance or setup pages - - const appStateLoader = () => - loadAppState().catch(() => { - return { - edition: undefined, - productionDatabase: true, - qualifiers: [], - settings: {}, - version: '' - }; - }); - - loadAll(appStateLoader).then( - ([l10nBundle, _user, appState, startReactApp]) => { - startReactApp(l10nBundle.locale, appState); - }, - error => { - logError(error); - } - ); -} - -async function loadAll( - appStateLoader: () => Promise, - userLoader?: () => Promise -): Promise< - [ - Required, - CurrentUser | undefined, - AppState, - (lang: string, appState: AppState, currentUser?: CurrentUser) => void - ] -> { - const [l10nBundle, user, appState] = await Promise.all([ +async function initApplication() { + const [l10nBundle, currentUser, appState] = await Promise.all([ loadL10nBundle(), - userLoader ? userLoader() : undefined, - appStateLoader() - ]); - - const startReactApp = await loadApp(); - - return [l10nBundle, user, appState, startReactApp]; -} - -function loadUser() { - return request('/api/users/current') - .submit() - .then(checkStatus) - .then(parseJSON); -} - -function loadAppState() { - return request('/api/navigation/global') - .submit() - .then(checkStatus) - .then(parseJSON); -} - -function loadApp() { - return import(/* webpackChunkName: 'app' */ './utils/startReactApp').then(i => i.default); -} - -function checkStatus(response: Response) { - return new Promise((resolve, reject) => { - if (response.status >= 200 && response.status < 300) { - resolve(response); - } else { - reject(response); - } + isMainApp() ? getCurrentUser() : undefined, + isMainApp() ? getGlobalNavigation() : undefined + ]).catch(error => { + // eslint-disable-next-line no-console + console.error('Application failed to start', error); + throw error; }); -} - -function isResponse(error: any): error is Response { - return typeof error.status === 'number'; -} - -function redirectToLogin() { - const returnTo = window.location.pathname + window.location.search + window.location.hash; - window.location.href = `${getBaseUrl()}/sessions/new?return_to=${encodeURIComponent(returnTo)}`; -} -function logError(error: any) { - // eslint-disable-next-line no-console - console.error('Application failed to start!', error); + const startReactApp = await import('./utils/startReactApp').then(i => i.default); + startReactApp(l10nBundle.locale, currentUser, appState); } function isMainApp() { diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx index b5387cfa9ec..3bd41cee3be 100644 --- a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx +++ b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx @@ -64,6 +64,7 @@ import { AppState } from '../../types/appstate'; import { CurrentUser } from '../../types/users'; import AdminContainer from '../components/AdminContainer'; import App from '../components/App'; +import { DEFAULT_APP_STATE } from '../components/app-state/AppStateContext'; import AppStateContextProvider from '../components/app-state/AppStateContextProvider'; import ComponentContainer from '../components/ComponentContainer'; import CurrentUserContextProvider from '../components/current-user/CurrentUserContextProvider'; @@ -223,14 +224,18 @@ function renderAdminRoutes() { ); } -export default function startReactApp(lang: string, appState: AppState, currentUser?: CurrentUser) { +export default function startReactApp( + lang: string, + currentUser?: CurrentUser, + appState?: AppState +) { exportModulesAsGlobals(); const el = document.getElementById('content'); render( - + diff --git a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx index 6270c9faecb..f4c18553a1e 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/EditionBoxes.tsx @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { getMarketplaceNavigation } from '../../api/nav'; +import { getMarketplaceNavigation } from '../../api/navigation'; import { getAllEditionsAbove } from '../../helpers/editions'; import { EditionKey } from '../../types/editions'; import EditionBox from './components/EditionBox'; diff --git a/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx b/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx index 7bc48dbcaa8..b1568cb8b73 100644 --- a/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx +++ b/server/sonar-web/src/main/js/apps/marketplace/__tests__/EditionBoxes-test.tsx @@ -22,7 +22,7 @@ import * as React from 'react'; import { EditionKey } from '../../../types/editions'; import EditionBoxes from '../EditionBoxes'; -jest.mock('../../../api/nav', () => ({ +jest.mock('../../../api/navigation', () => ({ getMarketplaceNavigation: jest.fn().mockResolvedValue({}) })); diff --git a/server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx b/server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx index f58be34596e..11bf1c33434 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/ApplicationCreation.tsx @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { getComponentNavigation } from '../../../api/nav'; +import { getComponentNavigation } from '../../../api/navigation'; import withAppStateContext from '../../../app/components/app-state/withAppStateContext'; import withCurrentUserContext from '../../../app/components/current-user/withCurrentUserContext'; import CreateApplicationForm from '../../../app/components/extensions/CreateApplicationForm'; diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ApplicationCreation-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ApplicationCreation-test.tsx index ecdfa1b29fe..163991f0357 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ApplicationCreation-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ApplicationCreation-test.tsx @@ -19,7 +19,7 @@ */ import { shallow, ShallowWrapper } from 'enzyme'; import * as React from 'react'; -import { getComponentNavigation } from '../../../../api/nav'; +import { getComponentNavigation } from '../../../../api/navigation'; import CreateApplicationForm from '../../../../app/components/extensions/CreateApplicationForm'; import { Button } from '../../../../components/controls/buttons'; import { mockAppState, mockLoggedInUser, mockRouter } from '../../../../helpers/testMocks'; @@ -27,7 +27,7 @@ import { queryToSearch } from '../../../../helpers/urls'; import { ComponentQualifier } from '../../../../types/component'; import { ApplicationCreation, ApplicationCreationProps } from '../ApplicationCreation'; -jest.mock('../../../../api/nav', () => ({ +jest.mock('../../../../api/navigation', () => ({ getComponentNavigation: jest.fn().mockResolvedValue({}) })); diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx index 6a592edf6d9..64f0274f234 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/ProjectRowActions.tsx @@ -19,7 +19,7 @@ */ import * as React from 'react'; import { Project } from '../../api/components'; -import { getComponentNavigation } from '../../api/nav'; +import { getComponentNavigation } from '../../api/navigation'; import ActionsDropdown, { ActionsDropdownItem } from '../../components/controls/ActionsDropdown'; import DeferredSpinner from '../../components/ui/DeferredSpinner'; import { translate } from '../../helpers/l10n'; diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx index 95182dc9f18..8a8b3f2e706 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRowActions-test.tsx @@ -19,12 +19,12 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { getComponentNavigation } from '../../../api/nav'; +import { getComponentNavigation } from '../../../api/navigation'; import { mockLoggedInUser } from '../../../helpers/testMocks'; import { click, waitAndUpdate } from '../../../helpers/testUtils'; import ProjectRowActions, { Props } from '../ProjectRowActions'; -jest.mock('../../../api/nav', () => ({ +jest.mock('../../../api/navigation', () => ({ getComponentNavigation: jest.fn().mockResolvedValue({}) })); -- cgit v1.2.3