aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-07-06 11:20:07 +0200
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-07-13 14:34:17 +0200
commitcc23c3d23ccd2e1b6b41b0211ee89ed1398818ee (patch)
tree4ec9aa5b6872343d382f1e890703c57fb1e8e785
parent739f7b148d606764efa7d785553b2eb4de4d2477 (diff)
downloadsonarqube-cc23c3d23ccd2e1b6b41b0211ee89ed1398818ee.tar.gz
sonarqube-cc23c3d23ccd2e1b6b41b0211ee89ed1398818ee.zip
SONAR-9403 Suport custom graph via url parameters on project activity page
-rw-r--r--server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/events/PreviewGraph.js14
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/__tests__/__snapshots__/utils-test.js.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/__tests__/actions-test.js2
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.js56
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js8
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js24
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js17
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js9
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityGraphs-test.js.snap6
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/types.js1
-rw-r--r--server/sonar-web/src/main/js/apps/projectActivity/utils.js39
-rw-r--r--sonar-core/src/main/resources/org/sonar/l10n/core.properties3
13 files changed, 150 insertions, 39 deletions
diff --git a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js
index 3ca38235265..ca6094836ef 100644
--- a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js
+++ b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.js
@@ -101,7 +101,11 @@ export default class OverviewApp extends React.PureComponent {
}
loadHistory(component: Component) {
- const metrics = uniq(HISTORY_METRICS_LIST.concat(GRAPHS_METRICS_DISPLAYED[getGraph()]));
+ let graphMetrics = GRAPHS_METRICS_DISPLAYED[getGraph()];
+ if (!graphMetrics || graphMetrics.length <= 0) {
+ graphMetrics = GRAPHS_METRICS_DISPLAYED['overview'];
+ }
+ const metrics = uniq(HISTORY_METRICS_LIST.concat(graphMetrics));
return getAllTimeMachineData(component.key, metrics).then(r => {
if (this.mounted) {
const history: History = {};
diff --git a/server/sonar-web/src/main/js/apps/overview/events/PreviewGraph.js b/server/sonar-web/src/main/js/apps/overview/events/PreviewGraph.js
index 39cbfbeb7c4..d5cd5f6aea5 100644
--- a/server/sonar-web/src/main/js/apps/overview/events/PreviewGraph.js
+++ b/server/sonar-web/src/main/js/apps/overview/events/PreviewGraph.js
@@ -67,16 +67,24 @@ export default class PreviewGraph extends React.PureComponent {
}
}
+ getDisplayedMetrics = (graph: string) => {
+ const metrics = GRAPHS_METRICS_DISPLAYED[graph];
+ if (!metrics || metrics.length <= 0) {
+ return GRAPHS_METRICS_DISPLAYED['overview'];
+ }
+ return metrics;
+ };
+
getSeries = (history: History, graph: string, metricsType: string): Array<Serie> => {
const measureHistory = map(history, (item, key) => ({
metric: key,
history: item.filter(p => p.value != null)
- })).filter(item => GRAPHS_METRICS_DISPLAYED[graph].indexOf(item.metric) >= 0);
- return generateSeries(measureHistory, graph, metricsType);
+ }));
+ return generateSeries(measureHistory, graph, metricsType, this.getDisplayedMetrics(graph));
};
getMetricType = (metrics: Array<Metric>, graph: string) => {
- const metricKey = GRAPHS_METRICS_DISPLAYED[graph][0];
+ const metricKey = this.getDisplayedMetrics(graph)[0];
const metric = metrics.find(metric => metric.key === metricKey);
return metric ? metric.type : 'INT';
};
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/__snapshots__/utils-test.js.snap b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/__snapshots__/utils-test.js.snap
index 6929c54a2e2..e9144bf2e16 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/__snapshots__/utils-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/__snapshots__/utils-test.js.snap
@@ -32,7 +32,7 @@ Array [
},
],
"name": "lines_to_cover",
- "style": 1,
+ "style": "0",
"translatedName": "metric.lines_to_cover.name",
},
Object {
@@ -47,7 +47,7 @@ Array [
},
],
"name": "covered_lines",
- "style": 0,
+ "style": "1",
"translatedName": "project_activity.custom_metric.covered_lines",
},
]
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/actions-test.js b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/actions-test.js
index c28dfae5210..d0c8af6370f 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/actions-test.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/actions-test.js
@@ -69,7 +69,7 @@ const emptyState = {
measuresHistory: [],
measures: [],
metrics: [],
- query: { category: '', graph: '', project: '' }
+ query: { category: '', graph: '', project: '', customMetrics: [] }
};
const state = { ...emptyState, analyses: ANALYSES };
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.js b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.js
index 3069113eb29..d94a9f626c4 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.js
@@ -77,7 +77,9 @@ const QUERY = {
from: new Date('2017-04-27T08:21:32+0200'),
graph: 'overview',
project: 'foo',
- to: undefined
+ to: undefined,
+ selectedDate: undefined,
+ customMetrics: ['foo', 'bar', 'baz']
};
jest.mock('moment', () => date => ({
@@ -97,7 +99,9 @@ describe('generateCoveredLinesMetric', () => {
describe('generateSeries', () => {
it('should correctly generate the series', () => {
- expect(utils.generateSeries(HISTORY, 'coverage', 'INT')).toMatchSnapshot();
+ expect(
+ utils.generateSeries(HISTORY, 'coverage', 'INT', ['lines_to_cover', 'uncovered_lines'])
+ ).toMatchSnapshot();
});
});
@@ -107,12 +111,51 @@ describe('getAnalysesByVersionByDay', () => {
});
});
+describe('getDisplayedHistoryMetrics', () => {
+ const customMetrics = ['foo', 'bar'];
+ it('should return only displayed metrics on the graph', () => {
+ expect(utils.getDisplayedHistoryMetrics('overview', [])).toEqual([
+ 'bugs',
+ 'code_smells',
+ 'vulnerabilities'
+ ]);
+ expect(utils.getDisplayedHistoryMetrics('coverage', customMetrics)).toEqual([
+ 'uncovered_lines',
+ 'lines_to_cover'
+ ]);
+ });
+ it('should return all custom metrics for the custom graph', () => {
+ expect(utils.getDisplayedHistoryMetrics('custom', customMetrics)).toEqual(customMetrics);
+ });
+});
+
+describe('getHistoryMetrics', () => {
+ const customMetrics = ['foo', 'bar'];
+ it('should return all metrics', () => {
+ expect(utils.getHistoryMetrics('overview', [])).toEqual([
+ 'bugs',
+ 'code_smells',
+ 'vulnerabilities',
+ 'reliability_rating',
+ 'security_rating',
+ 'sqale_rating'
+ ]);
+ expect(utils.getHistoryMetrics('coverage', customMetrics)).toEqual([
+ 'uncovered_lines',
+ 'lines_to_cover',
+ 'coverage'
+ ]);
+ expect(utils.getHistoryMetrics('custom', customMetrics)).toEqual(customMetrics);
+ });
+});
+
describe('parseQuery', () => {
it('should parse query with default values', () => {
expect(
utils.parseQuery({
from: '2017-04-27T08:21:32+0200',
- id: 'foo'
+ id: 'foo',
+ custom_metrics: 'foo,bar,baz'
})
).toEqual(QUERY);
});
@@ -136,9 +179,12 @@ describe('serializeUrlQuery', () => {
it('should serialize query for url', () => {
expect(utils.serializeUrlQuery(QUERY)).toEqual({
from: '2017-04-27T06:21:32.000Z',
- id: 'foo'
+ id: 'foo',
+ custom_metrics: 'foo,bar,baz'
});
- expect(utils.serializeUrlQuery({ ...QUERY, graph: 'coverage', category: 'test' })).toEqual({
+ expect(
+ utils.serializeUrlQuery({ ...QUERY, graph: 'coverage', category: 'test', customMetrics: [] })
+ ).toEqual({
from: '2017-04-27T06:21:32.000Z',
id: 'foo',
graph: 'coverage',
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js
index 46b6dd4fe53..954c6169f29 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js
@@ -24,7 +24,7 @@ import moment from 'moment';
import ProjectActivityPageHeader from './ProjectActivityPageHeader';
import ProjectActivityAnalysesList from './ProjectActivityAnalysesList';
import ProjectActivityGraphs from './ProjectActivityGraphs';
-import { GRAPHS_METRICS_DISPLAYED, activityQueryChanged } from '../utils';
+import { getDisplayedHistoryMetrics, activityQueryChanged } from '../utils';
import { translate } from '../../../helpers/l10n';
import './projectActivity.css';
import type { Analysis, MeasureHistory, Metric, Query } from '../types';
@@ -82,7 +82,11 @@ export default class ProjectActivityApp extends React.PureComponent {
};
getMetricType = () => {
- const metricKey = GRAPHS_METRICS_DISPLAYED[this.props.query.graph][0];
+ const historyMetrics = getDisplayedHistoryMetrics(
+ this.props.query.graph,
+ this.props.query.customMetrics
+ );
+ const metricKey = historyMetrics.length > 0 ? historyMetrics[0] : '';
const metric = this.props.metrics.find(metric => metric.key === metricKey);
return metric ? metric.type : 'INT';
};
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js
index 774f1165eae..3ebe68f48de 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAppContainer.js
@@ -30,7 +30,13 @@ import { getMetrics } from '../../../api/metrics';
import * as api from '../../../api/projectActivity';
import * as actions from '../actions';
import { getGraph, saveGraph } from '../../../helpers/storage';
-import { GRAPHS_METRICS, parseQuery, serializeQuery, serializeUrlQuery } from '../utils';
+import {
+ customMetricsChanged,
+ getHistoryMetrics,
+ parseQuery,
+ serializeQuery,
+ serializeUrlQuery
+} from '../utils';
import type { RawQuery } from '../../../helpers/query';
import type { Analysis, MeasureHistory, Metric, Paging, Query } from '../types';
@@ -89,8 +95,8 @@ class ProjectActivityAppContainer extends React.PureComponent {
componentWillReceiveProps(nextProps: Props) {
if (nextProps.location.query !== this.props.location.query) {
const query = parseQuery(nextProps.location.query);
- if (query.graph !== this.state.query.graph) {
- this.updateGraphData(query.graph);
+ if (query.graph !== this.state.query.graph || customMetricsChanged(this.state.query, query)) {
+ this.updateGraphData(query.graph, query.customMetrics);
}
this.setState({ query });
}
@@ -158,6 +164,9 @@ class ProjectActivityAppContainer extends React.PureComponent {
};
fetchMeasuresHistory = (metrics: Array<string>): Promise<Array<MeasureHistory>> => {
+ if (metrics.length <= 0) {
+ return Promise.resolve([]);
+ }
return getAllTimeMachineData(this.props.project.key, metrics).then(
({ measures }) =>
measures.map(measure => ({
@@ -197,7 +206,7 @@ class ProjectActivityAppContainer extends React.PureComponent {
firstLoadData() {
const { query } = this.state;
- const graphMetrics = GRAPHS_METRICS[query.graph];
+ const graphMetrics = getHistoryMetrics(query.graph, query.customMetrics);
const ignoreHistory = this.shouldRedirect();
Promise.all([
this.fetchActivity(query.project, 1, 100, serializeQuery(query)),
@@ -237,11 +246,10 @@ class ProjectActivityAppContainer extends React.PureComponent {
});
}
- updateGraphData = (graph: string) => {
+ updateGraphData = (graph: string, customMetrics: Array<string>) => {
+ const graphMetrics = getHistoryMetrics(graph, customMetrics);
this.setState({ graphLoading: true });
- return this.fetchMeasuresHistory(
- GRAPHS_METRICS[graph]
- ).then((measuresHistory: Array<MeasureHistory>) =>
+ this.fetchMeasuresHistory(graphMetrics).then((measuresHistory: Array<MeasureHistory>) =>
this.setState({ graphLoading: false, measuresHistory })
);
};
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js
index 03605c8114c..7fb3bb818f6 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityGraphs.js
@@ -23,7 +23,12 @@ import { debounce, findLast, maxBy, minBy, sortBy } from 'lodash';
import ProjectActivityGraphsHeader from './ProjectActivityGraphsHeader';
import GraphsZoom from './GraphsZoom';
import StaticGraphs from './StaticGraphs';
-import { datesQueryChanged, generateSeries, historyQueryChanged } from '../utils';
+import {
+ datesQueryChanged,
+ generateSeries,
+ getDisplayedHistoryMetrics,
+ historyQueryChanged
+} from '../utils';
import type { RawQuery } from '../../../helpers/query';
import type { Analysis, MeasureHistory, Query } from '../types';
import type { Serie } from '../../../components/charts/AdvancedTimeline';
@@ -51,7 +56,12 @@ export default class ProjectActivityGraphs extends React.PureComponent {
constructor(props: Props) {
super(props);
- const series = generateSeries(props.measuresHistory, props.query.graph, props.metricsType);
+ const series = generateSeries(
+ props.measuresHistory,
+ props.query.graph,
+ props.metricsType,
+ getDisplayedHistoryMetrics(props.query.graph, props.query.customMetrics)
+ );
this.state = { series, ...this.getStateZoomDates(null, props, series) };
this.updateQueryDateRange = debounce(this.updateQueryDateRange, 500);
}
@@ -64,7 +74,8 @@ export default class ProjectActivityGraphs extends React.PureComponent {
const series = generateSeries(
nextProps.measuresHistory,
nextProps.query.graph,
- nextProps.metricsType
+ nextProps.metricsType,
+ getDisplayedHistoryMetrics(nextProps.query.graph, nextProps.query.customMetrics)
);
const newDates = this.getStateZoomDates(this.props, nextProps, series);
if (newDates) {
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js b/server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js
index 7209c9ee15b..a303e66b9bc 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/StaticGraphs.js
@@ -25,7 +25,7 @@ import AdvancedTimeline from '../../../components/charts/AdvancedTimeline';
import GraphsTooltips from './GraphsTooltips';
import StaticGraphsLegend from './StaticGraphsLegend';
import { formatMeasure, getShortType } from '../../../helpers/measures';
-import { EVENT_TYPES } from '../utils';
+import { EVENT_TYPES, isCustomGraph } from '../utils';
import { translate } from '../../../helpers/l10n';
import type { Analysis, MeasureHistory } from '../types';
import type { Serie } from '../../../components/charts/AdvancedTimeline';
@@ -121,11 +121,16 @@ export default class StaticGraphs extends React.PureComponent {
return (
<div className="project-activity-graph-container">
<div className="note text-center">
- {translate('component_measures.no_history')}
+ {translate(
+ isCustomGraph(this.props.graph)
+ ? 'project_activity.graphs.custom.no_history'
+ : 'component_measures.no_history'
+ )}
</div>
</div>
);
}
+
const { selectedDate, tooltipIdx, tooltipXPos } = this.state;
const { graph, series } = this.props;
return (
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityGraphs-test.js.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityGraphs-test.js.snap
index 8fabf12a07c..6da3cc4d3bb 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityGraphs-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityGraphs-test.js.snap
@@ -92,7 +92,7 @@ exports[`should render correctly the graph and legends 1`] = `
},
],
"name": "code_smells",
- "style": 1,
+ "style": "1",
"translatedName": "metric.code_smells.name",
},
]
@@ -124,7 +124,7 @@ exports[`should render correctly the graph and legends 1`] = `
},
],
"name": "code_smells",
- "style": 1,
+ "style": "1",
"translatedName": "metric.code_smells.name",
},
]
@@ -156,7 +156,7 @@ Object {
},
],
"name": "code_smells",
- "style": 1,
+ "style": "1",
"translatedName": "metric.code_smells.name",
},
],
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/types.js b/server/sonar-web/src/main/js/apps/projectActivity/types.js
index f3c75fa8535..9a9c96abf71 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/types.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/types.js
@@ -50,6 +50,7 @@ export type Paging = {
export type Query = {
category: string,
+ customMetrics: Array<string>,
from?: Date,
graph: string,
project: string,
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/utils.js b/server/sonar-web/src/main/js/apps/projectActivity/utils.js
index 5e04dcc5436..9ee44a3fc59 100644
--- a/server/sonar-web/src/main/js/apps/projectActivity/utils.js
+++ b/server/sonar-web/src/main/js/apps/projectActivity/utils.js
@@ -19,10 +19,13 @@
*/
// @flow
import moment from 'moment';
+import { isEqual } from 'lodash';
import {
cleanQuery,
+ parseAsArray,
parseAsDate,
parseAsString,
+ serializeStringArray,
serializeDate,
serializeString
} from '../../helpers/query';
@@ -32,7 +35,7 @@ import type { RawQuery } from '../../helpers/query';
import type { Serie } from '../../components/charts/AdvancedTimeline';
export const EVENT_TYPES = ['VERSION', 'QUALITY_GATE', 'QUALITY_PROFILE', 'OTHER'];
-export const GRAPH_TYPES = ['overview', 'coverage', 'duplications'];
+export const GRAPH_TYPES = ['overview', 'coverage', 'duplications', 'custom'];
export const GRAPHS_METRICS_DISPLAYED = {
overview: ['bugs', 'code_smells', 'vulnerabilities'],
coverage: ['uncovered_lines', 'lines_to_cover'],
@@ -51,6 +54,9 @@ export const GRAPHS_METRICS = {
export const activityQueryChanged = (prevQuery: Query, nextQuery: Query): boolean =>
prevQuery.category !== nextQuery.category || datesQueryChanged(prevQuery, nextQuery);
+export const customMetricsChanged = (prevQuery: Query, nextQuery: Query): boolean =>
+ !isEqual(prevQuery.customMetrics, nextQuery.customMetrics);
+
export const datesQueryChanged = (prevQuery: Query, nextQuery: Query): boolean => {
const nextFrom = nextQuery.from ? nextQuery.from.valueOf() : null;
const previousFrom = prevQuery.from ? prevQuery.from.valueOf() : null;
@@ -62,6 +68,8 @@ export const datesQueryChanged = (prevQuery: Query, nextQuery: Query): boolean =
export const historyQueryChanged = (prevQuery: Query, nextQuery: Query): boolean =>
prevQuery.graph !== nextQuery.graph;
+export const isCustomGraph = (graph: string) => graph === 'custom';
+
export const selectedDateQueryChanged = (prevQuery: Query, nextQuery: Query): boolean => {
const nextSelectedDate = nextQuery.selectedDate ? nextQuery.selectedDate.valueOf() : null;
const previousSelectedDate = prevQuery.selectedDate ? prevQuery.selectedDate.valueOf() : null;
@@ -90,28 +98,33 @@ export const generateCoveredLinesMetric = (
export const generateSeries = (
measuresHistory: Array<MeasureHistory>,
graph: string,
- dataType: string
-): Array<Serie> =>
- measuresHistory
- .filter(measure => GRAPHS_METRICS_DISPLAYED[graph].indexOf(measure.metric) >= 0)
+ dataType: string,
+ displayedMetrics: Array<string>
+): Array<Serie> => {
+ if (displayedMetrics.length <= 0) {
+ return [];
+ }
+ return measuresHistory
+ .filter(measure => displayedMetrics.indexOf(measure.metric) >= 0)
.map(measure => {
- if (measure.metric === 'uncovered_lines') {
+ if (measure.metric === 'uncovered_lines' && !isCustomGraph(graph)) {
return generateCoveredLinesMetric(
measure,
measuresHistory,
- GRAPHS_METRICS_DISPLAYED[graph].indexOf(measure.metric)
+ displayedMetrics.indexOf(measure.metric).toString()
);
}
return {
name: measure.metric,
translatedName: translate('metric', measure.metric, 'name'),
- style: GRAPHS_METRICS_DISPLAYED[graph].indexOf(measure.metric),
+ style: displayedMetrics.indexOf(measure.metric).toString(),
data: measure.history.map(analysis => ({
x: analysis.date,
y: dataType === 'LEVEL' ? analysis.value : Number(analysis.value)
}))
};
});
+};
export const getAnalysesByVersionByDay = (
analyses: Array<Analysis>
@@ -140,6 +153,14 @@ export const getAnalysesByVersionByDay = (
return acc;
}, []);
+export const getDisplayedHistoryMetrics = (
+ graph: string,
+ customMetrics: Array<string>
+): Array<string> => (isCustomGraph(graph) ? customMetrics : GRAPHS_METRICS_DISPLAYED[graph]);
+
+export const getHistoryMetrics = (graph: string, customMetrics: Array<string>): Array<string> =>
+ (isCustomGraph(graph) ? customMetrics : GRAPHS_METRICS[graph]);
+
const parseGraph = (value?: string): string => {
const graph = parseAsString(value);
return GRAPH_TYPES.includes(graph) ? graph : 'overview';
@@ -149,6 +170,7 @@ const serializeGraph = (value: string): ?string => (value === 'overview' ? undef
export const parseQuery = (urlQuery: RawQuery): Query => ({
category: parseAsString(urlQuery['category']),
+ customMetrics: parseAsArray(urlQuery['custom_metrics'], parseAsString),
from: parseAsDate(urlQuery['from']),
graph: parseGraph(urlQuery['graph']),
project: parseAsString(urlQuery['id']),
@@ -167,6 +189,7 @@ export const serializeQuery = (query: Query): RawQuery =>
export const serializeUrlQuery = (query: Query): RawQuery => {
return cleanQuery({
category: serializeString(query.category),
+ custom_metrics: serializeStringArray(query.customMetrics),
from: serializeDate(query.from),
graph: serializeGraph(query.graph),
id: serializeString(query.project),
diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
index f42eca5589d..b5a2d54c317 100644
--- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties
+++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties
@@ -1287,7 +1287,8 @@ project_activity.filter_events=Filter events
project_activity.graphs.overview=Overview
project_activity.graphs.coverage=Coverage
project_activity.graphs.duplications=Duplications
-project_activity.graphs.remediation=Remediation Effort
+project_activity.graphs.custom=Custom
+project_activity.graphs.custom.no_history=There is no historical data to show, please add more metrics to your graph.
project_activity.custom_metric.covered_lines=Covered Lines