* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { getJSON } from '../helpers/request';
+import throwGlobalError from '../app/utils/throwGlobalError';
export interface Changelog {
description: string;
format: string;
}
-export function fetchWebApi(showInternal: boolean = true): Promise<Array<Domain>> {
- return getJSON('/api/webservices/list', { include_internals: showInternal }).then(r =>
- r.webServices.map((domain: any) => {
- const deprecated = !domain.actions.find((action: any) => !action.deprecatedSince);
- const internal = !domain.actions.find((action: any) => !action.internal);
- return { ...domain, deprecated, internal };
- })
- );
+export function fetchWebApi(showInternal = true): Promise<Domain[]> {
+ return getJSON('/api/webservices/list', { include_internals: showInternal })
+ .then(r =>
+ r.webServices.map((domain: any) => {
+ const deprecated = !domain.actions.find((action: any) => !action.deprecatedSince);
+ const internal = !domain.actions.find((action: any) => !action.internal);
+ return { ...domain, deprecated, internal };
+ })
+ )
+ .catch(throwGlobalError);
}
export function fetchResponseExample(domain: string, action: string): Promise<Example> {
- return getJSON('/api/webservices/response_example', { controller: domain, action });
+ return getJSON('/api/webservices/response_example', { controller: domain, action }).catch(
+ throwGlobalError
+ );
}
margin-top: 16px;
}
+.huge-spacer-bottom {
+ margin-bottom: 40px;
+}
+
.huge-spacer-top {
margin-top: 40px;
}
gray71: '#b4b4b4',
gray67: '#aaa',
gray60: '#999',
+ gray40: '#404040',
barBackgroundColor: '#f3f3f3',
barBorderColor: '#e6e6e6',
leakColor: '#fbf3d5',
leakBorderColor: '#eae3c7',
+ snippetFontColor: '#f0f0f0',
+
// sizes
gridSize: `${grid}px`,
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 * as classNames from 'classnames';
+import { BadgeType } from './utils';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+ onClick: (type: BadgeType) => void;
+ selected: boolean;
+ type: BadgeType;
+ url: string;
+}
+
+export default class BadgeButton extends React.PureComponent<Props> {
+ handleClick = () => this.props.onClick(this.props.type);
+
+ render() {
+ const { selected, type, url } = this.props;
+ const width = type !== BadgeType.measure ? '128px' : undefined;
+ return (
+ <button className={classNames('badge-button', { selected })} onClick={this.handleClick}>
+ <img src={url} alt={translate('overview.badges', type, 'alt')} width={width} />
+ </button>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { connect } from 'react-redux';
+import Select from '../../../components/controls/Select';
+import { fetchWebApi } from '../../../api/web-api';
+import { Metric } from '../../../app/types';
+import { BadgeColors, BadgeType, BadgeOptions } from './utils';
+import { getLocalizedMetricName, translate } from '../../../helpers/l10n';
+import { fetchMetrics } from '../../../store/rootActions';
+import { getMetrics } from '../../../store/rootReducer';
+
+interface StateToProps {
+ metrics: { [key: string]: Metric };
+}
+
+interface DispatchToProps {
+ fetchMetrics: () => void;
+}
+
+interface OwnProps {
+ className?: string;
+ options: BadgeOptions;
+ type: BadgeType;
+ updateOptions: (options: Partial<BadgeOptions>) => void;
+}
+
+type Props = StateToProps & DispatchToProps & OwnProps;
+
+interface State {
+ badgeMetrics: string[];
+}
+
+export class BadgeParams extends React.PureComponent<Props> {
+ mounted: boolean;
+ state: State = { badgeMetrics: [] };
+
+ componentDidMount() {
+ this.mounted = true;
+ this.props.fetchMetrics();
+ this.fetchBadgeMetrics();
+ }
+
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
+ fetchBadgeMetrics() {
+ fetchWebApi(false).then(
+ webservices => {
+ if (this.mounted) {
+ const domain = webservices.find(domain => domain.path === 'api/project_badges');
+ const ws = domain && domain.actions.find(ws => ws.key === 'measure');
+ const param = ws && ws.params && ws.params.find(param => param.key === 'metric');
+ if (param && param.possibleValues) {
+ this.setState({ badgeMetrics: param.possibleValues });
+ }
+ }
+ },
+ () => {}
+ );
+ }
+
+ getColorOptions = () =>
+ ['white', 'black', 'orange'].map(color => ({
+ label: translate('overview.badges.options.colors', color),
+ value: color
+ }));
+
+ getMetricOptions = () => {
+ const { metrics } = this.props;
+ return this.state.badgeMetrics.map(key => {
+ const metric = metrics[key];
+ return {
+ value: key,
+ label: metric && getLocalizedMetricName(metric)
+ };
+ });
+ };
+
+ handleColorChange = ({ value }: { value: BadgeColors }) =>
+ this.props.updateOptions({ color: value });
+
+ handleMetricChange = ({ value }: { value: string }) =>
+ this.props.updateOptions({ metric: value });
+
+ render() {
+ const { className, options, type } = this.props;
+ switch (type) {
+ case BadgeType.marketing:
+ return (
+ <div className={className}>
+ <label className="big-spacer-right" htmlFor="badge-color">
+ {translate('color')}
+ </label>
+ <Select
+ className="input-medium"
+ clearable={false}
+ name="badge-color"
+ onChange={this.handleColorChange}
+ options={this.getColorOptions()}
+ searchable={false}
+ value={options.color}
+ />
+ </div>
+ );
+ case BadgeType.measure:
+ return (
+ <div className={className}>
+ <label className="big-spacer-right" htmlFor="badge-metric">
+ {translate('overview.badges.metric')}
+ </label>
+ <Select
+ className="input-medium"
+ clearable={false}
+ name="badge-metric"
+ onChange={this.handleMetricChange}
+ options={this.getMetricOptions()}
+ searchable={false}
+ value={options.metric}
+ />
+ </div>
+ );
+ default:
+ return null;
+ }
+ }
+}
+
+const mapDispatchToProps: DispatchToProps = { fetchMetrics };
+
+const mapStateToProps = (state: any): StateToProps => ({
+ metrics: getMetrics(state)
+});
+
+export default connect<StateToProps, DispatchToProps, OwnProps>(
+ mapStateToProps,
+ mapDispatchToProps
+)(BadgeParams);
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 ClipboardButton from '../../../components/controls/ClipboardButton';
+
+interface Props {
+ snippet: string;
+}
+
+export default function BadgeSnippet({ snippet }: Props) {
+ return (
+ <div className="badge-snippet">
+ <pre>{snippet}</pre>
+ <ClipboardButton copyValue={snippet} tooltipPlacement="top" />
+ </div>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 Modal from '../../../components/controls/Modal';
+import BadgeButton from './BadgeButton';
+import BadgeSnippet from './BadgeSnippet';
+import BadgeParams from './BadgeParams';
+import { getBadgeUrl, BadgeType, BadgeOptions } from './utils';
+import { translate } from '../../../helpers/l10n';
+import './styles.css';
+
+interface Props {
+ branch: string;
+ project: string;
+}
+
+interface State {
+ open: boolean;
+ selectedType: BadgeType;
+ badgeOptions: BadgeOptions;
+}
+
+export default class BadgesModal extends React.PureComponent<Props, State> {
+ state: State = {
+ open: false,
+ selectedType: BadgeType.measure,
+ badgeOptions: { color: 'white', metric: 'alert_status' }
+ };
+
+ handleClose = () => this.setState({ open: false });
+
+ handleOpen = () => this.setState({ open: true });
+
+ handleSelectBadge = (selectedType: BadgeType) => this.setState({ selectedType });
+
+ handleUpdateOptions = (options: Partial<BadgeOptions>) =>
+ this.setState(state => ({
+ badgeOptions: { ...state.badgeOptions, ...options }
+ }));
+
+ handleCancelClick = () => this.handleClose();
+
+ render() {
+ const { branch, project } = this.props;
+ const { selectedType, badgeOptions } = this.state;
+ const header = translate('overview.badges.title');
+ const fullBadgeOptions = { branch, project, ...badgeOptions };
+ return (
+ <>
+ <button onClick={this.handleOpen}>{translate('overview.badges.get_badge')}</button>
+ {this.state.open && (
+ <Modal contentLabel={header} onRequestClose={this.handleClose}>
+ <header className="modal-head">
+ <h2>{header}</h2>
+ </header>
+ <div className="modal-body">
+ <p className="huge-spacer-bottom">{translate('overview.badges.description')}</p>
+ <div className="badges-list spacer-bottom">
+ {[BadgeType.measure, BadgeType.qualityGate, BadgeType.marketing].map(type => (
+ <BadgeButton
+ key={type}
+ onClick={this.handleSelectBadge}
+ selected={type === selectedType}
+ type={type}
+ url={getBadgeUrl(type, fullBadgeOptions)}
+ />
+ ))}
+ </div>
+ <p className="text-center note huge-spacer-bottom">
+ {translate('overview.badges', selectedType, 'description')}
+ </p>
+ <BadgeParams
+ className="big-spacer-bottom"
+ options={badgeOptions}
+ type={selectedType}
+ updateOptions={this.handleUpdateOptions}
+ />
+ <BadgeSnippet snippet={getBadgeUrl(selectedType, fullBadgeOptions)} />
+ </div>
+ <footer className="modal-foot">
+ <button className="button-link js-modal-close" onClick={this.handleCancelClick}>
+ {translate('close')}
+ </button>
+ </footer>
+ </Modal>
+ )}
+ </>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { shallow } from 'enzyme';
+import BadgeButton from '../BadgeButton';
+import { BadgeType } from '../utils';
+import { click } from '../../../../helpers/testUtils';
+
+it('should display correctly', () => {
+ expect(getWrapper()).toMatchSnapshot();
+ expect(getWrapper({ selected: true })).toMatchSnapshot();
+ expect(getWrapper({ type: BadgeType.measure })).toMatchSnapshot();
+});
+
+it('should return the badge type on click', () => {
+ const onClick = jest.fn();
+ const wrapper = getWrapper({ onClick });
+ click(wrapper.find('button'));
+ expect(onClick).toHaveBeenCalledWith(BadgeType.marketing);
+});
+
+function getWrapper(props = {}) {
+ return shallow(
+ <BadgeButton
+ onClick={jest.fn()}
+ selected={false}
+ type={BadgeType.marketing}
+ url="http://foo.bar"
+ {...props}
+ />
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { shallow } from 'enzyme';
+import { BadgeParams } from '../BadgeParams';
+import { BadgeType } from '../utils';
+import { Metric } from '../../../../app/types';
+
+jest.mock('../../../../api/web-api', () => ({
+ fetchWebApi: () =>
+ Promise.resolve([
+ {
+ path: 'api/project_badges',
+ actions: [
+ {
+ key: 'measure',
+ params: [{ key: 'metric', possibleValues: ['alert_status', 'coverage'] }]
+ }
+ ]
+ }
+ ])
+}));
+
+const METRICS = {
+ alert_status: { key: 'alert_status', name: 'Quality Gate' } as Metric,
+ coverage: { key: 'coverage', name: 'Coverage' } as Metric
+};
+
+it('should display marketing badge params', () => {
+ const updateOptions = jest.fn();
+ const wrapper = getWrapper({ updateOptions });
+ expect(wrapper).toMatchSnapshot();
+ (wrapper.instance() as BadgeParams).handleColorChange({ value: 'black' });
+ expect(updateOptions).toHaveBeenCalledWith({ color: 'black' });
+});
+
+it('should display measure badge params', () => {
+ const updateOptions = jest.fn();
+ const wrapper = getWrapper({ updateOptions, type: BadgeType.measure });
+ expect(wrapper).toMatchSnapshot();
+ (wrapper.instance() as BadgeParams).handleColorChange({ value: 'black' });
+ expect(updateOptions).toHaveBeenCalledWith({ color: 'black' });
+});
+
+function getWrapper(props = {}) {
+ return shallow(
+ <BadgeParams
+ fetchMetrics={jest.fn()}
+ metrics={METRICS}
+ options={{ color: 'white', metric: 'alert_status' }}
+ type={BadgeType.marketing}
+ updateOptions={jest.fn()}
+ {...props}
+ />
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { shallow } from 'enzyme';
+import BadgesModal from '../BadgesModal';
+import { click } from '../../../../helpers/testUtils';
+
+jest.mock('../../../../helpers/urls', () => ({
+ getHostUrl: () => 'host'
+}));
+
+it('should display the modal after click', () => {
+ const wrapper = shallow(<BadgesModal branch="branch-6.6" project="foo" />);
+ expect(wrapper).toMatchSnapshot();
+ click(wrapper.find('button'));
+ expect(wrapper.find('Modal')).toMatchSnapshot();
+});
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should display correctly 1`] = `
+<button
+ className="badge-button"
+ onClick={[Function]}
+>
+ <img
+ alt="overview.badges.marketing.alt"
+ src="http://foo.bar"
+ width="128px"
+ />
+</button>
+`;
+
+exports[`should display correctly 2`] = `
+<button
+ className="badge-button selected"
+ onClick={[Function]}
+>
+ <img
+ alt="overview.badges.marketing.alt"
+ src="http://foo.bar"
+ width="128px"
+ />
+</button>
+`;
+
+exports[`should display correctly 3`] = `
+<button
+ className="badge-button"
+ onClick={[Function]}
+>
+ <img
+ alt="overview.badges.measure.alt"
+ src="http://foo.bar"
+ />
+</button>
+`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should display marketing badge params 1`] = `
+<div>
+ <label
+ className="big-spacer-right"
+ htmlFor="badge-color"
+ >
+ color
+ </label>
+ <Select
+ className="input-medium"
+ clearable={false}
+ name="badge-color"
+ onChange={[Function]}
+ options={
+ Array [
+ Object {
+ "label": "overview.badges.options.colors.white",
+ "value": "white",
+ },
+ Object {
+ "label": "overview.badges.options.colors.black",
+ "value": "black",
+ },
+ Object {
+ "label": "overview.badges.options.colors.orange",
+ "value": "orange",
+ },
+ ]
+ }
+ searchable={false}
+ value="white"
+ />
+</div>
+`;
+
+exports[`should display measure badge params 1`] = `
+<div>
+ <label
+ className="big-spacer-right"
+ htmlFor="badge-metric"
+ >
+ overview.badges.metric
+ </label>
+ <Select
+ className="input-medium"
+ clearable={false}
+ name="badge-metric"
+ onChange={[Function]}
+ options={Array []}
+ searchable={false}
+ value="alert_status"
+ />
+</div>
+`;
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should display the modal after click 1`] = `
+<React.Fragment>
+ <button
+ onClick={[Function]}
+ >
+ overview.badges.get_badge
+ </button>
+</React.Fragment>
+`;
+
+exports[`should display the modal after click 2`] = `
+<Modal
+ contentLabel="overview.badges.title"
+ onRequestClose={[Function]}
+>
+ <header
+ className="modal-head"
+ >
+ <h2>
+ overview.badges.title
+ </h2>
+ </header>
+ <div
+ className="modal-body"
+ >
+ <p
+ className="huge-spacer-bottom"
+ >
+ overview.badges.description
+ </p>
+ <div
+ className="badges-list spacer-bottom"
+ >
+ <BadgeButton
+ key="measure"
+ onClick={[Function]}
+ selected={true}
+ type="measure"
+ url="host/api/project_badges/measure?branch=branch-6.6&project=foo&metric=alert_status"
+ />
+ <BadgeButton
+ key="quality_gate"
+ onClick={[Function]}
+ selected={false}
+ type="quality_gate"
+ url="host/api/project_badges/quality_gate?branch=branch-6.6&project=foo"
+ />
+ <BadgeButton
+ key="marketing"
+ onClick={[Function]}
+ selected={false}
+ type="marketing"
+ url="host/images/project_badges/sonarcloud-white.svg"
+ />
+ </div>
+ <p
+ className="text-center note huge-spacer-bottom"
+ >
+ overview.badges.measure.description
+ </p>
+ <Connect(BadgeParams)
+ className="big-spacer-bottom"
+ options={
+ Object {
+ "color": "white",
+ "metric": "alert_status",
+ }
+ }
+ type="measure"
+ updateOptions={[Function]}
+ />
+ <BadgeSnippet
+ snippet="host/api/project_badges/measure?branch=branch-6.6&project=foo&metric=alert_status"
+ />
+ </div>
+ <footer
+ className="modal-foot"
+ >
+ <button
+ className="button-link js-modal-close"
+ onClick={[Function]}
+ >
+ close
+ </button>
+ </footer>
+</Modal>
+`;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { getBadgeUrl, BadgeOptions, BadgeType } from '../utils';
+
+jest.mock('../../../../helpers/urls', () => ({
+ getHostUrl: () => 'host'
+}));
+
+const options: BadgeOptions = {
+ branch: 'master',
+ color: 'white',
+ project: 'foo',
+ metric: 'alert_status'
+};
+
+describe('#getBadgeUrl', () => {
+ it('should generate correct marketing badge links', () => {
+ expect(getBadgeUrl(BadgeType.marketing, options)).toBe(
+ 'host/images/project_badges/sonarcloud-white.svg'
+ );
+ expect(getBadgeUrl(BadgeType.marketing, { ...options, color: 'orange' })).toBe(
+ 'host/images/project_badges/sonarcloud-orange.svg'
+ );
+ });
+
+ it('should generate correct quality gate badge links', () => {
+ expect(getBadgeUrl(BadgeType.qualityGate, options));
+ });
+
+ it('should generate correct measures badge links', () => {
+ expect(getBadgeUrl(BadgeType.measure, options)).toBe(
+ 'host/api/project_badges/measure?branch=master&project=foo&metric=alert_status'
+ );
+ });
+
+ it('should ignore undefined parameters', () => {
+ expect(getBadgeUrl(BadgeType.measure, { color: 'white', metric: 'alert_status' })).toBe(
+ 'host/api/project_badges/measure?metric=alert_status'
+ );
+ });
+});
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.
+ */
+.badges-list {
+ display: flex;
+ justify-content: space-around;
+ justify-content: space-evenly;
+ flex-wrap: nowrap;
+}
+
+.badge-button {
+ display: flex;
+ justify-content: center;
+ padding: var(--gridSize);
+ min-width: 146px;
+ height: 116px;
+ background-color: var(--barBackgroundColor);
+ border: solid 1px var(--barBorderColor);
+ border-radius: 3px;
+ transition: all 0.3s ease;
+}
+
+.badge-button:hover,
+.badge-button:focus,
+.badge-button:active {
+ background-color: var(--barBackgroundColor);
+ border-color: var(--blue);
+ box-shadow: none;
+}
+
+.badge-button.selected {
+ background-color: var(--lightBlue);
+ border-color: var(--darkBlue);
+}
+
+.badge-snippet {
+ position: relative;
+ margin: var(--gridSize) 0;
+ background: var(--gray40);
+ color: var(--snippetFontColor);
+ border-radius: 3px;
+}
+
+.badge-snippet pre {
+ padding: calc(2 * var(--gridSize));
+ padding-bottom: 40px;
+ overflow: auto;
+}
+
+.badge-snippet button {
+ position: absolute;
+ top: auto;
+ top: 40px;
+ right: calc(2 * var(--gridSize));
+ height: var(--smallControlHeight);
+ line-height: 18px;
+ border: 1px solid #fff;
+ color: #fff;
+ font-size: 11px;
+ font-weight: normal;
+ user-select: none;
+}
+
+.badge-snippet button:hover,
+.badge-snippet button:focus,
+.badge-snippet button:active {
+ background-color: #fff;
+ color: var(--gray40);
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 { stringify } from 'querystring';
+import { omitNil } from '../../../helpers/request';
+import { getHostUrl } from '../../../helpers/urls';
+
+export type BadgeColors = 'white' | 'black' | 'orange';
+
+export interface BadgeOptions {
+ branch?: string;
+ color?: BadgeColors;
+ project?: string;
+ metric?: string;
+}
+
+export enum BadgeType {
+ measure = 'measure',
+ qualityGate = 'quality_gate',
+ marketing = 'marketing'
+}
+
+export function getBadgeUrl(
+ type: BadgeType,
+ { branch, project, color = 'white', metric = 'alert_status' }: BadgeOptions
+) {
+ switch (type) {
+ case BadgeType.marketing:
+ return `${getHostUrl()}/images/project_badges/sonarcloud-${color}.svg`;
+ case BadgeType.qualityGate:
+ return `${getHostUrl()}/api/project_badges/quality_gate?${stringify(
+ omitNil({ branch, project })
+ )}`;
+ case BadgeType.measure:
+ default:
+ return `${getHostUrl()}/api/project_badges/measure?${stringify(
+ omitNil({ branch, project, metric })
+ )}`;
+ }
+}
import AnalysesList from '../events/AnalysesList';
import MetaSize from './MetaSize';
import MetaTags from './MetaTags';
-import { areThereCustomOrganizations } from '../../../store/rootReducer';
+import BadgesModal from '../badges/BadgesModal';
+import { areThereCustomOrganizations, getGlobalSettingValue } from '../../../store/rootReducer';
+import { Visibility } from '../../../app/types';
const Meta = ({
branch,
onComponentChange,
onSonarCloud
}) => {
- const { qualifier, description, qualityProfiles, qualityGate } = component;
+ const { qualifier, description, qualityProfiles, qualityGate, visibility } = component;
const isProject = qualifier === 'TRK';
+ const isPrivate = visibility === Visibility.Private;
const hasDescription = !!description;
const hasQualityProfiles = Array.isArray(qualityProfiles) && qualityProfiles.length > 0;
<MetaKey component={component} />
{hasOrganization && <MetaOrganizationKey component={component} />}
+
+ {onSonarCloud &&
+ isProject &&
+ !isPrivate && <BadgesModal branch={branch} project={component.key} />}
</div>
);
};
.onboarding-command pre {
padding: 15px;
border-radius: 2px;
- background: #404040;
- color: #f0f0f0;
+ background: var(--gray40);
+ color: var(--snippetFontColor);
overflow: auto;
}
.onboarding-command button:focus,
.onboarding-command button:active {
background-color: #fff;
- color: #404040;
+ color: var(--gray40);
}
.onboarding-command-oneline pre {
fetchResponseExample() {
const { domain, action } = this.props;
- fetchResponseExampleApi(domain.path, action.key).then(responseExample =>
- this.setState({ responseExample })
+ fetchResponseExampleApi(domain.path, action.key).then(
+ responseExample => this.setState({ responseExample }),
+ () => {}
);
}
}
}
- fetchList(cb?: () => void) {
- fetchWebApi().then(domains => {
- if (this.mounted) {
- this.setState({ domains }, cb);
- }
- });
+ fetchList() {
+ fetchWebApi().then(
+ domains => {
+ if (this.mounted) {
+ this.setState({ domains });
+ }
+ },
+ () => {}
+ );
}
scrollToAction = () => {
margin-top: -1px;
padding: 5px 10px;
border-top: 1px solid #e0e0e0;
- color: #404040;
+ color: var(--gray40);
transition: transform 0.3s ease;
}
componentWillUnmount() {
this.mounted = false;
- this.clipboard.destroy();
+ if (this.clipboard) {
+ this.clipboard.destroy();
+ }
}
showTooltip = () => {
vertical-align: middle;
margin-right: 10px;
padding: 3px 10px;
- background-color: #404040;
+ background-color: var(--gray40);
color: #fff;
font-size: var(--smallFontSize);
font-weight: 300;
height: 30px;
padding: 3px 10px;
box-sizing: border-box;
- background-color: #404040;
+ background-color: var(--gray40);
color: #fff;
font-weight: 300;
}
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 535 122" viewBox="0 0 535 122" width="128" height="30">
+<path d="m523.1 120h-510.7c-5.7 0-10.4-4.7-10.4-10.4v-97.2c0-5.7 4.7-10.4 10.4-10.4h510.7c5.7 0 10.4 4.7 10.4 10.4v97.2c0 5.7-4.7 10.4-10.4 10.4z" fill="#070706"/>
+<g fill="#fff">
+ <path d="m135 89.6c2.4 1.5 7.5 3.2 11.4 3.2 4 0 5.6-1.4 5.6-3.6s-1.3-3.2-6.2-4.9c-8.8-2.9-12.1-7.7-12-12.7 0-7.9 6.8-13.9 17.2-13.9 4.9 0 9.4 1.1 12 2.4l-2.3 9.1c-1.9-1-5.5-2.4-9.2-2.4-3.2 0-5 1.3-5 3.5 0 2 1.6 3 6.8 4.9 8.1 2.8 11.4 6.8 11.5 13.1 0 7.9-6.2 13.7-18.4 13.7-5.5 0-10.5-1.2-13.7-2.9z"/>
+ <path d="m211.1 79.4c0 15.5-11 22.6-22.4 22.6-12.4 0-21.9-8.1-21.9-21.8s9-22.4 22.6-22.4c13.1 0 21.7 8.9 21.7 21.6zm-30.6.5c0 7.3 3 12.7 8.7 12.7 5.1 0 8.4-5.1 8.4-12.7 0-6.3-2.4-12.7-8.4-12.7-6.4-.1-8.7 6.4-8.7 12.7z"/>
+ <path d="m215.8 72.2c0-5.3-.2-9.8-.3-13.5h11.4l.6 5.8h.3c1.7-2.7 6.1-6.8 13.1-6.8 8.7 0 15.2 5.7 15.2 18.2v25.1h-13.2v-23.4c0-5.5-1.9-9.2-6.7-9.2-3.6 0-5.8 2.5-6.7 4.9-.3.8-.5 2.1-.5 3.3v24.4h-13.2z"/>
+ <path d="m287.2 101.1-.8-4.2h-.3c-2.8 3.4-7.1 5.2-12.1 5.2-8.6 0-13.7-6.2-13.7-13 0-11 9.9-16.3 24.9-16.2v-.6c0-2.3-1.2-5.5-7.7-5.5-4.3 0-8.9 1.5-11.7 3.2l-2.4-8.5c2.9-1.6 8.8-3.7 16.5-3.7 14.1 0 18.6 8.3 18.6 18.3v14.7c0 4.1.2 8 .6 10.3zm-1.5-20c-6.9-.1-12.3 1.6-12.3 6.7 0 3.4 2.3 5 5.2 5 3.3 0 6-2.2 6.8-4.9.2-.7.3-1.5.3-2.3z"/>
+ <path d="m305.4 72.7c0-6.2-.2-10.3-.3-14h11.4l.4 7.8h.3c2.2-6.2 7.4-8.8 11.4-8.8 1.2 0 1.8 0 2.8.2v12.4c-1-.2-2.1-.3-3.6-.3-4.9 0-8.1 2.6-9 6.7-.2.9-.3 1.9-.3 2.9v21.5h-13.2v-28.4z"/>
+ <path d="m367.6 99.5c-2 1-6.4 2.4-12 2.4-12.7 0-20.9-8.6-20.9-21.4 0-12.9 8.8-22.3 22.5-22.3 4.5 0 8.5 1.1 10.6 2.2l-1.7 5.9c-1.8-1-4.7-2-8.8-2-9.6 0-14.8 7.1-14.8 15.9 0 9.7 6.2 15.7 14.6 15.7 4.3 0 7.2-1.1 9.4-2.1z"/>
+ <path d="m373 39.6h7.6v61.5h-7.6z"/>
+ <path d="m428 79.8c0 15.5-10.7 22.3-20.9 22.3-11.4 0-20.1-8.3-20.1-21.6 0-14 9.2-22.3 20.8-22.3 12 0 20.2 8.7 20.2 21.6zm-33.3.4c0 9.2 5.3 16.1 12.7 16.1 7.3 0 12.7-6.8 12.7-16.3 0-7.1-3.6-16.1-12.6-16.1-8.9 0-12.8 8.3-12.8 16.3z"/>
+ <path d="m469.2 89.7c0 4.3.1 8.1.3 11.4h-6.8l-.4-6.8h-.2c-2 3.4-6.4 7.8-13.9 7.8-6.6 0-14.5-3.6-14.5-18.4v-24.6h7.6v23.2c0 8 2.4 13.3 9.4 13.3 5.1 0 8.7-3.6 10.1-6.9.4-1.1.7-2.5.7-3.9v-25.7h7.6v30.6z"/>
+ <path d="m514.6 39.6v50.7c0 3.7.1 8 .3 10.8h-6.8l-.3-7.3h-.2c-2.3 4.7-7.5 8.2-14.3 8.2-10.1 0-17.9-8.6-17.9-21.3-.1-14 8.6-22.5 18.8-22.5 6.4 0 10.7 3 12.7 6.4h.2v-25zm-7.6 36.6c0-1-.1-2.3-.3-3.2-1.1-4.9-5.3-8.8-11-8.8-7.9 0-12.6 6.9-12.6 16.2 0 8.5 4.2 15.5 12.4 15.5 5.1 0 9.8-3.4 11.2-9.1.3-1 .3-2.1.3-3.3z"/>
+</g>
+<path d="m111.3 56.9c-3.6-4.4-8.5-7.7-14-9.4 0-.1 0-.2 0-.3 0-16.1-13-29.2-29.1-29.2s-29.2 13.1-29.2 29.2v.4c-11.8 3.7-20.4 14.8-20.4 27.8 0 16.1 13.1 29.2 29.2 29.2 7.8 0 15.1-3 20.5-8.4 5.3 5.2 12.5 8.4 20.5 8.4 16.1 0 29.2-13.1 29.2-29.2-.1-6.7-2.4-13.3-6.7-18.5zm-22.6 40.3c-12 0-21.8-9.8-21.8-21.8 0-2-1.6-3.7-3.7-3.7-2 0-3.7 1.6-3.7 3.7 0 5.5 1.5 10.6 4.1 14.9-4.1 4.4-9.8 6.9-15.9 6.9-12 0-21.8-9.8-21.8-21.8s9.8-21.8 21.8-21.8c2.6 0 5.1.5 7.5 1.3h.1c.8.3 2 .8 2.4 1.1 1.5 1.3 3.9 1.1 5.2-.4s1.1-3.9-.4-5.2c-1.6-1.4-4.1-2.3-4.7-2.5-3.2-1.2-6.6-1.8-10-1.8-.5 0-.9 0-1.4 0 .5-11.6 10.1-20.9 21.8-20.9 12 0 21.8 9.8 21.8 21.8 0 7-3.4 13.7-9.2 17.8-1.7 1.2-2 3.5-.9 5.1.7 1 1.8 1.5 3 1.5.7 0 1.5-.2 2.1-.7 5.5-3.9 9.4-9.6 11.2-16 8.4 3.1 14.3 11.3 14.3 20.5.1 12.2-9.7 22-21.8 22z" fill="#f60"/>
+<path d="m133.4 40.8c1.3.8 3.9 1.7 6 1.7 2.6 0 3.7-1.1 3.7-2.6 0-1.6-1-2.4-3.8-3.4-4.5-1.6-6.5-4.1-6.4-6.8 0-4.1 3.4-7.3 8.8-7.3 2.6 0 4.8.7 6.2 1.4l-1.1 4.2c-1-.6-2.9-1.3-4.9-1.3-2.1 0-3.3 1-3.3 2.4 0 1.5 1.1 2.2 4.1 3.3 4.2 1.5 6.2 3.7 6.2 7.1 0 4.2-3.3 7.3-9.5 7.3-2.8 0-5.4-.7-7.1-1.6z" fill="#fff"/>
+<path d="m170.7 45.5c-1.2.6-3.7 1.2-6.6 1.2-7.3 0-12-4.6-12-11.9 0-7 4.8-12.4 13-12.4 2.2 0 4.3.5 5.7 1.1l-1.1 4.4c-1-.4-2.3-.9-4.4-.9-4.5 0-7.2 3.3-7.1 7.6 0 4.8 3.1 7.5 7.1 7.5 2.1 0 3.5-.4 4.6-.9z" fill="#fff"/>
+<path d="m188.2 46.2-.4-2.6h-.1c-1.4 1.8-3.9 3.1-6.9 3.1-4.7 0-7.3-3.4-7.3-6.9 0-5.9 5.2-8.8 13.8-8.8v-.4c0-1.5-.6-4.1-4.7-4.1-2.3 0-4.7.7-6.3 1.7l-1.1-3.8c1.7-1.1 4.7-2.1 8.4-2.1 7.5 0 9.6 4.7 9.6 9.8v8.4c0 2.1.1 4.2.3 5.6h-5.3zm-.8-11.4c-4.2-.1-8.1.8-8.1 4.4 0 2.3 1.5 3.3 3.3 3.3 2.3 0 4.1-1.5 4.6-3.2.1-.4.2-.9.2-1.3z" fill="#fff"/>
+<path d="m199.4 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z" fill="#fff"/>
+<path d="m226.7 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z" fill="#fff"/>
+<path d="m258.2 36.2c.1 4.2 3.4 6 7.2 6 2.7 0 4.7-.4 6.5-1.1l.9 4.1c-2 .8-4.8 1.4-8.1 1.4-7.6 0-12-4.6-12-11.8 0-6.5 3.9-12.5 11.4-12.5 7.6 0 10 6.2 10 11.3 0 1.1-.1 2-.2 2.5h-15.7zm10.2-4.1c0-2.2-.9-5.7-4.8-5.7-3.6 0-5.2 3.3-5.4 5.7z" fill="#fff"/>
+<path d="m300.2 12.2v27.4c0 2.4.1 5 .2 6.5h-5.3l-.2-3.7h-.1c-1.4 2.6-4.2 4.2-7.6 4.2-5.5 0-9.9-4.7-9.9-11.9 0-7.8 4.8-12.4 10.4-12.4 3.2 0 5.5 1.3 6.5 3.1h.1v-13.2zm-5.9 20.4c0-.5 0-1.1-.1-1.5-.5-2.3-2.4-4.2-5.1-4.2-3.8 0-5.9 3.3-5.9 7.7 0 4.3 2.1 7.4 5.8 7.4 2.4 0 4.5-1.6 5.1-4.2.1-.5.2-1.1.2-1.7z" fill="#fff"/>
+<path d="m338.5 34.3c0 8.6-6 12.4-12 12.4-6.6 0-11.7-4.5-11.7-12 0-7.6 5-12.3 12-12.3 7.1 0 11.7 4.8 11.7 11.9zm-17.5.3c0 4.5 2.2 7.9 5.8 7.9 3.3 0 5.7-3.3 5.7-8 0-3.6-1.6-7.8-5.6-7.8-4.3 0-5.9 4-5.9 7.9z" fill="#fff"/>
+<path d="m343.3 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z" fill="#fff"/>
+</svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 535 122" viewBox="0 0 535 122" width="128" height="30">
+<path d="m523.1 120h-510.7c-5.7 0-10.4-4.7-10.4-10.4v-97.2c0-5.7 4.7-10.4 10.4-10.4h510.7c5.7 0 10.4 4.7 10.4 10.4v97.2c0 5.7-4.7 10.4-10.4 10.4z" fill="#f60"/>
+<g fill="#fff">
+ <path d="m135 89.6c2.4 1.5 7.5 3.2 11.4 3.2 4 0 5.6-1.4 5.6-3.6s-1.3-3.2-6.2-4.9c-8.8-2.9-12.1-7.7-12-12.7 0-7.9 6.8-13.9 17.2-13.9 4.9 0 9.4 1.1 12 2.4l-2.3 9.1c-1.9-1-5.5-2.4-9.2-2.4-3.2 0-5 1.3-5 3.5 0 2 1.6 3 6.8 4.9 8.1 2.8 11.4 6.8 11.5 13.1 0 7.9-6.2 13.7-18.4 13.7-5.5 0-10.5-1.2-13.7-2.9z"/>
+ <path d="m211.1 79.4c0 15.5-11 22.6-22.4 22.6-12.4 0-21.9-8.1-21.9-21.8s9-22.4 22.6-22.4c13.1 0 21.7 8.9 21.7 21.6zm-30.6.5c0 7.3 3 12.7 8.7 12.7 5.1 0 8.4-5.1 8.4-12.7 0-6.3-2.4-12.7-8.4-12.7-6.4-.1-8.7 6.4-8.7 12.7z"/>
+ <path d="m215.8 72.2c0-5.3-.2-9.8-.3-13.5h11.4l.6 5.8h.3c1.7-2.7 6.1-6.8 13.1-6.8 8.7 0 15.2 5.7 15.2 18.2v25.1h-13.2v-23.4c0-5.5-1.9-9.2-6.7-9.2-3.6 0-5.8 2.5-6.7 4.9-.3.8-.5 2.1-.5 3.3v24.4h-13.2z"/>
+ <path d="m287.2 101.1-.8-4.2h-.3c-2.8 3.4-7.1 5.2-12.1 5.2-8.6 0-13.7-6.2-13.7-13 0-11 9.9-16.3 24.9-16.2v-.6c0-2.3-1.2-5.5-7.7-5.5-4.3 0-8.9 1.5-11.7 3.2l-2.4-8.5c2.9-1.6 8.8-3.7 16.5-3.7 14.1 0 18.6 8.3 18.6 18.3v14.7c0 4.1.2 8 .6 10.3zm-1.5-20c-6.9-.1-12.3 1.6-12.3 6.7 0 3.4 2.3 5 5.2 5 3.3 0 6-2.2 6.8-4.9.2-.7.3-1.5.3-2.3z"/>
+ <path d="m305.4 72.7c0-6.2-.2-10.3-.3-14h11.4l.4 7.8h.3c2.2-6.2 7.4-8.8 11.4-8.8 1.2 0 1.8 0 2.8.2v12.4c-1-.2-2.1-.3-3.6-.3-4.9 0-8.1 2.6-9 6.7-.2.9-.3 1.9-.3 2.9v21.5h-13.2v-28.4z"/>
+ <path d="m367.6 99.5c-2 1-6.4 2.4-12 2.4-12.7 0-20.9-8.6-20.9-21.4 0-12.9 8.8-22.3 22.5-22.3 4.5 0 8.5 1.1 10.6 2.2l-1.7 5.9c-1.8-1-4.7-2-8.8-2-9.6 0-14.8 7.1-14.8 15.9 0 9.7 6.2 15.7 14.6 15.7 4.3 0 7.2-1.1 9.4-2.1z"/>
+ <path d="m373 39.6h7.6v61.5h-7.6z"/>
+ <path d="m428 79.8c0 15.5-10.7 22.3-20.9 22.3-11.4 0-20.1-8.3-20.1-21.6 0-14 9.2-22.3 20.8-22.3 12 0 20.2 8.7 20.2 21.6zm-33.3.4c0 9.2 5.3 16.1 12.7 16.1 7.3 0 12.7-6.8 12.7-16.3 0-7.1-3.6-16.1-12.6-16.1-8.9 0-12.8 8.3-12.8 16.3z"/>
+ <path d="m469.2 89.7c0 4.3.1 8.1.3 11.4h-6.8l-.4-6.8h-.2c-2 3.4-6.4 7.8-13.9 7.8-6.6 0-14.5-3.6-14.5-18.4v-24.6h7.6v23.2c0 8 2.4 13.3 9.4 13.3 5.1 0 8.7-3.6 10.1-6.9.4-1.1.7-2.5.7-3.9v-25.7h7.6v30.6z"/>
+ <path d="m514.6 39.6v50.7c0 3.7.1 8 .3 10.8h-6.8l-.3-7.3h-.2c-2.3 4.7-7.5 8.2-14.3 8.2-10.1 0-17.9-8.6-17.9-21.3-.1-14 8.6-22.5 18.8-22.5 6.4 0 10.7 3 12.7 6.4h.2v-25zm-7.6 36.6c0-1-.1-2.3-.3-3.2-1.1-4.9-5.3-8.8-11-8.8-7.9 0-12.6 6.9-12.6 16.2 0 8.5 4.2 15.5 12.4 15.5 5.1 0 9.8-3.4 11.2-9.1.3-1 .3-2.1.3-3.3z"/>
+ <path d="m111.3 56.9c-3.6-4.4-8.5-7.7-14-9.4 0-.1 0-.2 0-.3 0-16.1-13-29.2-29.1-29.2s-29.2 13.1-29.2 29.2v.4c-11.8 3.7-20.4 14.8-20.4 27.8 0 16.1 13.1 29.2 29.2 29.2 7.8 0 15.1-3 20.5-8.4 5.3 5.2 12.5 8.4 20.5 8.4 16.1 0 29.2-13.1 29.2-29.2-.1-6.7-2.4-13.3-6.7-18.5zm-22.6 40.3c-12 0-21.8-9.8-21.8-21.8 0-2-1.6-3.7-3.7-3.7-2 0-3.7 1.6-3.7 3.7 0 5.5 1.5 10.6 4.1 14.9-4.1 4.4-9.8 6.9-15.9 6.9-12 0-21.8-9.8-21.8-21.8s9.8-21.8 21.8-21.8c2.6 0 5.1.5 7.5 1.3h.1c.8.3 2 .8 2.4 1.1 1.5 1.3 3.9 1.1 5.2-.4s1.1-3.9-.4-5.2c-1.6-1.4-4.1-2.3-4.7-2.5-3.2-1.2-6.6-1.8-10-1.8-.5 0-.9 0-1.4 0 .5-11.6 10.1-20.9 21.8-20.9 12 0 21.8 9.8 21.8 21.8 0 7-3.4 13.7-9.2 17.8-1.7 1.2-2 3.5-.9 5.1.7 1 1.8 1.5 3 1.5.7 0 1.5-.2 2.1-.7 5.5-3.9 9.4-9.6 11.2-16 8.4 3.1 14.3 11.3 14.3 20.5.1 12.2-9.7 22-21.8 22z"/>
+ <path d="m133.4 40.8c1.3.8 3.9 1.7 6 1.7 2.6 0 3.7-1.1 3.7-2.6 0-1.6-1-2.4-3.8-3.4-4.5-1.6-6.5-4.1-6.4-6.8 0-4.1 3.4-7.3 8.8-7.3 2.6 0 4.8.7 6.2 1.4l-1.1 4.2c-1-.6-2.9-1.3-4.9-1.3-2.1 0-3.3 1-3.3 2.4 0 1.5 1.1 2.2 4.1 3.3 4.2 1.5 6.2 3.7 6.2 7.1 0 4.2-3.3 7.3-9.5 7.3-2.8 0-5.4-.7-7.1-1.6z"/>
+ <path d="m170.7 45.5c-1.2.6-3.7 1.2-6.6 1.2-7.3 0-12-4.6-12-11.9 0-7 4.8-12.4 13-12.4 2.2 0 4.3.5 5.7 1.1l-1.1 4.4c-1-.4-2.3-.9-4.4-.9-4.5 0-7.2 3.3-7.1 7.6 0 4.8 3.1 7.5 7.1 7.5 2.1 0 3.5-.4 4.6-.9z"/>
+ <path d="m188.2 46.2-.4-2.6h-.1c-1.4 1.8-3.9 3.1-6.9 3.1-4.7 0-7.3-3.4-7.3-6.9 0-5.9 5.2-8.8 13.8-8.8v-.4c0-1.5-.6-4.1-4.7-4.1-2.3 0-4.7.7-6.3 1.7l-1.1-3.8c1.7-1.1 4.7-2.1 8.4-2.1 7.5 0 9.6 4.7 9.6 9.8v8.4c0 2.1.1 4.2.3 5.6h-5.3zm-.8-11.4c-4.2-.1-8.1.8-8.1 4.4 0 2.3 1.5 3.3 3.3 3.3 2.3 0 4.1-1.5 4.6-3.2.1-.4.2-.9.2-1.3z"/>
+ <path d="m199.4 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z"/>
+ <path d="m226.7 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z"/>
+ <path d="m258.2 36.2c.1 4.2 3.4 6 7.2 6 2.7 0 4.7-.4 6.5-1.1l.9 4.1c-2 .8-4.8 1.4-8.1 1.4-7.6 0-12-4.6-12-11.8 0-6.5 3.9-12.5 11.4-12.5 7.6 0 10 6.2 10 11.3 0 1.1-.1 2-.2 2.5h-15.7zm10.2-4.1c0-2.2-.9-5.7-4.8-5.7-3.6 0-5.2 3.3-5.4 5.7z"/>
+ <path d="m300.2 12.2v27.4c0 2.4.1 5 .2 6.5h-5.3l-.2-3.7h-.1c-1.4 2.6-4.2 4.2-7.6 4.2-5.5 0-9.9-4.7-9.9-11.9 0-7.8 4.8-12.4 10.4-12.4 3.2 0 5.5 1.3 6.5 3.1h.1v-13.2zm-5.9 20.4c0-.5 0-1.1-.1-1.5-.5-2.3-2.4-4.2-5.1-4.2-3.8 0-5.9 3.3-5.9 7.7 0 4.3 2.1 7.4 5.8 7.4 2.4 0 4.5-1.6 5.1-4.2.1-.5.2-1.1.2-1.7z"/>
+ <path d="m338.5 34.3c0 8.6-6 12.4-12 12.4-6.6 0-11.7-4.5-11.7-12 0-7.6 5-12.3 12-12.3 7.1 0 11.7 4.8 11.7 11.9zm-17.5.3c0 4.5 2.2 7.9 5.8 7.9 3.3 0 5.7-3.3 5.7-8 0-3.6-1.6-7.8-5.6-7.8-4.3 0-5.9 4-5.9 7.9z"/>
+ <path d="m343.3 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z"/>
+</g>
+</svg>
\ No newline at end of file
--- /dev/null
+<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 535 122" viewBox="0 0 535 122" width="128" height="30">
+<path d="m522.2 119.5h-508.8c-5.7 0-10.3-4.7-10.3-10.3v-96.4c-.1-5.6 4.6-10.3 10.3-10.3h508.8c5.7 0 10.3 4.7 10.3 10.3v96.3c0 5.7-4.7 10.4-10.3 10.4z" fill="#fff"/>
+<path d="m522.2 121h-508.8c-6.5 0-11.8-5.3-11.8-11.8v-96.4c-.1-6.5 5.2-11.8 11.8-11.8h508.8c6.5 0 11.8 5.3 11.8 11.8v96.3c0 6.6-5.3 11.9-11.8 11.9zm-508.8-117c-4.9 0-8.9 4-8.9 8.9v96.3c0 4.9 4 8.9 8.9 8.9h508.8c4.9 0 8.9-4 8.9-8.9v-96.4c0-4.9-4-8.9-8.9-8.9h-508.8z" fill="#cfd3d7"/>
+<path d="m135 89.6c2.4 1.5 7.5 3.2 11.4 3.2 4 0 5.6-1.4 5.6-3.6s-1.3-3.2-6.2-4.9c-8.8-2.9-12.1-7.7-12-12.7 0-7.9 6.8-13.9 17.2-13.9 4.9 0 9.4 1.1 12 2.4l-2.3 9.1c-1.9-1-5.5-2.4-9.2-2.4-3.2 0-5 1.3-5 3.5 0 2 1.6 3 6.8 4.9 8.1 2.8 11.4 6.8 11.5 13.1 0 7.9-6.2 13.7-18.4 13.7-5.5 0-10.5-1.2-13.7-2.9z"/>
+<path d="m211.1 79.4c0 15.5-11 22.6-22.4 22.6-12.4 0-21.9-8.1-21.9-21.8s9-22.4 22.6-22.4c13.1 0 21.7 8.9 21.7 21.6zm-30.6.5c0 7.3 3 12.7 8.7 12.7 5.1 0 8.4-5.1 8.4-12.7 0-6.3-2.4-12.7-8.4-12.7-6.4-.1-8.7 6.4-8.7 12.7z"/>
+<path d="m215.8 72.2c0-5.3-.2-9.8-.3-13.5h11.4l.6 5.8h.3c1.7-2.7 6.1-6.8 13.1-6.8 8.7 0 15.2 5.7 15.2 18.2v25.1h-13.2v-23.4c0-5.5-1.9-9.2-6.7-9.2-3.6 0-5.8 2.5-6.7 4.9-.3.8-.5 2.1-.5 3.3v24.4h-13.2z"/>
+<path d="m287.2 101.1-.8-4.2h-.3c-2.8 3.4-7.1 5.2-12.1 5.2-8.6 0-13.7-6.2-13.7-13 0-11 9.9-16.3 24.9-16.2v-.6c0-2.3-1.2-5.5-7.7-5.5-4.3 0-8.9 1.5-11.7 3.2l-2.4-8.5c2.9-1.6 8.8-3.7 16.5-3.7 14.1 0 18.6 8.3 18.6 18.3v14.7c0 4.1.2 8 .6 10.3zm-1.5-20c-6.9-.1-12.3 1.6-12.3 6.7 0 3.4 2.3 5 5.2 5 3.3 0 6-2.2 6.8-4.9.2-.7.3-1.5.3-2.3z"/>
+<path d="m305.4 72.7c0-6.2-.2-10.3-.3-14h11.4l.4 7.8h.3c2.2-6.2 7.4-8.8 11.4-8.8 1.2 0 1.8 0 2.8.2v12.4c-1-.2-2.1-.3-3.6-.3-4.9 0-8.1 2.6-9 6.7-.2.9-.3 1.9-.3 2.9v21.5h-13.2v-28.4z"/>
+<path d="m367.6 99.5c-2 1-6.4 2.4-12 2.4-12.7 0-20.9-8.6-20.9-21.4 0-12.9 8.8-22.3 22.5-22.3 4.5 0 8.5 1.1 10.6 2.2l-1.7 5.9c-1.8-1-4.7-2-8.8-2-9.6 0-14.8 7.1-14.8 15.9 0 9.7 6.2 15.7 14.6 15.7 4.3 0 7.2-1.1 9.4-2.1z"/>
+<path d="m373 39.6h7.6v61.5h-7.6z"/>
+<path d="m428 79.8c0 15.5-10.7 22.3-20.9 22.3-11.4 0-20.1-8.3-20.1-21.6 0-14 9.2-22.3 20.8-22.3 12 0 20.2 8.7 20.2 21.6zm-33.3.4c0 9.2 5.3 16.1 12.7 16.1 7.3 0 12.7-6.8 12.7-16.3 0-7.1-3.6-16.1-12.6-16.1-8.9 0-12.8 8.3-12.8 16.3z"/>
+<path d="m469.2 89.7c0 4.3.1 8.1.3 11.4h-6.8l-.4-6.8h-.2c-2 3.4-6.4 7.8-13.9 7.8-6.6 0-14.5-3.6-14.5-18.4v-24.6h7.6v23.2c0 8 2.4 13.3 9.4 13.3 5.1 0 8.7-3.6 10.1-6.9.4-1.1.7-2.5.7-3.9v-25.7h7.6v30.6z"/>
+<path d="m514.6 39.6v50.7c0 3.7.1 8 .3 10.8h-6.8l-.3-7.3h-.2c-2.3 4.7-7.5 8.2-14.3 8.2-10.1 0-17.9-8.6-17.9-21.3-.1-14 8.6-22.5 18.8-22.5 6.4 0 10.7 3 12.7 6.4h.2v-25zm-7.6 36.6c0-1-.1-2.3-.3-3.2-1.1-4.9-5.3-8.8-11-8.8-7.9 0-12.6 6.9-12.6 16.2 0 8.5 4.2 15.5 12.4 15.5 5.1 0 9.8-3.4 11.2-9.1.3-1 .3-2.1.3-3.3z"/>
+<path d="m111.3 56.9c-3.6-4.4-8.5-7.7-14-9.4 0-.1 0-.2 0-.3 0-16.1-13-29.2-29.1-29.2s-29.2 13.1-29.2 29.2v.4c-11.8 3.7-20.4 14.8-20.4 27.8 0 16.1 13.1 29.2 29.2 29.2 7.8 0 15.1-3 20.5-8.4 5.3 5.2 12.5 8.4 20.5 8.4 16.1 0 29.2-13.1 29.2-29.2-.1-6.7-2.4-13.3-6.7-18.5zm-22.6 40.3c-12 0-21.8-9.8-21.8-21.8 0-2-1.6-3.7-3.7-3.7-2 0-3.7 1.6-3.7 3.7 0 5.5 1.5 10.6 4.1 14.9-4.1 4.4-9.8 6.9-15.9 6.9-12 0-21.8-9.8-21.8-21.8s9.8-21.8 21.8-21.8c2.6 0 5.1.5 7.5 1.3h.1c.8.3 2 .8 2.4 1.1 1.5 1.3 3.9 1.1 5.2-.4s1.1-3.9-.4-5.2c-1.6-1.4-4.1-2.3-4.7-2.5-3.2-1.2-6.6-1.8-10-1.8-.5 0-.9 0-1.4 0 .5-11.6 10.1-20.9 21.8-20.9 12 0 21.8 9.8 21.8 21.8 0 7-3.4 13.7-9.2 17.8-1.7 1.2-2 3.5-.9 5.1.7 1 1.8 1.5 3 1.5.7 0 1.5-.2 2.1-.7 5.5-3.9 9.4-9.6 11.2-16 8.4 3.1 14.3 11.3 14.3 20.5.1 12.2-9.7 22-21.8 22z" fill="#f60"/>
+<path d="m133.4 40.8c1.3.8 3.9 1.7 6 1.7 2.6 0 3.7-1.1 3.7-2.6 0-1.6-1-2.4-3.8-3.4-4.5-1.6-6.5-4.1-6.4-6.8 0-4.1 3.4-7.3 8.8-7.3 2.6 0 4.8.7 6.2 1.4l-1.1 4.2c-1-.6-2.9-1.3-4.9-1.3-2.1 0-3.3 1-3.3 2.4 0 1.5 1.1 2.2 4.1 3.3 4.2 1.5 6.2 3.7 6.2 7.1 0 4.2-3.3 7.3-9.5 7.3-2.8 0-5.4-.7-7.1-1.6z"/>
+<path d="m170.7 45.5c-1.2.6-3.7 1.2-6.6 1.2-7.3 0-12-4.6-12-11.9 0-7 4.8-12.4 13-12.4 2.2 0 4.3.5 5.7 1.1l-1.1 4.4c-1-.4-2.3-.9-4.4-.9-4.5 0-7.2 3.3-7.1 7.6 0 4.8 3.1 7.5 7.1 7.5 2.1 0 3.5-.4 4.6-.9z"/>
+<path d="m188.2 46.2-.4-2.6h-.1c-1.4 1.8-3.9 3.1-6.9 3.1-4.7 0-7.3-3.4-7.3-6.9 0-5.9 5.2-8.8 13.8-8.8v-.4c0-1.5-.6-4.1-4.7-4.1-2.3 0-4.7.7-6.3 1.7l-1.1-3.8c1.7-1.1 4.7-2.1 8.4-2.1 7.5 0 9.6 4.7 9.6 9.8v8.4c0 2.1.1 4.2.3 5.6h-5.3zm-.8-11.4c-4.2-.1-8.1.8-8.1 4.4 0 2.3 1.5 3.3 3.3 3.3 2.3 0 4.1-1.5 4.6-3.2.1-.4.2-.9.2-1.3z"/>
+<path d="m199.4 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z"/>
+<path d="m226.7 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z"/>
+<path d="m258.2 36.2c.1 4.2 3.4 6 7.2 6 2.7 0 4.7-.4 6.5-1.1l.9 4.1c-2 .8-4.8 1.4-8.1 1.4-7.6 0-12-4.6-12-11.8 0-6.5 3.9-12.5 11.4-12.5 7.6 0 10 6.2 10 11.3 0 1.1-.1 2-.2 2.5h-15.7zm10.2-4.1c0-2.2-.9-5.7-4.8-5.7-3.6 0-5.2 3.3-5.4 5.7z"/>
+<path d="m300.2 12.2v27.4c0 2.4.1 5 .2 6.5h-5.3l-.2-3.7h-.1c-1.4 2.6-4.2 4.2-7.6 4.2-5.5 0-9.9-4.7-9.9-11.9 0-7.8 4.8-12.4 10.4-12.4 3.2 0 5.5 1.3 6.5 3.1h.1v-13.2zm-5.9 20.4c0-.5 0-1.1-.1-1.5-.5-2.3-2.4-4.2-5.1-4.2-3.8 0-5.9 3.3-5.9 7.7 0 4.3 2.1 7.4 5.8 7.4 2.4 0 4.5-1.6 5.1-4.2.1-.5.2-1.1.2-1.7z"/>
+<path d="m338.5 34.3c0 8.6-6 12.4-12 12.4-6.6 0-11.7-4.5-11.7-12 0-7.6 5-12.3 12-12.3 7.1 0 11.7 4.8 11.7 11.9zm-17.5.3c0 4.5 2.2 7.9 5.8 7.9 3.3 0 5.7-3.3 5.7-8 0-3.6-1.6-7.8-5.6-7.8-4.3 0-5.9 4-5.9 7.9z"/>
+<path d="m343.3 29.8c0-2.7 0-4.9-.2-6.9h5.2l.3 3.5h.1c1-1.8 3.5-4 7.4-4 4.1 0 8.3 2.6 8.3 10v13.8h-5.9v-13.2c0-3.3-1.2-5.9-4.4-5.9-2.3 0-4 1.7-4.6 3.4-.2.5-.2 1.2-.2 1.9v13.7h-5.9v-16.3z"/>
+</svg>
overview.deprecated_profile=This quality profile uses {0} deprecated rules and should be updated.
+overview.badges.get_badge=Get project badges
+overview.badges.title=Badges
+overview.badges.description=Show the status of your project metrics on your README or website. Pick your style:
+overview.badges.metric=Metric
+overview.badges.options.colors.white=White
+overview.badges.options.colors.black=Black
+overview.badges.options.colors.orange=Orange
+overview.badges.measure.alt=Standard badge
+overview.badges.measure.description=This badge dynamically displays the current status of one metric of your project.
+overview.badges.marketing.alt=Scanned on SonarCloud badge
+overview.badges.marketing.description=This badge lets you advertise that you're using SonarCloud for code quality.
+overview.badges.quality_gate.alt=SonarCloud Quality Gate badge
+overview.badges.quality_gate.description=This badge dynamically displays the current quality gate status of your project.
+
widget.as_calculated_on_x=As calculated on {0}