/* * 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 { uniq } from 'lodash'; import { connect } from 'react-redux'; import ApplicationQualityGate from '../qualityGate/ApplicationQualityGate'; import BugsAndVulnerabilities from '../main/BugsAndVulnerabilities'; import CodeSmells from '../main/CodeSmells'; import Coverage from '../main/Coverage'; import Duplications from '../main/Duplications'; import MetaContainer from '../meta/MetaContainer'; import QualityGate from '../qualityGate/QualityGate'; import throwGlobalError from '../../../app/utils/throwGlobalError'; import { getMeasuresAndMeta } from '../../../api/measures'; import { getAllTimeMachineData } from '../../../api/time-machine'; import { parseDate } from '../../../helpers/dates'; import { enhanceMeasuresWithMetrics } from '../../../helpers/measures'; import { getLeakPeriod } from '../../../helpers/periods'; import { get } from '../../../helpers/storage'; import { METRICS, HISTORY_METRICS_LIST } from '../utils'; import { DEFAULT_GRAPH, getDisplayedHistoryMetrics, PROJECT_ACTIVITY_GRAPH, PROJECT_ACTIVITY_GRAPH_CUSTOM } from '../../projectActivity/utils'; import { isSameBranchLike, getBranchLikeQuery, isLongLivingBranch } from '../../../helpers/branches'; import { fetchMetrics } from '../../../store/rootActions'; import { getMetrics, Store } from '../../../store/rootReducer'; import { translate } from '../../../helpers/l10n'; import '../styles.css'; interface OwnProps { branchLike?: T.BranchLike; component: T.Component; onComponentChange: (changes: {}) => void; } interface StateToProps { metrics: { [key: string]: T.Metric }; } interface DispatchToProps { fetchMetrics: () => void; } type Props = StateToProps & DispatchToProps & OwnProps; interface State { history?: { [metric: string]: Array<{ date: Date; value?: string }>; }; historyStartDate?: Date; loading: boolean; measures: T.MeasureEnhanced[]; periods?: T.Period[]; } export class OverviewApp extends React.PureComponent { mounted = false; state: State = { loading: true, measures: [] }; componentDidMount() { this.mounted = true; this.props.fetchMetrics(); this.loadMeasures().then(this.loadHistory, () => {}); } componentDidUpdate(prevProps: Props) { if ( this.props.component.key !== prevProps.component.key || !isSameBranchLike(this.props.branchLike, prevProps.branchLike) ) { this.loadMeasures().then(this.loadHistory, () => {}); } } componentWillUnmount() { this.mounted = false; } loadMeasures() { const { branchLike, component } = this.props; this.setState({ loading: true }); return getMeasuresAndMeta(component.key, METRICS, { additionalFields: 'metrics,periods', ...getBranchLikeQuery(branchLike) }).then( ({ component, metrics, periods }) => { if (this.mounted && metrics && component.measures) { this.setState({ loading: false, measures: enhanceMeasuresWithMetrics(component.measures, metrics), periods }); } }, error => { throwGlobalError(error); if (this.mounted) { this.setState({ loading: false }); } } ); } loadHistory = () => { const { branchLike, component } = this.props; const customGraphs = get(PROJECT_ACTIVITY_GRAPH_CUSTOM); let graphMetrics = getDisplayedHistoryMetrics( get(PROJECT_ACTIVITY_GRAPH) || 'issues', customGraphs ? customGraphs.split(',') : [] ); if (!graphMetrics || graphMetrics.length <= 0) { graphMetrics = getDisplayedHistoryMetrics(DEFAULT_GRAPH, []); } const metrics = uniq(HISTORY_METRICS_LIST.concat(graphMetrics)); return getAllTimeMachineData({ ...getBranchLikeQuery(branchLike), component: component.key, metrics: metrics.join() }).then(r => { if (this.mounted) { const history: { [metric: string]: Array<{ date: Date; value?: string }> } = {}; r.measures.forEach(measure => { const measureHistory = measure.history.map(analysis => ({ date: parseDate(analysis.date), value: analysis.value })); history[measure.metric] = measureHistory; }); const historyStartDate = history[HISTORY_METRICS_LIST[0]][0].date; this.setState({ history, historyStartDate }); } }); }; getApplicationLeakPeriod = () => this.state.measures.find(measure => measure.metric.key === 'new_bugs') ? ({ index: 1 } as T.Period) : undefined; isEmpty = () => this.state.measures === undefined || this.state.measures.find(measure => measure.metric.key === 'ncloc') === undefined; renderLoading() { return (
); } renderEmpty() { const { component } = this.props; const isApp = component.qualifier === 'APP'; return (

{!this.state.measures || !this.state.measures.find(measure => measure.metric.key === 'projects') ? translate(isApp ? 'portfolio.app.empty' : 'overview.project.empty') : translate( isApp ? 'portfolio.app.no_lines_of_code' : 'overview.project.no_lines_of_code' )}

); } renderMain() { const { branchLike, component } = this.props; const { periods, measures, history, historyStartDate } = this.state; const leakPeriod = component.qualifier === 'APP' ? this.getApplicationLeakPeriod() : getLeakPeriod(periods); const domainProps = { branchLike, component, measures, leakPeriod, history, historyStartDate }; if (this.isEmpty()) { return this.renderEmpty(); } return (
{component.qualifier === 'APP' ? ( ) : ( )}
); } render() { const { branchLike, component } = this.props; const { loading, measures, history } = this.state; if (loading) { return this.renderLoading(); } return (
{this.renderMain()}
); } } const mapDispatchToProps: DispatchToProps = { fetchMetrics }; const mapStateToProps = (state: Store): StateToProps => ({ metrics: getMetrics(state) }); export default connect( mapStateToProps, mapDispatchToProps )(OverviewApp);