瀏覽代碼

SONAR-9867 Add button promoting governance on background tasks page

tags/6.7-RC1
Stas Vilchik 6 年之前
父節點
當前提交
fed59c4f46

+ 1
- 1
server/sonar-web/src/main/js/api/ce.ts 查看文件

@@ -81,7 +81,7 @@ export function getTypes(): Promise<any> {
return getJSON('/api/ce/task_types').then(r => r.taskTypes);
}

export function getWorkers(): Promise<{ canSetWorkerCount: boolean; value: number } | Response> {
export function getWorkers(): Promise<{ canSetWorkerCount: boolean; value: number }> {
return getJSON('/api/ce/worker_count').catch(throwGlobalError);
}


+ 1
- 1
server/sonar-web/src/main/js/app/components/nav/component/NoBranchSupportPopup.tsx 查看文件

@@ -33,7 +33,7 @@ export default function NoBranchSupportPopup(props: Props) {
<p className="big-spacer-bottom markdown">{translate('branches.no_support.header.text')}</p>
<p>
<a href="https://redirect.sonarsource.com/doc/branches.html" target="_blank">
{translate('branches.learn_more')}
{translate('learn_more')}
</a>
<a
className="button spacer-left"

+ 1
- 1
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/NoBranchSupportPopup-test.tsx.snap 查看文件

@@ -22,7 +22,7 @@ exports[`renders 1`] = `
href="https://redirect.sonarsource.com/doc/branches.html"
target="_blank"
>
branches.learn_more
learn_more
</a>
<a
className="button spacer-left"

server/sonar-web/src/main/js/apps/background-tasks/components/Header.js → server/sonar-web/src/main/js/apps/background-tasks/components/Header.tsx 查看文件

@@ -17,18 +17,15 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/* @flow */
import React from 'react';
import * as React from 'react';
import Workers from './Workers';
import { translate } from '../../../helpers/l10n';

/*::
type Props = {
component?: Object
};
*/
interface Props {
component?: any;
}

export default function Header(props /*: Props */) {
export default function Header(props: Props) {
return (
<header className="page-header">
<h1 className="page-title">{translate('background_tasks.page')}</h1>

+ 44
- 0
server/sonar-web/src/main/js/apps/background-tasks/components/NoWorkersSupportPopup.tsx 查看文件

@@ -0,0 +1,44 @@
/*
* SonarQube
* Copyright (C) 2009-2016 SonarSource SA
* mailto:contact 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 BubblePopup from '../../../components/common/BubblePopup';
import { translate } from '../../../helpers/l10n';

interface Props {
popupPosition?: any;
}

export default function NoWorkersSupportPopup(props: Props) {
return (
<BubblePopup position={props.popupPosition} customClass="bubble-popup-bottom-right">
<div className="abs-width-400">
<h6 className="spacer-bottom">{translate('background_tasks.add_more_workers')}</h6>
<p className="big-spacer-bottom markdown">
{translate('background_tasks.add_more_workers.text')}
</p>
<p>
<a href="https://redirect.sonarsource.com/plugins/governance.html" target="_blank">
{translate('learn_more')}
</a>
</p>
</div>
</BubblePopup>
);
}

server/sonar-web/src/main/js/apps/background-tasks/components/Workers.js → server/sonar-web/src/main/js/apps/background-tasks/components/Workers.tsx 查看文件

@@ -17,28 +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.
*/
// @flow
import React from 'react';
import * as React from 'react';
import WorkersForm from './WorkersForm';
import NoWorkersSupportPopup from './NoWorkersSupportPopup';
import Tooltip from '../../../components/controls/Tooltip';
import { getWorkers } from '../../../api/ce';
import { translate } from '../../../helpers/l10n';
import HelpIcon from '../../../components/icons-components/HelpIcon';
import BubblePopupHelper from '../../../components/common/BubblePopupHelper';

/*::
type State = {
canSetWorkerCount: boolean,
formOpen: boolean,
loading: boolean,
workerCount: number
};
*/

export default class Workers extends React.PureComponent {
/*:: mounted: boolean; */
state /*: State */ = {
interface State {
canSetWorkerCount: boolean;
formOpen: boolean;
loading: boolean;
noSupportPopup: boolean;
workerCount: number;
}

export default class Workers extends React.PureComponent<{}, State> {
mounted: boolean;
state: State = {
canSetWorkerCount: false,
formOpen: false,
loading: true,
noSupportPopup: false,
workerCount: 1
};

@@ -64,16 +66,30 @@ export default class Workers extends React.PureComponent {
});
};

closeForm = (newWorkerCount /*: ?number */) =>
closeForm = (newWorkerCount?: number) =>
newWorkerCount
? this.setState({ formOpen: false, workerCount: newWorkerCount })
: this.setState({ formOpen: false });

handleChangeClick = (event /*: Event */) => {
handleChangeClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
this.setState({ formOpen: true });
};

handleHelpClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
event.stopPropagation();
this.toggleNoSupportPopup();
};

toggleNoSupportPopup = (show?: boolean) => {
if (show != undefined) {
this.setState({ noSupportPopup: show });
} else {
this.setState(state => ({ noSupportPopup: !state.noSupportPopup }));
}
};

render() {
const { canSetWorkerCount, formOpen, loading, workerCount } = this.state;

@@ -101,6 +117,21 @@ export default class Workers extends React.PureComponent {
</Tooltip>
)}

{!loading &&
!canSetWorkerCount && (
<span className="spacer-left">
<a className="link-no-underline" href="#" onClick={this.handleHelpClick}>
<HelpIcon className="text-text-bottom" fill="#cdcdcd" />
</a>
<BubblePopupHelper
isOpen={this.state.noSupportPopup}
position="bottomright"
popup={<NoWorkersSupportPopup />}
togglePopup={this.toggleNoSupportPopup}
/>
</span>
)}

{formOpen && <WorkersForm onClose={this.closeForm} workerCount={this.state.workerCount} />}
</div>
);

server/sonar-web/src/main/js/apps/background-tasks/components/WorkersForm.js → server/sonar-web/src/main/js/apps/background-tasks/components/WorkersForm.tsx 查看文件

@@ -17,36 +17,29 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// @flow
import React from 'react';
import * as React from 'react';
import Modal from 'react-modal';
import Select from 'react-select';
import * as Select from 'react-select';
import { times } from 'lodash';
import { setWorkerCount } from '../../../api/ce';
import { translate } from '../../../helpers/l10n';

const MAX_WORKERS = 10;

/*::
type Props = {
onClose: (newWorkerCount?: number) => void,
workerCount: number
};
*/
interface Props {
onClose: (newWorkerCount?: number) => void;
workerCount: number;
}

/*::
type State = {
newWorkerCount: number,
submitting: boolean
};
*/
interface State {
newWorkerCount: number;
submitting: boolean;
}

export default class WorkersForm extends React.PureComponent {
/*:: mounted: boolean; */
/*:: props: Props; */
/*:: state: State; */
export default class WorkersForm extends React.PureComponent<Props, State> {
mounted: boolean;

constructor(props /*: Props */) {
constructor(props: Props) {
super(props);
this.state = {
newWorkerCount: props.workerCount,
@@ -64,10 +57,10 @@ export default class WorkersForm extends React.PureComponent {

handleClose = () => this.props.onClose();

handleWorkerCountChange = (option /*: { value: number } */) =>
handleWorkerCountChange = (option: { value: number }) =>
this.setState({ newWorkerCount: option.value });

handleSubmit = (event /*: Event */) => {
handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();
this.setState({ submitting: true });
const { newWorkerCount } = this.state;
@@ -86,7 +79,7 @@ export default class WorkersForm extends React.PureComponent {
};

render() {
const options = times(MAX_WORKERS).map((_, i) => ({ label: i + 1, value: i + 1 }));
const options = times(MAX_WORKERS).map((_, i) => ({ label: String(i + 1), value: i + 1 }));

return (
<Modal

server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Workers-test.js → server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/Workers-test.tsx 查看文件

@@ -17,8 +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.
*/
// @flow
import React from 'react';
import * as React from 'react';
import { shallow } from 'enzyme';
import Workers from '../Workers';
import { click } from '../../../../helpers/testUtils';
@@ -34,10 +33,10 @@ it('renders', () => {
});
expect(wrapper).toMatchSnapshot();

wrapper.setState({ canSetWorkerCount: false });
wrapper.setState({ workerCount: 2 });
expect(wrapper).toMatchSnapshot();

wrapper.setState({ workerCount: 2 });
wrapper.setState({ canSetWorkerCount: false });
expect(wrapper).toMatchSnapshot();
});

@@ -66,7 +65,7 @@ it('updates worker count', () => {
});
expect(wrapper).toMatchSnapshot();

wrapper.find('WorkersForm').prop('onClose')(7);
wrapper.find('WorkersForm').prop<Function>('onClose')(7);
wrapper.update();
expect(wrapper).toMatchSnapshot();
});

server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/WorkersForm-test.js → server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/WorkersForm-test.tsx 查看文件

@@ -17,36 +17,33 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// @flow
import React from 'react';
import { shallow } from 'enzyme';
import WorkersForm from '../WorkersForm';
import { submit, doAsync } from '../../../../helpers/testUtils';

jest.mock('../../../../api/ce', () => ({
setWorkerCount: () => Promise.resolve()
}));

import * as React from 'react';
import { shallow } from 'enzyme';
import WorkersForm from '../WorkersForm';
import { submit } from '../../../../helpers/testUtils';

it('changes select', () => {
const wrapper = shallow(<WorkersForm onClose={jest.fn()} workerCount={1} />);
expect(wrapper).toMatchSnapshot();

wrapper.find('Select').prop('onChange')({ value: 7 });
wrapper.find('Select').prop<Function>('onChange')({ value: 7 });
wrapper.update();
expect(wrapper).toMatchSnapshot();
});

it('returns new worker count', () => {
it('returns new worker count', async () => {
const onClose = jest.fn();
const wrapper = shallow(<WorkersForm onClose={onClose} workerCount={1} />);
// $FlowFixMe
wrapper.instance().mounted = true;
wrapper.find('Select').prop('onChange')({ value: 7 });
(wrapper.instance() as WorkersForm).mounted = true;
wrapper.find('Select').prop<Function>('onChange')({ value: 7 });

wrapper.update();
submit(wrapper.find('form'));

return doAsync(() => {
expect(onClose).toBeCalled();
});
await new Promise(setImmediate);
expect(onClose).toBeCalled();
});

server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Workers-test.js.snap → server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/Workers-test.tsx.snap 查看文件

@@ -78,12 +78,30 @@ exports[`renders 2`] = `

exports[`renders 3`] = `
<div>
<Tooltip
overlay="background_tasks.number_of_workers.warning"
placement="bottom"
>
<i
className="icon-alert-warn little-spacer-right bt-workers-warning-icon"
/>
</Tooltip>
background_tasks.number_of_workers
<strong
className="little-spacer-left"
>
1
2
</strong>
<Tooltip
overlay="background_tasks.change_number_of_workers"
placement="bottom"
>
<a
className="icon-edit spacer-left"
href="#"
onClick={[Function]}
/>
</Tooltip>
</div>
`;

@@ -103,6 +121,26 @@ exports[`renders 4`] = `
>
2
</strong>
<span
className="spacer-left"
>
<a
className="link-no-underline"
href="#"
onClick={[Function]}
>
<HelpIcon
className="text-text-bottom"
fill="#cdcdcd"
/>
</a>
<BubblePopupHelper
isOpen={false}
popup={<NoWorkersSupportPopup />}
position="bottomright"
togglePopup={[Function]}
/>
</span>
</div>
`;


server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/WorkersForm-test.js.snap → server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/WorkersForm-test.tsx.snap 查看文件

@@ -62,43 +62,43 @@ exports[`changes select 1`] = `
options={
Array [
Object {
"label": 1,
"label": "1",
"value": 1,
},
Object {
"label": 2,
"label": "2",
"value": 2,
},
Object {
"label": 3,
"label": "3",
"value": 3,
},
Object {
"label": 4,
"label": "4",
"value": 4,
},
Object {
"label": 5,
"label": "5",
"value": 5,
},
Object {
"label": 6,
"label": "6",
"value": 6,
},
Object {
"label": 7,
"label": "7",
"value": 7,
},
Object {
"label": 8,
"label": "8",
"value": 8,
},
Object {
"label": 9,
"label": "9",
"value": 9,
},
Object {
"label": 10,
"label": "10",
"value": 10,
},
]
@@ -205,43 +205,43 @@ exports[`changes select 2`] = `
options={
Array [
Object {
"label": 1,
"label": "1",
"value": 1,
},
Object {
"label": 2,
"label": "2",
"value": 2,
},
Object {
"label": 3,
"label": "3",
"value": 3,
},
Object {
"label": 4,
"label": "4",
"value": 4,
},
Object {
"label": 5,
"label": "5",
"value": 5,
},
Object {
"label": 6,
"label": "6",
"value": 6,
},
Object {
"label": 7,
"label": "7",
"value": 7,
},
Object {
"label": 8,
"label": "8",
"value": 8,
},
Object {
"label": 9,
"label": "9",
"value": 9,
},
Object {
"label": 10,
"label": "10",
"value": 10,
},
]

+ 1
- 3
server/sonar-web/src/main/less/components/bubble-popup.less 查看文件

@@ -90,13 +90,11 @@
.bubble-popup-bottom-right {
.bubble-popup-bottom;
margin-left: 0;
margin-right: -@popupArrowSize;
margin-right: -16px;

.bubble-popup-arrow {
left: auto;
right: 15px;
border-right-width: 0;
border-left-color: barBorderColor;
}
}


+ 3
- 2
sonar-core/src/main/resources/org/sonar/l10n/core.properties 查看文件

@@ -76,6 +76,7 @@ issues=Issues
inheritance=Inheritance
key=Key
language=Language
learn_more=Learn More
library=Library
line_number=Line Number
links=Links
@@ -2211,7 +2212,8 @@ background_tasks.number_of_workers=Number of Workers:
background_tasks.number_of_workers.warning=Configuring additional workers without first vertically scaling your server could have negative performance impacts.
background_tasks.change_number_of_workers=Edit CE Workers
background_tasks.change_number_of_workers.hint=If your queue backs up behind the analysis reports from large projects, increasing the number of Compute Engine workers will allow you to take full advantage of having configured increased Compute Engine memory on a multi-core server (vertical scaling).
background_tasks.add_more_with_governance=Add more with Governance
background_tasks.add_more_workers=Speed up your analysis by adding more Workers
background_tasks.add_more_workers.text=Increase the number of Compute Engine Workers with the Governance product. Available in our commercial editions.
background_tasks.search_by_task_or_component=Search by Task or Component
background_tasks.failing_count=Count of projects where processing of most recent analysis report failed

@@ -2580,7 +2582,6 @@ branches.set_leak_period=Set Leak Period
branches.last_analysis_date=Last Analysis Date
branches.no_support.header=Get the most out of SonarQube with branches analysis
branches.no_support.header.text=Analyze each branch of your project separately with our Developer Pack.
branches.learn_more=Learn More
branches.buy_developer_pack=Buy Developer Pack



Loading…
取消
儲存