From e5827b3671013c14776ca189e400e3687bfa0983 Mon Sep 17 00:00:00 2001 From: Pascal Mugnier Date: Thu, 21 Jun 2018 11:08:48 +0200 Subject: [PATCH] SONAR-10813 Add project branches --- .../sonar-web/src/main/js/api/application.ts | 10 ++- .../src/main/js/api/quality-gates.ts | 1 + .../nav/component/ComponentNavBranch.tsx | 74 ++++++++++++++----- .../nav/component/ComponentNavMenu.tsx | 3 +- .../nav/component/ComponentNavMeta.tsx | 3 +- .../__tests__/ComponentNavBranch-test.tsx | 14 ++-- ...ComponentNavBranchesMenuItem-test.tsx.snap | 1 + .../ComponentNavHeader-test.tsx.snap | 2 + .../ComponentNavMenu-test.tsx.snap | 6 ++ .../__snapshots__/SearchResult-test.js.snap | 8 ++ server/sonar-web/src/main/js/app/types.ts | 4 +- .../ProjectNotifications-test.tsx.snap | 1 + .../__snapshots__/TaskComponent-test.tsx.snap | 1 + .../apps/code/components/ComponentMeasure.tsx | 2 +- .../js/apps/code/components/ComponentName.tsx | 25 ++++++- .../sonar-web/src/main/js/apps/code/types.ts | 1 + .../drilldown/ComponentCell.js | 26 ++++++- .../main/js/apps/component/components/App.tsx | 14 +--- .../main/js/apps/issues/components/App.tsx | 5 +- .../ComponentBreadcrumbs-test.tsx.snap | 3 + .../ApplicationLeakPeriodLegend.tsx | 11 ++- .../apps/overview/components/OverviewApp.tsx | 11 ++- .../ApplicationLeakPeriodLegend-test.tsx | 6 +- .../overview/main/BugsAndVulnerabilities.tsx | 9 ++- .../qualityGate/ApplicationQualityGate.tsx | 6 +- ...pplicationQualityGateProject-test.tsx.snap | 1 + .../__snapshots__/WorstProjects-test.tsx.snap | 3 + .../__snapshots__/Risk-test.tsx.snap | 1 + .../SimpleBubbleChart-test.tsx.snap | 1 + .../CreateProjectForm-test.tsx.snap | 1 + .../MeasuresOverlayCoveredFiles-test.tsx.snap | 1 + .../sonar-web/src/main/js/helpers/branches.ts | 18 +++++ server/sonar-web/src/main/js/helpers/urls.ts | 8 +- .../resources/org/sonar/l10n/core.properties | 2 + 34 files changed, 220 insertions(+), 63 deletions(-) diff --git a/server/sonar-web/src/main/js/api/application.ts b/server/sonar-web/src/main/js/api/application.ts index 5821d5705be..6fb3876a486 100644 --- a/server/sonar-web/src/main/js/api/application.ts +++ b/server/sonar-web/src/main/js/api/application.ts @@ -26,6 +26,12 @@ export interface ApplicationLeak { projectName: string; } -export function getApplicationLeak(application: string): Promise> { - return getJSON('/api/applications/show_leak', { application }).then(r => r.leaks, throwGlobalError); +export function getApplicationLeak( + application: string, + branch?: string +): Promise> { + return getJSON('/api/applications/show_leak', { application, branch }).then( + r => r.leaks, + throwGlobalError + ); } diff --git a/server/sonar-web/src/main/js/api/quality-gates.ts b/server/sonar-web/src/main/js/api/quality-gates.ts index d2f212bd457..af49578283e 100644 --- a/server/sonar-web/src/main/js/api/quality-gates.ts +++ b/server/sonar-web/src/main/js/api/quality-gates.ts @@ -160,6 +160,7 @@ export interface ApplicationQualityGate { export function getApplicationQualityGate(data: { application: string; + branch?: string; organization?: string; }): Promise { return getJSON('/api/qualitygates/application_status', data).catch(throwGlobalError); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranch.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranch.tsx index 8cba10a5dbc..d02389be63d 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranch.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavBranch.tsx @@ -20,6 +20,7 @@ import * as React from 'react'; import * as PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; +import { Link } from 'react-router'; import ComponentNavBranchesMenu from './ComponentNavBranchesMenu'; import DocTooltip from '../../../../components/docs/DocTooltip'; import { BranchLike, Component } from '../../../types'; @@ -53,7 +54,8 @@ export default class ComponentNavBranch extends React.PureComponent { + const adminLink = { + pathname: '/project/admin/extension/governance/console', + query: { id: this.props.component.breadcrumbs[0].key, qualifier: 'APP' } + }; + return ( + <> +

{translate('application.branches.help')}

+
+ + {translate('application.branches.link')} + + + ); + }; + render() { const { branchLikes, currentBranchLike } = this.props; - const { configuration } = this.props.component; + const { configuration, breadcrumbs } = this.props.component; if (isSonarCloud() && !this.context.branchesEnabled) { return null; } const displayName = getBranchLikeDisplayName(currentBranchLike); + const isApp = breadcrumbs && breadcrumbs[0] && breadcrumbs[0].qualifier === 'APP'; - if (!this.context.branchesEnabled) { + if (isApp && branchLikes.length < 2) { return (
{displayName} - - - -
- ); - } - - if (branchLikes.length < 2) { - return ( -
- - {displayName} - - - + {configuration && + configuration.showSettings && ( + + + + )}
); + } else { + if (!this.context.branchesEnabled) { + return ( +
+ + {displayName} + + + +
+ ); + } + + if (branchLikes.length < 2) { + return ( +
+ + {displayName} + + + +
+ ); + } } return ( diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx index 1f3045cfc52..53009cc4d40 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMenu.tsx @@ -400,9 +400,10 @@ export default class ComponentNavMenu extends React.PureComponent { renderExtension = ({ key, name }: Extension, isAdmin: boolean) => { const pathname = isAdmin ? `/project/admin/extension/${key}` : `/project/extension/${key}`; + const query = { id: this.props.component.key, qualifier: this.props.component.qualifier }; return (
  • - + {name}
  • diff --git a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx index 6b52b5eefbf..7b2f2e2c221 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/ComponentNavMeta.tsx @@ -111,7 +111,8 @@ function getCurrentPage(component: Component, branchLike: BranchLike | undefined if (component.qualifier === 'VW' || component.qualifier === 'SVW') { currentPage = { type: HomePageType.Portfolio, component: component.key }; } else if (component.qualifier === 'APP') { - currentPage = { type: HomePageType.Application, component: component.key }; + const branch = isLongLivingBranch(branchLike) ? branchLike.name : undefined; + currentPage = { type: HomePageType.Application, component: component.key, branch }; } else if (component.qualifier === 'TRK') { // when home page is set to the default branch of a project, its name is returned as `undefined` const branch = isLongLivingBranch(branchLike) ? branchLike.name : undefined; diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranch-test.tsx b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranch-test.tsx index 9a805b74c82..3fd20dbc41e 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranch-test.tsx +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNavBranch-test.tsx @@ -49,7 +49,7 @@ it('renders main branch', () => { component={component} currentBranchLike={mainBranch} />, - { context: { branchesEnabled: true } } + { context: { branchesEnabled: true, canAdmin: true } } ) ).toMatchSnapshot(); }); @@ -70,7 +70,7 @@ it('renders short-living branch', () => { component={component} currentBranchLike={branch} />, - { context: { branchesEnabled: true } } + { context: { branchesEnabled: true, canAdmin: true } } ) ).toMatchSnapshot(); }); @@ -91,7 +91,7 @@ it('renders pull request', () => { component={component} currentBranchLike={pullRequest} />, - { context: { branchesEnabled: true } } + { context: { branchesEnabled: true, canAdmin: true } } ) ).toMatchSnapshot(); }); @@ -104,7 +104,7 @@ it('opens menu', () => { component={component} currentBranchLike={mainBranch} />, - { context: { branchesEnabled: true } } + { context: { branchesEnabled: true, canAdmin: true } } ); expect(wrapper.find('Toggler').prop('open')).toBe(false); click(wrapper.find('a')); @@ -119,7 +119,7 @@ it('renders single branch popup', () => { component={component} currentBranchLike={mainBranch} />, - { context: { branchesEnabled: true } } + { context: { branchesEnabled: true, canAdmin: true } } ); expect(wrapper.find('DocTooltip')).toMatchSnapshot(); }); @@ -132,7 +132,7 @@ it('renders no branch support popup', () => { component={component} currentBranchLike={mainBranch} />, - { context: { branchesEnabled: false } } + { context: { branchesEnabled: false, canAdmin: true } } ); expect(wrapper.find('DocTooltip')).toMatchSnapshot(); }); @@ -146,7 +146,7 @@ it('renders nothing on SonarCloud without branch support', () => { component={component} currentBranchLike={mainBranch} />, - { context: { branchesEnabled: false, onSonarCloud: true } } + { context: { branchesEnabled: false, onSonarCloud: true, canAdmin: true } } ); expect(wrapper.type()).toBeNull(); }); diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenuItem-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenuItem-test.tsx.snap index b4c9f37a5d3..25528a5523c 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenuItem-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavBranchesMenuItem-test.tsx.snap @@ -13,6 +13,7 @@ exports[`renders main branch 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "component", }, } diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavHeader-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavHeader-test.tsx.snap index 4d85fb56b11..b8498689bda 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavHeader-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavHeader-test.tsx.snap @@ -23,6 +23,7 @@ exports[`should not render breadcrumbs with one element 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "my-project", }, } @@ -90,6 +91,7 @@ exports[`should render organization 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "my-project", }, } diff --git a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap index 3abe30c25f4..a62c3ce9e0e 100644 --- a/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap +++ b/server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNavMenu-test.tsx.snap @@ -873,6 +873,7 @@ exports[`should work with extensions 1`] = ` "pathname": "/project/extension/component-foo", "query": Object { "id": "foo", + "qualifier": "TRK", }, } } @@ -954,6 +955,7 @@ exports[`should work with extensions 2`] = ` "pathname": "/project/admin/extension/foo", "query": Object { "id": "foo", + "qualifier": "TRK", }, } } @@ -1001,6 +1003,7 @@ exports[`should work with multiple extensions 1`] = ` "pathname": "/project/extension/component-foo", "query": Object { "id": "foo", + "qualifier": "TRK", }, } } @@ -1018,6 +1021,7 @@ exports[`should work with multiple extensions 1`] = ` "pathname": "/project/extension/component-bar", "query": Object { "id": "foo", + "qualifier": "TRK", }, } } @@ -1099,6 +1103,7 @@ exports[`should work with multiple extensions 2`] = ` "pathname": "/project/admin/extension/foo", "query": Object { "id": "foo", + "qualifier": "TRK", }, } } @@ -1116,6 +1121,7 @@ exports[`should work with multiple extensions 2`] = ` "pathname": "/project/admin/extension/bar", "query": Object { "id": "foo", + "qualifier": "TRK", }, } } diff --git a/server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchResult-test.js.snap b/server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchResult-test.js.snap index 4ad1c1e2e2d..0a9d6781bf0 100644 --- a/server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchResult-test.js.snap +++ b/server/sonar-web/src/main/js/app/components/search/__tests__/__snapshots__/SearchResult-test.js.snap @@ -21,6 +21,7 @@ exports[`renders favorite 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "foo", }, } @@ -69,6 +70,7 @@ exports[`renders match 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "foo", }, } @@ -116,6 +118,7 @@ exports[`renders organizations 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "foo", }, } @@ -168,6 +171,7 @@ exports[`renders organizations 2`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "foo", }, } @@ -215,6 +219,7 @@ exports[`renders projects 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "qwe", }, } @@ -267,6 +272,7 @@ exports[`renders recently browsed 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "foo", }, } @@ -314,6 +320,7 @@ exports[`renders selected 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "foo", }, } @@ -359,6 +366,7 @@ exports[`renders selected 2`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "foo", }, } diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts index 11e29502c06..75ac42d3237 100644 --- a/server/sonar-web/src/main/js/app/types.ts +++ b/server/sonar-web/src/main/js/app/types.ts @@ -169,7 +169,7 @@ export interface Group { } export type HomePage = - | { type: HomePageType.Application; component: string } + | { type: HomePageType.Application; branch: string | undefined; component: string } | { type: HomePageType.Issues } | { type: HomePageType.MyIssues } | { type: HomePageType.MyProjects } @@ -220,6 +220,7 @@ export interface Issue { assigneeLogin?: string; assigneeName?: string; author?: string; + branch?: string; comments?: IssueComment[]; component: string; componentLongName: string; @@ -237,6 +238,7 @@ export interface Issue { projectName: string; projectOrganization: string; projectUuid: string; + pullRequest?: string; resolution?: string; rule: string; ruleName: string; diff --git a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/ProjectNotifications-test.tsx.snap b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/ProjectNotifications-test.tsx.snap index 90b25b16cf1..11b25e6dd9d 100644 --- a/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/ProjectNotifications-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/account/notifications/__tests__/__snapshots__/ProjectNotifications-test.tsx.snap @@ -25,6 +25,7 @@ exports[`should match snapshot 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "foo", }, } diff --git a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskComponent-test.tsx.snap b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskComponent-test.tsx.snap index 88328a93b61..b054cb915e8 100644 --- a/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskComponent-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskComponent-test.tsx.snap @@ -20,6 +20,7 @@ exports[`renders correctly 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "foo", }, } diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx index bc900211a0e..bb3cbe506a2 100644 --- a/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx @@ -42,5 +42,5 @@ export default function ComponentMeasure({ component, metricKey, metricType }: P return ; } - return ; + return ; } diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx index 57473532b8a..391c1862c04 100644 --- a/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/ComponentName.tsx @@ -25,6 +25,8 @@ import * as theme from '../../../app/theme'; import { BranchLike } from '../../../app/types'; import QualifierIcon from '../../../components/icons-components/QualifierIcon'; import { getBranchLikeQuery } from '../../../helpers/branches'; +import LongLivingBranchIcon from '../../../components/icons-components/LongLivingBranchIcon'; +import { translate } from '../../../helpers/l10n'; function getTooltip(component: Component) { const isFile = component.qualifier === 'FIL' || component.qualifier === 'UTS'; @@ -77,10 +79,11 @@ export default function ComponentName(props: Props) { let inner = null; if (component.refKey && component.qualifier !== 'SVW') { + const branch = rootComponent.qualifier === 'APP' ? { branch: component.branch } : {}; inner = ( + className="link-with-icon" + to={{ pathname: '/dashboard', query: { id: component.refKey, ...branch } }}> {name} ); @@ -90,7 +93,7 @@ export default function ComponentName(props: Props) { Object.assign(query, { selected: component.key }); } inner = ( - + {name} ); @@ -102,5 +105,21 @@ export default function ComponentName(props: Props) { ); } + if (rootComponent.qualifier === 'APP') { + inner = ( + <> + {inner} + {component.branch ? ( + <> + + {component.branch} + + ) : ( + {translate('branches.main_branch')} + )} + + ); + } + return {inner}; } diff --git a/server/sonar-web/src/main/js/apps/code/types.ts b/server/sonar-web/src/main/js/apps/code/types.ts index 3a226f8127d..a0b6459a3d3 100644 --- a/server/sonar-web/src/main/js/apps/code/types.ts +++ b/server/sonar-web/src/main/js/apps/code/types.ts @@ -21,6 +21,7 @@ import { Measure } from '../../helpers/measures'; export interface Component extends Breadcrumb { + branch?: string; measures?: Measure[]; path?: string; refKey?: string; diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js index 9b2ce2e9b6e..fc268fa3425 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/ComponentCell.js @@ -22,12 +22,15 @@ import React from 'react'; import { Link } from 'react-router'; import LinkIcon from '../../../components/icons-components/LinkIcon'; import QualifierIcon from '../../../components/icons-components/QualifierIcon'; +import LongLivingBranchIcon from '../../../components/icons-components/LongLivingBranchIcon'; import { splitPath } from '../../../helpers/path'; import { getPathUrlAsString, getBranchLikeUrl, + getLongLivingBranchUrl, getComponentDrilldownUrlWithSelection } from '../../../helpers/urls'; +import { translate } from '../../../helpers/l10n'; /*:: import type { Component, ComponentEnhanced } from '../types'; */ /*:: import type { Metric } from '../../../store/metrics/actions'; */ @@ -56,23 +59,44 @@ export default class ComponentCell extends React.PureComponent { const { component } = this.props; let head = ''; let tail = component.name; + let branch = null; if (['DIR', 'FIL', 'UTS'].includes(component.qualifier)) { const parts = splitPath(component.path); ({ head, tail } = parts); } + + if (this.props.rootComponent.qualifier === 'APP') { + branch = ( + + {component.branch ? ( + + + {component.branch} + + ) : ( + {translate('branches.main_branch')} + )} + + ); + } return (   {head.length > 0 && {head}/} {tail} + {branch} ); } render() { const { branchLike, component, metric, rootComponent } = this.props; + const to = + this.props.rootComponent.qualifier === 'APP' + ? getLongLivingBranchUrl(component.refKey, component.branch) + : getBranchLikeUrl(component.refKey, branchLike); return (
    @@ -95,7 +119,7 @@ export default class ComponentCell extends React.PureComponent { + to={to}> diff --git a/server/sonar-web/src/main/js/apps/component/components/App.tsx b/server/sonar-web/src/main/js/apps/component/components/App.tsx index 71fc859ea2f..d70fc1c92e6 100644 --- a/server/sonar-web/src/main/js/apps/component/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/component/components/App.tsx @@ -18,8 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { PullRequest, BranchType, ShortLivingBranch } from '../../../app/types'; import SourceViewer from '../../../components/SourceViewer/SourceViewer'; +import { fillBranchLike } from '../../../helpers/branches'; interface Props { location: { @@ -54,17 +54,7 @@ export default class App extends React.PureComponent { // TODO find a way to avoid creating this fakeBranchLike // probably the best way would be to drop this page completely // and redirect to the Code page - let fakeBranchLike: ShortLivingBranch | PullRequest | undefined = undefined; - if (branch) { - fakeBranchLike = { - isMain: false, - mergeBranch: '', - name: branch, - type: BranchType.SHORT - } as ShortLivingBranch; - } else if (pullRequest) { - fakeBranchLike = { base: '', branch: '', key: pullRequest, title: '' } as PullRequest; - } + const fakeBranchLike = fillBranchLike(branch, pullRequest); return (
    diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.tsx b/server/sonar-web/src/main/js/apps/issues/components/App.tsx index 2ae38d862de..4e22af0bfd3 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/App.tsx @@ -62,7 +62,8 @@ import { isShortLivingBranch, isSameBranchLike, getBranchLikeQuery, - isPullRequest + isPullRequest, + fillBranchLike } from '../../../helpers/branches'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { RawQuery } from '../../../helpers/query'; @@ -1046,7 +1047,7 @@ export default class App extends React.PureComponent {
    {openIssue ? ( { if (!this.state.leaks) { - getApplicationLeak(this.props.component).then( + getApplicationLeak( + this.props.component.key, + this.props.branch ? this.props.branch.name : undefined + ).then( leaks => { if (this.mounted) { this.setState({ diff --git a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx index 68a7d1bbb1d..df32445c412 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx @@ -43,7 +43,11 @@ import { PROJECT_ACTIVITY_GRAPH, PROJECT_ACTIVITY_GRAPH_CUSTOM } from '../../projectActivity/utils'; -import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches'; +import { + isSameBranchLike, + getBranchLikeQuery, + isLongLivingBranch +} from '../../../helpers/branches'; import { fetchMetrics } from '../../../store/rootActions'; import { getMetrics } from '../../../store/rootReducer'; import { BranchLike, Component, Metric } from '../../../app/types'; @@ -213,7 +217,10 @@ export class OverviewApp extends React.PureComponent { return (
    {component.qualifier === 'APP' ? ( - + ) : ( )} diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/ApplicationLeakPeriodLegend-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/ApplicationLeakPeriodLegend-test.tsx index 27a71ac1851..1dba9c67b4b 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/ApplicationLeakPeriodLegend-test.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/ApplicationLeakPeriodLegend-test.tsx @@ -32,7 +32,11 @@ jest.mock('../../../../api/application', () => ({ })); it('renders', async () => { - const wrapper = shallow(); + const wrapper = shallow( + + ); expect(wrapper).toMatchSnapshot(); await waitAndUpdate(wrapper); diff --git a/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx b/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx index 3ce147ba718..4715c66a2ff 100644 --- a/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx +++ b/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx @@ -28,11 +28,11 @@ import VulnerabilityIcon from '../../../components/icons-components/Vulnerabilit import { getMetricName } from '../helpers/metrics'; import { getComponentDrilldownUrl } from '../../../helpers/urls'; import { translate } from '../../../helpers/l10n'; +import { isLongLivingBranch } from '../../../helpers/branches'; export class BugsAndVulnerabilities extends React.PureComponent { renderHeader() { const { branchLike, component } = this.props; - return (
    @@ -62,7 +62,7 @@ export class BugsAndVulnerabilities extends React.PureComponent { } renderLeak() { - const { component, leakPeriod } = this.props; + const { branchLike, component, leakPeriod } = this.props; if (!leakPeriod) { return null; } @@ -70,7 +70,10 @@ export class BugsAndVulnerabilities extends React.PureComponent { return (
    {component.qualifier === 'APP' ? ( - + ) : ( )} diff --git a/server/sonar-web/src/main/js/apps/overview/qualityGate/ApplicationQualityGate.tsx b/server/sonar-web/src/main/js/apps/overview/qualityGate/ApplicationQualityGate.tsx index d50bc8a42f1..e4771d74e76 100644 --- a/server/sonar-web/src/main/js/apps/overview/qualityGate/ApplicationQualityGate.tsx +++ b/server/sonar-web/src/main/js/apps/overview/qualityGate/ApplicationQualityGate.tsx @@ -23,10 +23,11 @@ import ApplicationQualityGateProject from './ApplicationQualityGateProject'; import Level from '../../../components/ui/Level'; import { getApplicationQualityGate, ApplicationProject } from '../../../api/quality-gates'; import { translate } from '../../../helpers/l10n'; -import { LightComponent, Metric } from '../../../app/types'; +import { LightComponent, Metric, LongLivingBranch } from '../../../app/types'; import DocTooltip from '../../../components/docs/DocTooltip'; interface Props { + branch?: LongLivingBranch; component: LightComponent; } @@ -57,10 +58,11 @@ export default class ApplicationQualityGate extends React.PureComponent { - const { component } = this.props; + const { branch, component } = this.props; this.setState({ loading: true }); getApplicationQualityGate({ application: component.key, + branch: branch ? branch.name : undefined, organization: component.organization }).then( ({ status, projects, metrics }) => { diff --git a/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/ApplicationQualityGateProject-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/ApplicationQualityGateProject-test.tsx.snap index b4c85030625..8af25b25049 100644 --- a/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/ApplicationQualityGateProject-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/overview/qualityGate/__tests__/__snapshots__/ApplicationQualityGateProject-test.tsx.snap @@ -9,6 +9,7 @@ exports[`renders 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "foo", }, } diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/WorstProjects-test.tsx.snap b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/WorstProjects-test.tsx.snap index ceb66108e3c..736ab248060 100644 --- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/WorstProjects-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/WorstProjects-test.tsx.snap @@ -53,6 +53,7 @@ exports[`renders 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "foo", }, } @@ -140,6 +141,7 @@ exports[`renders 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "barbar", }, } @@ -227,6 +229,7 @@ exports[`renders 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "bazbaz", }, } diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap index c1aac43c7d7..38408926a5b 100644 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap @@ -18,6 +18,7 @@ exports[`renders 1`] = ` "link": Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "foo", }, }, diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap index 0c07a84920e..fd133225a39 100644 --- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap @@ -18,6 +18,7 @@ exports[`renders 1`] = ` "link": Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "foo", }, }, diff --git a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap index 98ca96135f8..aead0a2ce17 100644 --- a/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/CreateProjectForm-test.tsx.snap @@ -352,6 +352,7 @@ exports[`creates project 4`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "name", }, } diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/MeasuresOverlayCoveredFiles-test.tsx.snap b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/MeasuresOverlayCoveredFiles-test.tsx.snap index b053a1bf9af..5552a961caa 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/MeasuresOverlayCoveredFiles-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/MeasuresOverlayCoveredFiles-test.tsx.snap @@ -55,6 +55,7 @@ exports[`should render OK test 1`] = ` Object { "pathname": "/dashboard", "query": Object { + "branch": undefined, "id": "project:src/file.js", }, } diff --git a/server/sonar-web/src/main/js/helpers/branches.ts b/server/sonar-web/src/main/js/helpers/branches.ts index 6b4507eddcd..86dc7e10671 100644 --- a/server/sonar-web/src/main/js/helpers/branches.ts +++ b/server/sonar-web/src/main/js/helpers/branches.ts @@ -168,3 +168,21 @@ export function getBranchLikeQuery(branchLike?: BranchLike): BranchParameters { return {}; } } + +// Create branch object from branch name or pull request key +export function fillBranchLike( + branch?: string, + pullRequest?: string +): ShortLivingBranch | PullRequest | undefined { + if (branch) { + return { + isMain: false, + mergeBranch: '', + name: branch, + type: BranchType.SHORT + } as ShortLivingBranch; + } else if (pullRequest) { + return { base: '', branch: '', key: pullRequest, title: '' } as PullRequest; + } + return undefined; +} diff --git a/server/sonar-web/src/main/js/helpers/urls.ts b/server/sonar-web/src/main/js/helpers/urls.ts index 93e531c52df..4d17bde0b09 100644 --- a/server/sonar-web/src/main/js/helpers/urls.ts +++ b/server/sonar-web/src/main/js/helpers/urls.ts @@ -53,8 +53,8 @@ export function getSonarCloudUrlAsString(location: Location) { return 'https://sonarcloud.io' + getPathUrlAsString(location); } -export function getProjectUrl(project: string): Location { - return { pathname: '/dashboard', query: { id: project } }; +export function getProjectUrl(project: string, branch?: string): Location { + return { pathname: '/dashboard', query: { id: project, branch } }; } export function getPortfolioUrl(key: string): Location { @@ -231,7 +231,9 @@ export function getOrganizationUrl(organization: string) { export function getHomePageUrl(homepage: HomePage) { switch (homepage.type) { case HomePageType.Application: - return getProjectUrl(homepage.component); + return homepage.branch + ? getProjectUrl(homepage.component, homepage.branch) + : getProjectUrl(homepage.component); case HomePageType.Project: return homepage.branch ? getLongLivingBranchUrl(homepage.component, homepage.branch) diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 65e77118171..79d87fab775 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -498,6 +498,8 @@ deletion.page=Deletion project_deletion.page.description=Delete this project. The operation cannot be undone. portfolio_deletion.page.description=This portfolio and its sub-portfolios will be deleted. If this portfolio is referenced by other entities, it will be removed from them. Independent entities referenced by this portfolio, such as projects and other top-level portfolios will not be deleted. This operation cannot be undone. application_deletion.page.description=Delete this application. Application projects will not be deleted. Projects referenced by this application will not be deleted. This operation cannot be undone. +application.branches.help=Easily create Application branches composed of the branches of projects in your application. +application.branches.link=Create Branch project_branches.page=Branches & Pull Requests project_branches.page.description=Use this page to manage project branches and pull requests. project_branches.page.life_time=Short-lived branches and pull requests are permanently deleted after {days} days without analysis. -- 2.39.5