aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/app
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2018-03-12 10:48:44 +0100
committerSonarTech <sonartech@sonarsource.com>2018-03-26 20:20:57 +0200
commitea341da5bb721a4d833c3dd324162bbb0bc04642 (patch)
tree856834be64b032fdcff68abbbc939a7b1c2f28ba /server/sonar-web/src/main/js/app
parent9ac78350022bfadca4845a42cb600dfa762fb66b (diff)
downloadsonarqube-ea341da5bb721a4d833c3dd324162bbb0bc04642.tar.gz
sonarqube-ea341da5bb721a4d833c3dd324162bbb0bc04642.zip
VSTS-141 Move vsts integration to it's own module
Diffstat (limited to 'server/sonar-web/src/main/js/app')
-rw-r--r--server/sonar-web/src/main/js/app/integration/vsts/components/Configuration.tsx194
-rw-r--r--server/sonar-web/src/main/js/app/integration/vsts/components/LoginForm.tsx100
-rw-r--r--server/sonar-web/src/main/js/app/integration/vsts/components/LoginLink.tsx60
-rw-r--r--server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelector.tsx264
-rw-r--r--server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelectorItem.tsx55
-rw-r--r--server/sonar-web/src/main/js/app/integration/vsts/components/QGWidget.tsx55
-rw-r--r--server/sonar-web/src/main/js/app/integration/vsts/components/SonarCloudIcon.tsx39
-rw-r--r--server/sonar-web/src/main/js/app/integration/vsts/components/Widget.tsx126
-rw-r--r--server/sonar-web/src/main/js/app/integration/vsts/index.js71
-rw-r--r--server/sonar-web/src/main/js/app/integration/vsts/utils.ts47
-rw-r--r--server/sonar-web/src/main/js/app/integration/vsts/vsts.css294
11 files changed, 0 insertions, 1305 deletions
diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/Configuration.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/Configuration.tsx
deleted file mode 100644
index 6afeb86b3a4..00000000000
--- a/server/sonar-web/src/main/js/app/integration/vsts/components/Configuration.tsx
+++ /dev/null
@@ -1,194 +0,0 @@
-/*
- * 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 LoginForm from './LoginForm';
-import ProjectSelector from './ProjectSelector';
-import { Component, searchProjects } from '../../../../api/components';
-import {
- Settings,
- VSTSWidgetSettings,
- VSTSConfigurationContext,
- serializeWidgetSettings,
- parseWidgetSettings
-} from '../utils';
-import { getCurrentUser } from '../../../../api/users';
-import { CurrentUser } from '../../../types';
-
-interface Props {
- contribution: string;
- widgetHelpers: any;
-}
-
-interface State {
- currentUser?: CurrentUser;
- loading: boolean;
- projects: Component[];
- settings: Settings;
- selectedProject?: Component;
- widgetConfigurationContext?: VSTSConfigurationContext;
-}
-
-declare const VSS: {
- register: (contributionId: string, callback: Function) => void;
- resize: Function;
-};
-
-const PAGE_SIZE = 10;
-
-export default class Configuration extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { loading: true, projects: [], settings: { project: '' } };
-
- componentDidMount() {
- this.mounted = true;
- VSS.register(this.props.contribution, () => {
- return { load: this.load, onSave: this.onSave };
- });
- }
-
- componentDidUpdate() {
- VSS.resize();
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- load = (
- widgetSettings: VSTSWidgetSettings,
- widgetConfigurationContext: VSTSConfigurationContext
- ) => {
- const settings = parseWidgetSettings(widgetSettings);
- if (this.mounted) {
- this.setState({ settings: settings || {}, widgetConfigurationContext });
- this.fetchInitialData();
- }
- return this.props.widgetHelpers.WidgetStatusHelper.Success();
- };
-
- onSave = () => {
- const { settings } = this.state;
- if (!settings.project) {
- return this.props.widgetHelpers.WidgetConfigurationSave.Invalid();
- }
- return this.props.widgetHelpers.WidgetConfigurationSave.Valid(
- serializeWidgetSettings(settings)
- );
- };
-
- fetchInitialData = () => {
- this.setState({ loading: true });
- getCurrentUser()
- .then(currentUser => {
- this.setState({ currentUser });
- const params: { ps: number; filter?: string } = { ps: PAGE_SIZE };
- if (currentUser.isLoggedIn) {
- params.filter = 'isFavorite';
- }
- return searchProjects(params);
- })
- .then(this.handleSearchProjectsResult, this.stopLoading);
- };
-
- handleReload = () => {
- this.fetchInitialData();
- };
-
- handleProjectChange = (
- event: React.ChangeEvent<HTMLSelectElement> | React.FocusEvent<HTMLSelectElement>
- ) => {
- const { value } = event.currentTarget;
- this.setState(
- ({ settings }) => ({ settings: { ...settings, project: value } }),
- this.notifyChange
- );
- };
-
- notifyChange = ({ settings, widgetConfigurationContext } = this.state) => {
- const { widgetHelpers } = this.props;
- if (widgetConfigurationContext && widgetConfigurationContext.notify) {
- const eventName = widgetHelpers.WidgetEvent.ConfigurationChange;
- const eventArgs = widgetHelpers.WidgetEvent.Args(serializeWidgetSettings(settings));
- widgetConfigurationContext.notify(eventName, eventArgs);
- }
- };
-
- handleProjectSearch = (query: string) => {
- const searchParams: { ps: number; filter?: string } = { ps: PAGE_SIZE };
- if (query) {
- searchParams.filter = query;
- }
- return searchProjects(searchParams).then(this.handleSearchProjectsResult, this.stopLoading);
- };
-
- handleProjectSelect = (project: Component) => {
- this.setState(
- ({ settings }) => ({
- selectedProject: project,
- settings: { ...settings, project: project.key }
- }),
- this.notifyChange
- );
- };
-
- handleSearchProjectsResult = ({ components }: { components: Component[] }) => {
- if (this.mounted) {
- this.setState({ loading: false, projects: components });
- }
- };
-
- stopLoading = () => {
- this.setState({ loading: false });
- };
-
- render() {
- const { currentUser, projects, loading, selectedProject, settings } = this.state;
- if (loading) {
- return (
- <div className="vsts-loading">
- <i className="spinner global-loading-spinner" />
- </div>
- );
- }
-
- const isLoggedIn = Boolean(currentUser && currentUser.isLoggedIn);
- const selected = selectedProject || projects.find(project => project.key === settings.project);
- return (
- <div className="widget-configuration vsts-configuration bowtie">
- <div className="dropdown config-settings-field" id="sonarcloud-project">
- <label>SonarCloud project</label>
- <ProjectSelector
- isLoggedIn={isLoggedIn}
- onQueryChange={this.handleProjectSearch}
- onSelect={this.handleProjectSelect}
- projects={projects}
- selected={selected}
- />
- </div>
- {!isLoggedIn && (
- <div className="config-settings-field">
- <label>You must be logged in to see your private projects :</label>
- <LoginForm onReload={this.handleReload} />
- </div>
- )}
- </div>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/LoginForm.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/LoginForm.tsx
deleted file mode 100644
index 10367598a92..00000000000
--- a/server/sonar-web/src/main/js/app/integration/vsts/components/LoginForm.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * 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 LoginLink from './LoginLink';
-import SonarCloudIcon from './SonarCloudIcon';
-import * as theme from '../../../../app/theme';
-import { IdentityProvider } from '../../../types';
-import { getIdentityProviders } from '../../../../api/users';
-import { getTextColor } from '../../../../helpers/colors';
-import { getBaseUrl } from '../../../../helpers/urls';
-
-interface Props {
- onReload: () => void;
- title?: string;
-}
-
-interface State {
- identityProviders?: IdentityProvider[];
-}
-
-export default class LoginForm extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = {};
-
- componentDidMount() {
- this.mounted = true;
- getIdentityProviders().then(
- identityProvidersResponse => {
- if (this.mounted) {
- this.setState({
- identityProviders: identityProvidersResponse.identityProviders
- });
- }
- },
- () => {}
- );
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- render() {
- const { onReload, title } = this.props;
- const { identityProviders } = this.state;
- const vstsProvider =
- identityProviders && identityProviders.find(provider => provider.key === 'microsoft');
-
- return (
- <div className="vsts-widget-login">
- {title && <SonarCloudIcon size={32} />}
- {title && <p className="login-message-text">{title}</p>}
- {identityProviders && (
- <section className="oauth-providers">
- {vstsProvider && (
- <LoginLink
- onReload={onReload}
- sessionUrl={`sessions/init/${vstsProvider.key}`}
- style={{
- backgroundColor: vstsProvider.backgroundColor,
- color: getTextColor(vstsProvider.backgroundColor, theme.secondFontColor)
- }}>
- <img
- alt={vstsProvider.name}
- height="20"
- src={getBaseUrl() + vstsProvider.iconPath}
- width="20"
- />
- <span>{vstsProvider.name} log in</span>
- </LoginLink>
- )}
- </section>
- )}
-
- <div className="text-center">
- <LoginLink onReload={onReload} sessionUrl={'sessions/new'}>
- {vstsProvider ? 'More options' : 'Log in on SonarCloud'}
- </LoginLink>
- </div>
- </div>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/LoginLink.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/LoginLink.tsx
deleted file mode 100644
index a4dc36adc92..00000000000
--- a/server/sonar-web/src/main/js/app/integration/vsts/components/LoginLink.tsx
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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 { getBaseUrl } from '../../../../helpers/urls';
-
-interface Props {
- className?: string;
- children: React.ReactNode;
- onReload: () => void;
- style?: React.CSSProperties;
- sessionUrl: string;
-}
-
-export default class LoginLink extends React.PureComponent<Props> {
- handleLoginClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
- event.preventDefault();
- event.stopPropagation();
- event.currentTarget.blur();
-
- (window as any).authenticationDone = () => {
- this.props.onReload();
- };
-
- const returnTo = encodeURIComponent(window.location.pathname + '?type=authenticated');
- window.open(
- `${getBaseUrl()}/${this.props.sessionUrl}?return_to=${returnTo}`,
- 'Login on SonarCloud',
- 'toolbar=0,status=0,width=377,height=380'
- );
- };
-
- render() {
- return (
- <a
- className={this.props.className}
- href="#"
- onClick={this.handleLoginClick}
- style={this.props.style}>
- {this.props.children}
- </a>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelector.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelector.tsx
deleted file mode 100644
index d28fa4dc504..00000000000
--- a/server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelector.tsx
+++ /dev/null
@@ -1,264 +0,0 @@
-/*
- * 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 { debounce } from 'lodash';
-import ProjectSelectorItem from './ProjectSelectorItem';
-import { Component } from '../../../../api/components';
-
-interface Props {
- isLoggedIn: boolean;
- onQueryChange: (query: string) => Promise<void>;
- onSelect: (component: Component) => void;
- projects: Component[];
- selected?: Component;
-}
-
-interface State {
- activeIdx: number;
- activeKey?: string;
- favorite: boolean;
- open: boolean;
- search: string;
- searching: boolean;
-}
-
-export default class ProjectSelector extends React.PureComponent<Props, State> {
- node?: HTMLElement | null;
- debouncedHandleSearch: () => void;
-
- constructor(props: Props) {
- super(props);
- const firstProject = props.projects[0];
- this.state = {
- activeIdx: firstProject ? 0 : -1,
- activeKey: firstProject && firstProject.key,
- favorite: props.isLoggedIn,
- open: false,
- search: '',
- searching: false
- };
- this.debouncedHandleSearch = debounce(this.handleSearch, 250);
- }
-
- componentDidMount() {
- window.addEventListener('click', this.handleClickOutside);
- if (this.node) {
- this.node.addEventListener('keydown', this.handleKeyDown, true);
- }
- }
-
- componentWillReceiveProps(nextProps: Props) {
- if (this.props.projects !== nextProps.projects) {
- let activeIdx = nextProps.projects.findIndex(project => project.key === this.state.activeKey);
- activeIdx = activeIdx >= 0 ? activeIdx : 0;
- this.setState({ activeIdx, activeKey: this.getActiveKey(activeIdx) });
- }
- }
-
- componentWillUnmount() {
- window.removeEventListener('click', this.handleClickOutside);
- if (this.node) {
- this.node.removeEventListener('keydown', this.handleKeyDown);
- }
- }
-
- getActiveKey = (idx: number) => {
- const { projects } = this.props;
- return projects[idx] && projects[idx].key;
- };
-
- getEmptyMessage = () => {
- const { favorite, search } = this.state;
- if (search) {
- return 'No project matching your search.';
- } else if (favorite) {
- return "You don't have any favorite projects yet.";
- }
- return 'No project have been found';
- };
-
- handleClickOutside = (event: Event) => {
- if (!this.node || !this.node.contains(event.target as HTMLElement)) {
- this.setState({ open: false });
- }
- };
-
- handleFilterAll = () => {
- this.setState({ favorite: false, searching: true }, this.handleSearch);
- };
-
- handleFilterFavorite = () => {
- this.setState({ favorite: true, searching: true }, this.handleSearch);
- };
-
- handleItemHover = (item: Component) => {
- let activeIdx = this.props.projects.findIndex(project => project.key === item.key);
- activeIdx = activeIdx >= 0 ? activeIdx : 0;
- this.setState({ activeIdx, activeKey: this.getActiveKey(activeIdx) });
- };
-
- handleKeyDown = (evt: KeyboardEvent) => {
- switch (evt.keyCode) {
- case 40: // down
- evt.stopPropagation();
- evt.preventDefault();
- this.setState(this.selectNextItem);
- break;
- case 38: // up
- evt.stopPropagation();
- evt.preventDefault();
- this.setState(this.selectPreviousItem);
- break;
- case 37: // left
- case 39: // right
- evt.stopPropagation();
- break;
- case 13: // enter
- if (this.state.activeIdx >= 0) {
- this.handleSelect(this.props.projects[this.state.activeIdx]);
- }
- break;
- case 27: // escape
- this.setState({ open: false });
- break;
- }
- };
-
- handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
- this.setState(
- { search: event.currentTarget.value, searching: true },
- this.debouncedHandleSearch
- );
- };
-
- handleSearch = () => {
- const filter = [];
- if (this.state.favorite) {
- filter.push('isFavorite');
- }
- if (this.state.search) {
- filter.push(`query = "${this.state.search}"`);
- }
- this.props.onQueryChange(filter.join(' and ')).then(this.stopSearching, this.stopSearching);
- };
-
- handleSelect = (project: Component) => {
- this.props.onSelect(project);
- this.setState({ open: false });
- };
-
- selectNextItem = ({ activeIdx }: State, { projects }: Props) => {
- let newActiveIdx = activeIdx + 1;
- if (activeIdx < 0 || activeIdx >= projects.length - 1) {
- newActiveIdx = 0;
- }
- return { activeIdx: newActiveIdx, activeKey: this.getActiveKey(newActiveIdx) };
- };
-
- selectPreviousItem = ({ activeIdx }: State, { projects }: Props) => {
- let newActiveIdx = activeIdx - 1;
- if (activeIdx <= 0) {
- newActiveIdx = projects.length - 1;
- }
- return { activeIdx: newActiveIdx, activeKey: this.getActiveKey(newActiveIdx) };
- };
-
- stopSearching = () => {
- this.setState({ searching: false });
- };
-
- toggleOpen = () => {
- this.setState(({ open }) => ({ open: !open }));
- };
-
- render() {
- const { isLoggedIn, projects, selected } = this.props;
- const { activeIdx, favorite, open, search, searching } = this.state;
- return (
- <div className="project-picker" ref={node => (this.node = node)}>
- <div
- className="filtered-list-dropdown-menu"
- onClick={this.toggleOpen}
- role="button"
- tabIndex={0}>
- <span className="selected-item-text">
- {selected ? selected.name : 'Select a project...'}
- </span>
- <span className="drop-icon bowtie-icon bowtie-chevron-down-light" />
- </div>
- {open && (
- <div className="filtered-list-popup" role="dialog">
- <div className="filtered-list-control bowtie-filtered-list">
- <div className="filter-container">
- {isLoggedIn && (
- <div className="views">
- <ul className="pivot-view" role="tablist">
- <li
- className={classNames('filtered-list-tab', { selected: favorite })}
- role="presentation">
- <a onClick={this.handleFilterFavorite} role="tab" tabIndex={0}>
- My Projects
- </a>
- </li>
- <li
- className={classNames('filtered-list-tab', { selected: !favorite })}
- role="presentation">
- <a onClick={this.handleFilterAll} role="tab" tabIndex={-1}>
- All
- </a>
- </li>
- </ul>
- </div>
- )}
- <div className="filtered-list-search-container bowtie-style">
- <input
- autoFocus={true}
- className="filtered-list-search"
- onChange={this.handleSearchChange}
- placeholder="Search by project name"
- type="text"
- value={search}
- />
- {searching && <i className="spinner" />}
- </div>
- </div>
- <ul className="filtered-list">
- {projects.map((project, idx) => (
- <ProjectSelectorItem
- isActive={activeIdx === idx}
- isSelected={Boolean(selected && selected.key === project.key)}
- key={project.key}
- onHover={this.handleItemHover}
- onSelect={this.handleSelect}
- project={project}
- />
- ))}
- {projects.length <= 0 && (
- <li className="filtered-list-message">{this.getEmptyMessage()}</li>
- )}
- </ul>
- </div>
- </div>
- )}
- </div>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelectorItem.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelectorItem.tsx
deleted file mode 100644
index 6dd56d58056..00000000000
--- a/server/sonar-web/src/main/js/app/integration/vsts/components/ProjectSelectorItem.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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 { Component } from '../../../../api/components';
-
-interface Props {
- isActive: boolean;
- isSelected: boolean;
- onHover: (project: Component) => void;
- onSelect: (project: Component) => void;
- project: Component;
-}
-
-export default class ProjectSelectorItem extends React.PureComponent<Props> {
- handleClick = () => {
- this.props.onSelect(this.props.project);
- };
-
- handleHover = () => {
- this.props.onHover(this.props.project);
- };
-
- render() {
- return (
- <li
- className={classNames('filtered-list-item', {
- 'current-item': this.props.isSelected,
- 'active-item': this.props.isActive
- })}
- onClick={this.handleClick}
- onFocus={this.handleHover}
- onMouseOver={this.handleHover}>
- {this.props.project.name}
- </li>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/QGWidget.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/QGWidget.tsx
deleted file mode 100644
index 51a08eec4ce..00000000000
--- a/server/sonar-web/src/main/js/app/integration/vsts/components/QGWidget.tsx
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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 SonarCloudIcon from './SonarCloudIcon';
-import { MeasureComponent } from '../../../../api/measures';
-import { getPathUrlAsString, getProjectUrl } from '../../../../helpers/urls';
-
-interface Props {
- component: MeasureComponent;
-}
-
-const QG_LEVELS: { [level: string]: string } = {
- ERROR: 'Failed',
- WARN: 'Warning',
- OK: 'Passed',
- NONE: 'None'
-};
-
-export default function QGWidget({ component }: Props) {
- const qgMeasure = component && component.measures.find(m => m.metric === 'alert_status');
-
- if (!qgMeasure || !qgMeasure.value) {
- return <p>Project Quality Gate not computed.</p>;
- }
-
- return (
- <div className={classNames('widget dark-widget clickable', 'level-' + qgMeasure.value)}>
- <a href={getPathUrlAsString(getProjectUrl(component.key))} target="_blank">
- <h2 className="title truncated-text-ellipsis">{component.name}</h2>
- <div className="big-value truncated-text-ellipsis">{QG_LEVELS[qgMeasure.value]}</div>
- <div className="footer truncated-text-ellipsis">
- <SonarCloudIcon fill="#FFF" /> Quality Gate
- </div>
- </a>
- </div>
- );
-}
diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/SonarCloudIcon.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/SonarCloudIcon.tsx
deleted file mode 100644
index 17cbb789252..00000000000
--- a/server/sonar-web/src/main/js/app/integration/vsts/components/SonarCloudIcon.tsx
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * 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 { IconProps } from '../../../../components/icons-components/types';
-
-export default function SonarCloudIcon({ className, fill = '#f3702a', size = 22 }: IconProps) {
- return (
- <svg
- className={className}
- height={size}
- version="1.1"
- viewBox="0 0 22 22"
- width={size}
- xmlSpace="preserve"
- xmlnsXlink="http://www.w3.org/1999/xlink">
- <path
- d="M20.24 10.65l.15.29.14.3.12.3.1.32.09.32.07.33.04.34.03.34.02.35-.06.8-.16.76-.25.73-.34.68-.42.62-.5.57-.56.5-.63.42-.68.34-.72.25-.77.16-.8.05-.39-.01-.39-.04-.38-.06-.37-.09-.37-.11-.35-.13-.34-.16-.33-.17-.31-.2-.31-.21-.28-.23-.28-.25-.27.25-.29.24-.3.22-.32.2-.33.17-.35.16-.35.14-.37.11-.37.09-.39.06-.39.04-.4.02-.8-.06-.76-.15-.73-.26-.68-.34-.63-.42-.56-.5-.5-.56-.42-.63-.34-.68-.25-.72-.16-.77-.05-.8.03-.64.1-.63.17-.6.23-.57.28-.55.34-.51.39-.47.43-.42.48-.38.52-.34.55-.28.59-.22v-.08l.05-.8.16-.76.25-.73.34-.68.43-.63.49-.56.57-.5.63-.42.67-.34.73-.25.77-.16L11 2l.79.05.77.16.73.25.68.34.62.42.57.5.49.56.43.63.34.68.25.73.16.76.05.8v.07l.04.01.27.09.27.11.26.12.25.13.24.14.24.15.23.17.22.17.21.19.21.19.19.21.19.21v.01l-.01-.01.19.26.19.27.17.28zm-3.77-.64l-.11.27-.12.25-.13.25-.15.24-.16.24-.17.23-.18.21-.19.21-.2.2-.21.2-.22.18-.22.17-.03.02-.03.02-.02.01-.03.02-.03.01-.03.02-.04.01-.03.01-.03.01h-.04l-.03.01h-.03l-.04.01h-.03l-.09-.01-.08-.01-.08-.03-.08-.03-.07-.04-.07-.05-.06-.05-.05-.06-.05-.07-.04-.07-.03-.07-.02-.08-.02-.09v-.19l.01-.05.02-.05.01-.05.02-.05.02-.04.03-.05.03-.04.03-.04.03-.04.04-.03.04-.03.04-.03.01-.01.23-.18.22-.2.21-.2.19-.22.18-.24.16-.24.15-.26.12-.27.12-.27.09-.29.07-.29.05-.3.04-.31.01-.31-.03-.51-.09-.5-.14-.48-.19-.45-.24-.42-.28-.39-.32-.36-.36-.33-.39-.28-.43-.23-.45-.2-.48-.14-.49-.08-.51-.03-.5.03-.49.08-.46.13-.44.18-.42.23-.39.27-.35.3-.32.35-.29.37-.24.41-.2.43-.15.46-.1.48-.05.49H7.04l.16.01.15.01.15.01.15.02.15.02.14.03.15.02.14.03.14.04.14.04.14.04.13.04.13.05.02.01.08.02.07.03.07.03.07.03.07.03.07.04.07.03.06.04.07.04.06.04.06.04.07.04.06.04.06.05.03.03.03.03.03.03.03.04.02.04.03.03.02.04.01.05.02.04.01.04.01.05.01.04v.05l.01.05-.01.08-.01.09-.03.08-.03.07-.04.08-.05.06-.05.06-.06.06-.07.04-.07.04-.07.04-.08.02-.09.01-.08.01h-.04l-.04-.01h-.04l-.04-.01-.04-.01-.03-.01-.04-.01-.03-.01-.03-.02-.04-.02-.03-.01-.03-.02-.03-.03-.03-.02.01.01-.03-.02-.03-.02-.03-.02-.04-.02-.03-.02-.03-.02-.04-.02-.03-.01-.04-.02-.04-.01-.03-.02-.04-.01-.04-.02-.04-.01h-.01l.03.01-.03-.01-.08-.03-.1-.03-.11-.03-.11-.03-.11-.03-.1-.03-.12-.02-.11-.02-.11-.01-.11-.02-.12-.01h-.11L7 9.17h-.11l-.52.03-.49.09-.48.14-.45.19-.42.23-.4.29-.36.32-.32.36-.28.39-.24.42-.19.46-.14.47-.09.5-.02.51.02.51.09.5.14.47.19.45.24.43.28.39.32.36.36.32.4.28.42.24.45.19.48.14.49.09.52.03.26-.01.27-.02.26-.04.25-.06.25-.06.24-.09.24-.09.23-.11.22-.12.22-.14.2-.14.2-.16.18-.17.18-.18-.1-.16-.1-.2-.1-.2-.09-.2-.08-.21-.07-.22-.07-.21-.06-.22-.04-.22-.05-.23-.03-.23-.02-.23-.01-.23-.01-.24v-.01l.01-.08.01-.09.02-.08.04-.07.04-.07.04-.07.06-.06.06-.06.06-.04.08-.04.07-.03.08-.03.09-.01.08-.01.09.01.08.01.08.03.08.03.07.04.06.04.07.06.05.06.05.07.04.07.03.07.02.08.02.09v.08l.03.52.09.49.14.48.19.45.24.42.28.39.32.36.36.33.4.28.42.24.45.19.48.14.49.08.52.03-.01-.01.52-.03.49-.09.48-.14.45-.19.42-.24.4-.28.36-.32.32-.36.28-.39.24-.43.19-.45.14-.47.08-.5.03-.51-.01-.4-.06-.39-.08-.38-.12-.36-.15-.35-.18-.34-.2-.32-.23-.29-.25-.28-.28-.26-.3-.23-.32-.2-.34-.18-.35-.15-.01.04-.09.27-.1.27zm-6.36 6.6l-.02-.03.02.03z"
- style={{ fill }}
- />
- </svg>
- );
-}
diff --git a/server/sonar-web/src/main/js/app/integration/vsts/components/Widget.tsx b/server/sonar-web/src/main/js/app/integration/vsts/components/Widget.tsx
deleted file mode 100644
index 276633320a8..00000000000
--- a/server/sonar-web/src/main/js/app/integration/vsts/components/Widget.tsx
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * 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 QGWidget from './QGWidget';
-import LoginForm from './LoginForm';
-import { getMeasuresAndMeta, MeasureComponent } from '../../../../api/measures';
-import { Metric } from '../../../types';
-import { Settings } from '../utils';
-
-interface Props {
- settings: Settings;
-}
-
-interface State {
- component?: MeasureComponent;
- loading: boolean;
- metrics?: Metric[];
- unauthorized: boolean;
-}
-export default class Widget extends React.PureComponent<Props, State> {
- mounted = false;
- state: State = { loading: true, unauthorized: false };
-
- componentDidMount() {
- this.mounted = true;
- const { settings } = this.props;
- if (settings.project) {
- this.fetchProjectMeasures(settings.project);
- } else {
- this.setState({ loading: false });
- }
- }
-
- componentWillReceiveProps(nextProps: Props) {
- const { project } = nextProps.settings;
- if (project !== this.props.settings.project) {
- if (project) {
- this.fetchProjectMeasures(project);
- } else {
- this.setState({ component: undefined });
- }
- }
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- fetchProjectMeasures = (project: string) => {
- this.setState({ loading: true });
- getMeasuresAndMeta(project, ['alert_status'], { additionalFields: 'metrics' }).then(
- ({ component, metrics }) => {
- if (this.mounted) {
- this.setState({ component, loading: false, metrics, unauthorized: false });
- }
- },
- response => {
- if (response && response.response.status === 403) {
- this.setState({ loading: false, unauthorized: true });
- } else {
- this.setState({ loading: false });
- }
- }
- );
- };
-
- handleReload = () => {
- const { settings } = this.props;
- if (settings.project) {
- this.fetchProjectMeasures(settings.project);
- }
- };
-
- render() {
- const { component, loading, metrics, unauthorized } = this.state;
- if (loading) {
- return (
- <div className="vsts-loading">
- <i className="spinner global-loading-spinner" />
- </div>
- );
- }
-
- if (unauthorized) {
- return (
- <div className="widget">
- <LoginForm onReload={this.handleReload} title="Authentication on SonarCloud required" />
- </div>
- );
- }
-
- if (!component || !metrics) {
- return (
- <div className="vsts-widget-configure widget">
- <h2 className="title">Code Quality</h2>
- <div className="content">
- <div>Configure widget</div>
- <img
- alt=""
- src="https://cdn.vsassets.io/v/20180301T143409/_content/Dashboards/unconfigured-small.png"
- />
- </div>
- </div>
- );
- }
-
- return <QGWidget component={component} />;
- }
-}
diff --git a/server/sonar-web/src/main/js/app/integration/vsts/index.js b/server/sonar-web/src/main/js/app/integration/vsts/index.js
deleted file mode 100644
index 468f5190698..00000000000
--- a/server/sonar-web/src/main/js/app/integration/vsts/index.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- * 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 { parse } from 'querystring';
-import React from 'react';
-import { render } from 'react-dom';
-import Configuration from './components/Configuration';
-import Widget from './components/Widget';
-import { parseWidgetSettings } from './utils';
-import './vsts.css';
-
-const container = document.getElementById('content');
-const query = parse(window.location.search.replace('?', ''));
-
-if (query.type === 'authenticated') {
- if (window.opener && window.opener.authenticationDone) {
- window.opener.authenticationDone();
- }
- window.close();
-} else if (VSS && query.contribution && VSS.init && VSS.require) {
- VSS.init({
- explicitNotifyLoaded: true,
- usePlatformStyles: true
- });
-
- VSS.require('TFS/Dashboards/WidgetHelpers', WidgetHelpers => {
- WidgetHelpers.IncludeWidgetStyles();
- WidgetHelpers.IncludeWidgetConfigurationStyles();
-
- if (query.type === 'configuration') {
- render(
- <Configuration contribution={query.contribution} widgetHelpers={WidgetHelpers} />,
- container
- );
- } else {
- VSS.register(query.contribution, () => {
- const loadFunction = loadVSTSWidget(WidgetHelpers);
- return { load: loadFunction, reload: loadFunction };
- });
- }
- VSS.notifyLoadSucceeded();
- });
-}
-
-function loadVSTSWidget(WidgetHelpers) {
- return widgetSettings => {
- try {
- render(<Widget settings={parseWidgetSettings(widgetSettings)} />, container);
- } catch (error) {
- return WidgetHelpers.WidgetStatusHelper.Failure(error);
- }
-
- return WidgetHelpers.WidgetStatusHelper.Success();
- };
-}
diff --git a/server/sonar-web/src/main/js/app/integration/vsts/utils.ts b/server/sonar-web/src/main/js/app/integration/vsts/utils.ts
deleted file mode 100644
index ec9bc4688b8..00000000000
--- a/server/sonar-web/src/main/js/app/integration/vsts/utils.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * 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.
- */
-
-export interface VSTSConfigurationContext {
- notify: Function;
-}
-
-export interface VSTSCustomSettings {
- data: string;
-}
-
-export interface VSTSWidgetSettings {
- customSettings: VSTSCustomSettings;
-}
-
-export interface Settings {
- project?: string;
-}
-
-export function parseWidgetSettings(widgetSettings: VSTSWidgetSettings): Settings {
- try {
- return JSON.parse(widgetSettings.customSettings.data) || {};
- } catch (e) {
- return {};
- }
-}
-
-export function serializeWidgetSettings(parsedSettings: Settings): VSTSCustomSettings {
- return { data: JSON.stringify(parsedSettings) };
-}
diff --git a/server/sonar-web/src/main/js/app/integration/vsts/vsts.css b/server/sonar-web/src/main/js/app/integration/vsts/vsts.css
deleted file mode 100644
index 634a9bd345a..00000000000
--- a/server/sonar-web/src/main/js/app/integration/vsts/vsts.css
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * 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 '../../styles/components/spinner.css';
-@import '../../styles/components/global-loading.css';
-
-#content {
- height: 100%;
-}
-
-.vsts-loading {
- height: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.vsts-widget-configure {
- display: block;
- position: relative;
- width: 100%;
- height: 100%;
- padding: 10px 14px;
- font-size: 16px;
-}
-
-.vsts-widget-configure .title {
- color: #333;
- font-weight: normal;
-}
-
-.vsts-widget-configure .content {
- padding-top: 10%;
- text-align: center;
- color: #666;
-}
-
-.vsts-widget-configure img {
- height: 40px;
- margin-top: 10px;
-}
-
-.widget.dark-widget.clickable > a {
- color: white;
-}
-
-.widget .footer {
- display: flex;
- align-items: center;
-}
-
-.widget .footer svg {
- margin-right: 8px;
-}
-
-.vsts-widget-login {
- text-align: center;
- padding-top: 4px;
-}
-
-.vsts-widget-login .login-message-text {
- color: #666;
- margin: 0;
-}
-
-.vsts-widget-login .oauth-providers {
- margin-top: 8px;
- margin-bottom: 8px;
-}
-.vsts-widget-login .oauth-providers a {
- display: inline-block;
- line-height: 22px;
- padding: 4px 6px;
- border: 1px solid rgba(0, 0, 0, 0.15);
- border-radius: 2px;
- box-sizing: border-box;
- background-color: var(--darkBlue);
- color: #fff;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
-}
-
-.vsts-widget-login .oauth-providers a:hover,
-.vsts-widget-login .oauth-providers a:focus {
- box-shadow: 0 0 16px rgba(0, 0, 0, 0.2);
-}
-
-.vsts-widget-login .oauth-providers span {
- padding-left: 4px;
-}
-
-.vsts-widget-login .oauth-providers img {
- vertical-align: top;
-}
-
-.vsts-configuration {
- min-height: 540px;
-}
-
-.vsts-configuration .config-settings-field {
- margin-bottom: 20px;
-}
-
-.big-value {
- font-size: 36px;
- line-height: 68px;
- margin: 20px 0 10px 0;
- font-weight: 300;
-}
-
-.level-OK {
- background-color: var(--green);
-}
-
-.level-WARN {
- background-color: var(--orange);
-}
-
-.level-ERROR {
- background-color: var(--red);
-}
-
-.level-NONE {
- background-color: var(--gray71);
-}
-
-.Select {
- width: 100%;
-}
-
-.project-picker {
- position: relative;
- width: 100%;
- height: 32px;
-}
-
-.filtered-list-dropdown-menu {
- white-space: nowrap;
- position: relative;
- cursor: pointer;
- padding: 6px;
- border: 1px solid #c8c8c8;
-}
-
-.filtered-list-dropdown-menu .drop-icon {
- float: right;
- position: relative;
- overflow: hidden;
- vertical-align: middle;
-}
-
-.filtered-list-dropdown-menu .selected-item-text {
- width: 90%;
- padding-left: 5px;
- padding-right: 5px;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- word-wrap: normal;
- vertical-align: middle;
- display: inline-block;
-}
-
-.filtered-list-popup {
- position: absolute;
- display: block;
- width: 100%;
- top: 30px;
- left: 0;
- z-index: 20000;
- overflow-y: auto;
- max-height: 400px;
- font-size: 12px;
- background-color: #fff;
- border: 1px solid #c8c8c8;
- box-shadow: 0 2.5px 5px rgba(0, 0, 0, 0.4);
-}
-
-.filtered-list-popup .filtered-list-control .pivot-view {
- margin-left: 0;
-}
-
-.filtered-list-control.bowtie-filtered-list .filtered-list-tab > a {
- outline: none;
-}
-
-.filtered-list-tab.selected::before {
- content: '';
- position: absolute;
- bottom: 0;
- left: 10px;
- right: 10px;
- height: 2px;
- background-color: #0078d7;
-}
-
-.filtered-list-tab:first-child.selected::before {
- left: 0;
-}
-
-.filtered-list-control .filter-container {
- position: relative;
-}
-
-.filtered-list-search::placeholder {
- font-size: 12px;
-}
-
-.filtered-list-search-container .spinner {
- position: absolute;
- right: 16px;
- bottom: 18px;
-}
-
-.filtered-list-control.bowtie-filtered-list .filter-container {
- padding: 10px;
- width: 100%;
-}
-
-.filtered-list-control.bowtie-filtered-list .filtered-list {
- padding: 0;
- margin: 0 0 4px 0;
- max-height: 300px;
- overflow: auto;
-}
-
-.filtered-list-control .filtered-list > li {
- list-style-type: none;
- padding: 5px 10px;
- border: none;
- margin: 0;
- height: 30px;
- line-height: 20px;
- cursor: pointer;
- position: relative;
- vertical-align: middle;
- outline: none;
- overflow: hidden;
- text-overflow: ellipsis;
- word-wrap: normal;
- white-space: pre;
-}
-
-.filtered-list-control .filtered-list > li.filtered-list-message {
- white-space: normal;
- color: #666;
- cursor: default;
- overflow: visible;
-}
-
-.filtered-list-control .filtered-list > li.filtered-list-item.current-item {
- font-weight: 700;
- color: #212121;
- background-color: #f4f4f4;
-}
-
-.filtered-list-control .filtered-list > li.filtered-list-item.active-item {
- font-weight: normal;
- color: #212121;
- background-color: #eff6fc;
-}
-
-.filtered-list-control .filtered-list > li.filtered-list-item.active-item::after {
- content: '';
- position: absolute;
- top: 0;
- right: 0;
- bottom: 0;
- left: 0;
- pointer-events: none;
- border: 1px solid #a6a6a6;
-}
-
-.filtered-list-control .filtered-list > li.filtered-list-item.current-item.active-item {
- font-weight: 700;
-}