diff options
author | Wouter Admiraal <wouter.admiraal@sonarsource.com> | 2022-01-25 12:01:52 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2022-01-27 20:03:05 +0000 |
commit | 68c3027b07fe706a58ac69dcb356dc86d9b3c1a0 (patch) | |
tree | a355c5162a0f48d031042b780d070522f5da9511 /server/sonar-web | |
parent | 1d409d5db753fe003513f4e1a8d99a0fe6c1becc (diff) | |
download | sonarqube-68c3027b07fe706a58ac69dcb356dc86d9b3c1a0.tar.gz sonarqube-68c3027b07fe706a58ac69dcb356dc86d9b3c1a0.zip |
SONAR-15782 Move application report code to core extensions
Diffstat (limited to 'server/sonar-web')
11 files changed, 14 insertions, 590 deletions
diff --git a/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts b/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts index d85617d3fa7..03bccc291d7 100644 --- a/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts +++ b/server/sonar-web/src/main/js/app/components/extensions/exposeLibraries.ts @@ -99,6 +99,7 @@ import { postJSONBody, request } from '../../../helpers/request'; +import { sanitizeStringRestricted } from '../../../helpers/sanitize'; import { getStandards, renderCWECategory, @@ -162,7 +163,8 @@ const exposeLibraries = () => { getComponentIssuesUrl, getComponentSecurityHotspotsUrl, getMeasureHistoryUrl, - getRulesUrl + getRulesUrl, + sanitizeStringRestricted }; } }); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx index dead2fed58b..609f6461392 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/Menu.tsx @@ -318,7 +318,6 @@ export class Menu extends React.PureComponent<Props> { this.renderSettingsLink(query, isApplication, isPortfolio), this.renderBranchesLink(query, isProject), this.renderBaselineLink(query, isApplication, isPortfolio), - this.renderReportSettingsLink(query, isApplication), ...this.renderAdminExtensions(query, isApplication), this.renderImportExportLink(query, isProject), this.renderProfilesLink(query), @@ -410,23 +409,6 @@ export class Menu extends React.PureComponent<Props> { ); }; - renderReportSettingsLink = (query: Query, isApplication: boolean) => { - const extensions = this.getConfiguration().extensions || []; - const hasGovernance = extensions.find(e => e.key === 'governance/console'); - - if (!isApplication || !hasGovernance) { - return null; - } - - return ( - <li key="report-settings"> - <Link activeClassName="active" to={{ pathname: '/application/settings', query }}> - {translate('application_settings.report')} - </Link> - </li> - ); - }; - renderImportExportLink = (query: Query, isProject: boolean) => { if (!isProject) { return null; 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 40c777ca892..2b03991f6e3 100644 --- a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx +++ b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx @@ -27,7 +27,6 @@ import { IntlProvider } from 'react-intl'; import { Provider } from 'react-redux'; import { IndexRoute, Redirect, Route, RouteConfig, RouteProps, Router } from 'react-router'; import accountRoutes from '../../apps/account/routes'; -import applicationSettingsRoutes from '../../apps/application-settings/routes'; import auditLogsRoutes from '../../apps/audit-logs/routes'; import backgroundTasksRoutes from '../../apps/background-tasks/routes'; import codeRoutes from '../../apps/code/routes'; @@ -124,6 +123,16 @@ function renderRedirects() { /> <Route + path="/application/settings" + onEnter={(nextState, replace) => { + replace({ + pathname: '/project/admin/extension/governance/application_report', + query: { id: nextState.location.query.id } + }); + }} + /> + + <Route path="/issues/search" onEnter={(_, replace) => { replace(`/issues${window.location.hash}`); @@ -226,7 +235,6 @@ function renderComponentRoutes() { <RouteWithChildRoutes path="project/import_export" childRoutes={projectDumpRoutes} /> <RouteWithChildRoutes path="project/settings" childRoutes={settingsRoutes} /> <RouteWithChildRoutes path="project_roles" childRoutes={projectPermissionsRoutes} /> - <RouteWithChildRoutes path="application/settings" childRoutes={applicationSettingsRoutes} /> <RouteWithChildRoutes path="project/webhooks" childRoutes={webhooksRoutes} /> <Route path="project/deletion" diff --git a/server/sonar-web/src/main/js/apps/application-settings/ApplicationSettingsApp.tsx b/server/sonar-web/src/main/js/apps/application-settings/ApplicationSettingsApp.tsx deleted file mode 100644 index a6c6617d2da..00000000000 --- a/server/sonar-web/src/main/js/apps/application-settings/ApplicationSettingsApp.tsx +++ /dev/null @@ -1,136 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 * as React from 'react'; -import { getDefinitions, getValues, setSimpleSettingValue } from '../../api/settings'; -import DeferredSpinner from '../../components/ui/DeferredSpinner'; -import { translate } from '../../helpers/l10n'; -import { SettingCategoryDefinition, SettingsKey } from '../../types/settings'; -import ReportFrequencyForm from './ReportFrequencyForm'; - -interface Props { - component: T.Component; -} - -interface State { - definition?: SettingCategoryDefinition; - frequency?: string; - loading: boolean; -} - -export default class ApplicationSettingsApp extends React.PureComponent<Props, State> { - mounted = false; - - state: State = { - loading: true - }; - - componentDidMount() { - this.mounted = true; - this.fetchInitialData(); - } - - async componentDidUpdate(prevProps: Props) { - if (prevProps.component.key !== this.props.component.key) { - this.setState({ loading: true }); - await this.fetchSetting(); - this.setState({ loading: false }); - } - } - - componentWillUnmount() { - this.mounted = false; - } - - fetchInitialData = async () => { - this.setState({ loading: true }); - await Promise.all([this.fetchDefinition(), this.fetchSetting()]); - this.setState({ loading: false }); - }; - - fetchDefinition = async () => { - const { component } = this.props; - try { - const definitions = await getDefinitions(component.key); - const definition = definitions.find(d => d.key === SettingsKey.ProjectReportFrequency); - - if (this.mounted) { - this.setState({ definition }); - } - } catch (_) { - /* do nothing */ - } - }; - - fetchSetting = async () => { - const { component } = this.props; - try { - const setting = ( - await getValues({ component: component.key, keys: SettingsKey.ProjectReportFrequency }) - ).pop(); - - if (this.mounted) { - this.setState({ - frequency: setting ? setting.value : undefined - }); - } - } catch (_) { - /* do nothing */ - } - }; - - handleSubmit = async (frequency: string) => { - const { component } = this.props; - - try { - await setSimpleSettingValue({ - component: component.key, - key: SettingsKey.ProjectReportFrequency, - value: frequency - }); - - this.setState({ frequency }); - } catch (_) { - /* Do nothing */ - } - }; - - render() { - const { definition, frequency, loading } = this.state; - - return ( - <div className="page page-limited application-settings"> - <h1>{translate('application_settings.page')}</h1> - <div className="boxed-group big-padded big-spacer-top"> - <DeferredSpinner loading={loading}> - <div> - {definition && frequency && ( - <ReportFrequencyForm - definition={definition} - frequency={frequency} - onSave={this.handleSubmit} - /> - )} - </div> - </DeferredSpinner> - </div> - </div> - ); - } -} diff --git a/server/sonar-web/src/main/js/apps/application-settings/ReportFrequencyForm.tsx b/server/sonar-web/src/main/js/apps/application-settings/ReportFrequencyForm.tsx deleted file mode 100644 index 545d432c459..00000000000 --- a/server/sonar-web/src/main/js/apps/application-settings/ReportFrequencyForm.tsx +++ /dev/null @@ -1,96 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 * as React from 'react'; -import { Button, ResetButtonLink } from '../../components/controls/buttons'; -import SelectLegacy from '../../components/controls/SelectLegacy'; -import { translate } from '../../helpers/l10n'; -import { sanitizeStringRestricted } from '../../helpers/sanitize'; -import { SettingCategoryDefinition } from '../../types/settings'; - -export interface ReportFrequencyFormProps { - definition: SettingCategoryDefinition; - frequency: string; - onSave: (value: string) => Promise<void>; -} - -export default function ReportFrequencyForm(props: ReportFrequencyFormProps) { - const { definition, frequency } = props; - const { defaultValue } = definition; - - const [currentSelection, setCurrentSelection] = React.useState(frequency); - - const options = props.definition.options.map(option => ({ - label: option, - value: option - })); - - const handleReset = () => { - if (defaultValue) { - setCurrentSelection(defaultValue); - props.onSave(defaultValue); - } - }; - - return ( - <div> - <h2>{translate('application_settings.report.frequency')}</h2> - {definition.description && ( - <div - className="markdown" - // eslint-disable-next-line react/no-danger - dangerouslySetInnerHTML={{ - __html: sanitizeStringRestricted(definition.description) - }} - /> - )} - - <SelectLegacy - className="input-medium" - clearable={false} - name={definition.name} - onChange={({ value }: { value: string }) => setCurrentSelection(value)} - options={options} - value={currentSelection} - /> - - <div className="display-flex-center big-spacer-top"> - {frequency !== currentSelection && ( - <Button - className="spacer-right button-success" - onClick={() => props.onSave(currentSelection)}> - {translate('save')} - </Button> - )} - - {defaultValue !== undefined && frequency !== defaultValue && ( - <Button className="spacer-right" onClick={handleReset}> - {translate('reset_verb')} - </Button> - )} - - {frequency !== currentSelection && ( - <ResetButtonLink className="spacer-right" onClick={() => setCurrentSelection(frequency)}> - {translate('cancel')} - </ResetButtonLink> - )} - </div> - </div> - ); -} diff --git a/server/sonar-web/src/main/js/apps/application-settings/__tests__/ApplicationSettingsApp-test.tsx b/server/sonar-web/src/main/js/apps/application-settings/__tests__/ApplicationSettingsApp-test.tsx deleted file mode 100644 index 77f8c981da3..00000000000 --- a/server/sonar-web/src/main/js/apps/application-settings/__tests__/ApplicationSettingsApp-test.tsx +++ /dev/null @@ -1,70 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { setSimpleSettingValue } from '../../../api/settings'; -import { mockComponent } from '../../../helpers/mocks/component'; -import { waitAndUpdate } from '../../../helpers/testUtils'; -import { SettingsKey } from '../../../types/settings'; -import ApplicationSettingsApp from '../ApplicationSettingsApp'; - -jest.mock('../../../api/settings', () => { - const { mockDefinition } = jest.requireActual('../../../helpers/mocks/settings'); - - const definition = mockDefinition({ - key: 'sonar.governance.report.project.branch.frequency', // SettingsKey.ProjectReportFrequency - defaultValue: 'Monthly', - description: 'description', - options: ['Daily', 'Weekly', 'Monthly'] - }); - - return { - getDefinitions: jest.fn().mockResolvedValue([definition]), - getValues: jest.fn().mockResolvedValue([{ value: 'Monthly' }]), - setSimpleSettingValue: jest.fn().mockResolvedValue(undefined) - }; -}); - -it('should render correctly', async () => { - const wrapper = shallowRender(); - expect(wrapper).toMatchSnapshot('loading'); - await waitAndUpdate(wrapper); - expect(wrapper).toMatchSnapshot('default'); -}); - -it('should handle submission', async () => { - const component = mockComponent({ key: 'app-key' }); - const wrapper = shallowRender({ component }); - await waitAndUpdate(wrapper); - - wrapper.instance().handleSubmit('Daily'); - - expect(setSimpleSettingValue).toBeCalledWith({ - component: component.key, - key: SettingsKey.ProjectReportFrequency, - value: 'Daily' - }); -}); - -function shallowRender(props: Partial<ApplicationSettingsApp['props']> = {}) { - return shallow<ApplicationSettingsApp>( - <ApplicationSettingsApp component={mockComponent()} {...props} /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/application-settings/__tests__/ReportFrequencyForm-test.tsx b/server/sonar-web/src/main/js/apps/application-settings/__tests__/ReportFrequencyForm-test.tsx deleted file mode 100644 index dc6db927c59..00000000000 --- a/server/sonar-web/src/main/js/apps/application-settings/__tests__/ReportFrequencyForm-test.tsx +++ /dev/null @@ -1,70 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 { shallow } from 'enzyme'; -import * as React from 'react'; -import { Button, ResetButtonLink } from '../../../components/controls/buttons'; -import SelectLegacy from '../../../components/controls/SelectLegacy'; -import { mockDefinition } from '../../../helpers/mocks/settings'; -import ReportFrequencyForm, { ReportFrequencyFormProps } from '../ReportFrequencyForm'; - -it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot('default'); - expect(shallowRender({ frequency: 'Weekly' })).toMatchSnapshot('changed'); - expect(shallowRender({ definition: mockDefinition() })).toMatchSnapshot('no description'); -}); - -it('should handle changes', () => { - const onSave = jest.fn(); - const wrapper = shallowRender({ onSave }); - - wrapper.find(SelectLegacy).simulate('change', { value: 'Daily' }); - - expect(wrapper.find('.button-success').exists()).toBe(true); - expect(wrapper.find(ResetButtonLink).exists()).toBe(true); - - wrapper.find(Button).simulate('click'); - - expect(onSave).toBeCalledWith('Daily'); -}); - -it('should handle reset', () => { - const onSave = jest.fn(); - const wrapper = shallowRender({ frequency: 'Weekly', onSave }); - - expect(wrapper.find('.button-success').exists()).toBe(false); - wrapper.find(Button).simulate('click'); - - expect(onSave).toBeCalledWith('Monthly'); -}); - -function shallowRender(props: Partial<ReportFrequencyFormProps> = {}) { - return shallow<ReportFrequencyFormProps>( - <ReportFrequencyForm - definition={mockDefinition({ - defaultValue: 'Monthly', - description: 'description', - options: ['Daily', 'Weekly', 'Monthly'] - })} - frequency="Monthly" - onSave={jest.fn()} - {...props} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ApplicationSettingsApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ApplicationSettingsApp-test.tsx.snap deleted file mode 100644 index 60191d42174..00000000000 --- a/server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ApplicationSettingsApp-test.tsx.snap +++ /dev/null @@ -1,59 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: default 1`] = ` -<div - className="page page-limited application-settings" -> - <h1> - application_settings.page - </h1> - <div - className="boxed-group big-padded big-spacer-top" - > - <DeferredSpinner - loading={false} - > - <div> - <ReportFrequencyForm - definition={ - Object { - "category": "foo category", - "defaultValue": "Monthly", - "description": "description", - "fields": Array [], - "key": "sonar.governance.report.project.branch.frequency", - "options": Array [ - "Daily", - "Weekly", - "Monthly", - ], - "subCategory": "foo subCat", - } - } - frequency="Monthly" - onSave={[Function]} - /> - </div> - </DeferredSpinner> - </div> -</div> -`; - -exports[`should render correctly: loading 1`] = ` -<div - className="page page-limited application-settings" -> - <h1> - application_settings.page - </h1> - <div - className="boxed-group big-padded big-spacer-top" - > - <DeferredSpinner - loading={true} - > - <div /> - </DeferredSpinner> - </div> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ReportFrequencyForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ReportFrequencyForm-test.tsx.snap deleted file mode 100644 index 7ea031a392b..00000000000 --- a/server/sonar-web/src/main/js/apps/application-settings/__tests__/__snapshots__/ReportFrequencyForm-test.tsx.snap +++ /dev/null @@ -1,108 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly: changed 1`] = ` -<div> - <h2> - application_settings.report.frequency - </h2> - <div - className="markdown" - dangerouslySetInnerHTML={ - Object { - "__html": "description", - } - } - /> - <SelectLegacy - className="input-medium" - clearable={false} - onChange={[Function]} - options={ - Array [ - Object { - "label": "Daily", - "value": "Daily", - }, - Object { - "label": "Weekly", - "value": "Weekly", - }, - Object { - "label": "Monthly", - "value": "Monthly", - }, - ] - } - value="Weekly" - /> - <div - className="display-flex-center big-spacer-top" - > - <Button - className="spacer-right" - onClick={[Function]} - > - reset_verb - </Button> - </div> -</div> -`; - -exports[`should render correctly: default 1`] = ` -<div> - <h2> - application_settings.report.frequency - </h2> - <div - className="markdown" - dangerouslySetInnerHTML={ - Object { - "__html": "description", - } - } - /> - <SelectLegacy - className="input-medium" - clearable={false} - onChange={[Function]} - options={ - Array [ - Object { - "label": "Daily", - "value": "Daily", - }, - Object { - "label": "Weekly", - "value": "Weekly", - }, - Object { - "label": "Monthly", - "value": "Monthly", - }, - ] - } - value="Monthly" - /> - <div - className="display-flex-center big-spacer-top" - /> -</div> -`; - -exports[`should render correctly: no description 1`] = ` -<div> - <h2> - application_settings.report.frequency - </h2> - <SelectLegacy - className="input-medium" - clearable={false} - onChange={[Function]} - options={Array []} - value="Monthly" - /> - <div - className="display-flex-center big-spacer-top" - /> -</div> -`; diff --git a/server/sonar-web/src/main/js/apps/application-settings/routes.ts b/server/sonar-web/src/main/js/apps/application-settings/routes.ts deleted file mode 100644 index e1f12d9d8ad..00000000000 --- a/server/sonar-web/src/main/js/apps/application-settings/routes.ts +++ /dev/null @@ -1,28 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2021 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 { lazyLoadComponent } from '../../components/lazyLoadComponent'; - -const routes = [ - { - indexRoute: { component: lazyLoadComponent(() => import('./ApplicationSettingsApp')) } - } -]; - -export default routes; diff --git a/server/sonar-web/src/main/js/types/settings.ts b/server/sonar-web/src/main/js/types/settings.ts index 82098ddfc3d..2d50a443fe2 100644 --- a/server/sonar-web/src/main/js/types/settings.ts +++ b/server/sonar-web/src/main/js/types/settings.ts @@ -21,8 +21,7 @@ export const enum SettingsKey { DaysBeforeDeletingInactiveBranchesAndPRs = 'sonar.dbcleaner.daysBeforeDeletingInactiveBranchesAndPRs', DefaultProjectVisibility = 'projects.default.visibility', ServerBaseUrl = 'sonar.core.serverBaseURL', - PluginRiskConsent = 'sonar.plugins.risk.consent', - ProjectReportFrequency = 'sonar.governance.report.project.branch.frequency' + PluginRiskConsent = 'sonar.plugins.risk.consent' } export type Setting = SettingValue & { definition: SettingDefinition; hasValue: boolean }; |