|
@@ -12,22 +12,37 @@ import TransitionChart from 'app/components/charts/transitionChart';
|
|
import TransparentLoadingMask from 'app/components/charts/transparentLoadingMask';
|
|
import TransparentLoadingMask from 'app/components/charts/transparentLoadingMask';
|
|
import QuestionTooltip from 'app/components/questionTooltip';
|
|
import QuestionTooltip from 'app/components/questionTooltip';
|
|
import {PlatformKey} from 'app/data/platformCategories';
|
|
import {PlatformKey} from 'app/data/platformCategories';
|
|
-import {ReleaseComparisonChartType} from 'app/types';
|
|
|
|
-import {Series} from 'app/types/echarts';
|
|
|
|
|
|
+import {t} from 'app/locale';
|
|
|
|
+import {
|
|
|
|
+ ReleaseComparisonChartType,
|
|
|
|
+ ReleaseProject,
|
|
|
|
+ ReleaseWithHealth,
|
|
|
|
+ SessionApiResponse,
|
|
|
|
+ SessionField,
|
|
|
|
+ SessionStatus,
|
|
|
|
+} from 'app/types';
|
|
import {defined} from 'app/utils';
|
|
import {defined} from 'app/utils';
|
|
|
|
+import {getCrashFreeRateSeries, getSessionStatusRateSeries} from 'app/utils/sessions';
|
|
import {Theme} from 'app/utils/theme';
|
|
import {Theme} from 'app/utils/theme';
|
|
import {displayCrashFreePercent} from 'app/views/releases/utils';
|
|
import {displayCrashFreePercent} from 'app/views/releases/utils';
|
|
|
|
|
|
import {
|
|
import {
|
|
|
|
+ generateReleaseMarkLines,
|
|
releaseComparisonChartHelp,
|
|
releaseComparisonChartHelp,
|
|
releaseComparisonChartTitles,
|
|
releaseComparisonChartTitles,
|
|
releaseMarkLinesLabels,
|
|
releaseMarkLinesLabels,
|
|
} from '../../utils';
|
|
} from '../../utils';
|
|
|
|
+import {
|
|
|
|
+ fillChartDataFromSessionsResponse,
|
|
|
|
+ initSessionsBreakdownChartData,
|
|
|
|
+} from '../chart/utils';
|
|
|
|
|
|
type Props = {
|
|
type Props = {
|
|
theme: Theme;
|
|
theme: Theme;
|
|
- series: Series[];
|
|
|
|
- previousSeries: Series[];
|
|
|
|
|
|
+ release: ReleaseWithHealth;
|
|
|
|
+ project: ReleaseProject;
|
|
|
|
+ releaseSessions: SessionApiResponse | null;
|
|
|
|
+ allSessions: SessionApiResponse | null;
|
|
chartType: ReleaseComparisonChartType;
|
|
chartType: ReleaseComparisonChartType;
|
|
platform: PlatformKey;
|
|
platform: PlatformKey;
|
|
value: React.ReactNode;
|
|
value: React.ReactNode;
|
|
@@ -70,7 +85,7 @@ class ReleaseSessionsChart extends React.Component<Props> {
|
|
}
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
- configureYAxis() {
|
|
|
|
|
|
+ getYAxis() {
|
|
const {theme, chartType} = this.props;
|
|
const {theme, chartType} = this.props;
|
|
switch (chartType) {
|
|
switch (chartType) {
|
|
case ReleaseComparisonChartType.CRASH_FREE_SESSIONS:
|
|
case ReleaseComparisonChartType.CRASH_FREE_SESSIONS:
|
|
@@ -159,31 +174,322 @@ class ReleaseSessionsChart extends React.Component<Props> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ getSeries(chartType: ReleaseComparisonChartType) {
|
|
|
|
+ const {releaseSessions, allSessions, release, location, project, theme} = this.props;
|
|
|
|
+
|
|
|
|
+ if (!releaseSessions) {
|
|
|
|
+ return {};
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const markLines = generateReleaseMarkLines(release, project.slug, theme, location);
|
|
|
|
+
|
|
|
|
+ switch (chartType) {
|
|
|
|
+ case ReleaseComparisonChartType.CRASH_FREE_SESSIONS:
|
|
|
|
+ return {
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('This Release'),
|
|
|
|
+ connectNulls: true,
|
|
|
|
+ data: getCrashFreeRateSeries(
|
|
|
|
+ releaseSessions?.groups,
|
|
|
|
+ releaseSessions?.intervals,
|
|
|
|
+ SessionField.SESSIONS
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ previousSeries: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('All Releases'),
|
|
|
|
+ data: getCrashFreeRateSeries(
|
|
|
|
+ allSessions?.groups,
|
|
|
|
+ allSessions?.intervals,
|
|
|
|
+ SessionField.SESSIONS
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ markLines,
|
|
|
|
+ };
|
|
|
|
+ case ReleaseComparisonChartType.HEALTHY_SESSIONS:
|
|
|
|
+ return {
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('This Release'),
|
|
|
|
+ connectNulls: true,
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ releaseSessions?.groups,
|
|
|
|
+ releaseSessions?.intervals,
|
|
|
|
+ SessionField.SESSIONS,
|
|
|
|
+ SessionStatus.HEALTHY
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ previousSeries: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('All Releases'),
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ allSessions?.groups,
|
|
|
|
+ allSessions?.intervals,
|
|
|
|
+ SessionField.SESSIONS,
|
|
|
|
+ SessionStatus.HEALTHY
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ markLines,
|
|
|
|
+ };
|
|
|
|
+ case ReleaseComparisonChartType.ABNORMAL_SESSIONS:
|
|
|
|
+ return {
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('This Release'),
|
|
|
|
+ connectNulls: true,
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ releaseSessions?.groups,
|
|
|
|
+ releaseSessions?.intervals,
|
|
|
|
+ SessionField.SESSIONS,
|
|
|
|
+ SessionStatus.ABNORMAL
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ previousSeries: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('All Releases'),
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ allSessions?.groups,
|
|
|
|
+ allSessions?.intervals,
|
|
|
|
+ SessionField.SESSIONS,
|
|
|
|
+ SessionStatus.ABNORMAL
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ markLines,
|
|
|
|
+ };
|
|
|
|
+ case ReleaseComparisonChartType.ERRORED_SESSIONS:
|
|
|
|
+ return {
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('This Release'),
|
|
|
|
+ connectNulls: true,
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ releaseSessions?.groups,
|
|
|
|
+ releaseSessions?.intervals,
|
|
|
|
+ SessionField.SESSIONS,
|
|
|
|
+ SessionStatus.ERRORED
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ previousSeries: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('All Releases'),
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ allSessions?.groups,
|
|
|
|
+ allSessions?.intervals,
|
|
|
|
+ SessionField.SESSIONS,
|
|
|
|
+ SessionStatus.ERRORED
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ markLines,
|
|
|
|
+ };
|
|
|
|
+ case ReleaseComparisonChartType.CRASHED_SESSIONS:
|
|
|
|
+ return {
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('This Release'),
|
|
|
|
+ connectNulls: true,
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ releaseSessions?.groups,
|
|
|
|
+ releaseSessions?.intervals,
|
|
|
|
+ SessionField.SESSIONS,
|
|
|
|
+ SessionStatus.CRASHED
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ previousSeries: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('All Releases'),
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ allSessions?.groups,
|
|
|
|
+ allSessions?.intervals,
|
|
|
|
+ SessionField.SESSIONS,
|
|
|
|
+ SessionStatus.CRASHED
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ markLines,
|
|
|
|
+ };
|
|
|
|
+ case ReleaseComparisonChartType.CRASH_FREE_USERS:
|
|
|
|
+ return {
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('This Release'),
|
|
|
|
+ connectNulls: true,
|
|
|
|
+ data: getCrashFreeRateSeries(
|
|
|
|
+ releaseSessions?.groups,
|
|
|
|
+ releaseSessions?.intervals,
|
|
|
|
+ SessionField.USERS
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ previousSeries: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('All Releases'),
|
|
|
|
+ data: getCrashFreeRateSeries(
|
|
|
|
+ allSessions?.groups,
|
|
|
|
+ allSessions?.intervals,
|
|
|
|
+ SessionField.USERS
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ markLines,
|
|
|
|
+ };
|
|
|
|
+ case ReleaseComparisonChartType.HEALTHY_USERS:
|
|
|
|
+ return {
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('This Release'),
|
|
|
|
+ connectNulls: true,
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ releaseSessions?.groups,
|
|
|
|
+ releaseSessions?.intervals,
|
|
|
|
+ SessionField.USERS,
|
|
|
|
+ SessionStatus.HEALTHY
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ previousSeries: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('All Releases'),
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ allSessions?.groups,
|
|
|
|
+ allSessions?.intervals,
|
|
|
|
+ SessionField.USERS,
|
|
|
|
+ SessionStatus.HEALTHY
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ markLines,
|
|
|
|
+ };
|
|
|
|
+ case ReleaseComparisonChartType.ABNORMAL_USERS:
|
|
|
|
+ return {
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('This Release'),
|
|
|
|
+ connectNulls: true,
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ releaseSessions?.groups,
|
|
|
|
+ releaseSessions?.intervals,
|
|
|
|
+ SessionField.USERS,
|
|
|
|
+ SessionStatus.ABNORMAL
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ previousSeries: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('All Releases'),
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ allSessions?.groups,
|
|
|
|
+ allSessions?.intervals,
|
|
|
|
+ SessionField.USERS,
|
|
|
|
+ SessionStatus.ABNORMAL
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ markLines,
|
|
|
|
+ };
|
|
|
|
+ case ReleaseComparisonChartType.ERRORED_USERS:
|
|
|
|
+ return {
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('This Release'),
|
|
|
|
+ connectNulls: true,
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ releaseSessions?.groups,
|
|
|
|
+ releaseSessions?.intervals,
|
|
|
|
+ SessionField.USERS,
|
|
|
|
+ SessionStatus.ERRORED
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ previousSeries: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('All Releases'),
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ allSessions?.groups,
|
|
|
|
+ allSessions?.intervals,
|
|
|
|
+ SessionField.USERS,
|
|
|
|
+ SessionStatus.ERRORED
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ markLines,
|
|
|
|
+ };
|
|
|
|
+ case ReleaseComparisonChartType.CRASHED_USERS:
|
|
|
|
+ return {
|
|
|
|
+ series: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('This Release'),
|
|
|
|
+ connectNulls: true,
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ releaseSessions?.groups,
|
|
|
|
+ releaseSessions?.intervals,
|
|
|
|
+ SessionField.USERS,
|
|
|
|
+ SessionStatus.CRASHED
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ previousSeries: [
|
|
|
|
+ {
|
|
|
|
+ seriesName: t('All Releases'),
|
|
|
|
+ data: getSessionStatusRateSeries(
|
|
|
|
+ allSessions?.groups,
|
|
|
|
+ allSessions?.intervals,
|
|
|
|
+ SessionField.USERS,
|
|
|
|
+ SessionStatus.CRASHED
|
|
|
|
+ ),
|
|
|
|
+ },
|
|
|
|
+ ],
|
|
|
|
+ markLines,
|
|
|
|
+ };
|
|
|
|
+ case ReleaseComparisonChartType.SESSION_COUNT:
|
|
|
|
+ return {
|
|
|
|
+ series: Object.values(
|
|
|
|
+ fillChartDataFromSessionsResponse({
|
|
|
|
+ response: releaseSessions,
|
|
|
|
+ field: SessionField.SESSIONS,
|
|
|
|
+ groupBy: 'session.status',
|
|
|
|
+ chartData: initSessionsBreakdownChartData(),
|
|
|
|
+ })
|
|
|
|
+ ),
|
|
|
|
+ markLines,
|
|
|
|
+ };
|
|
|
|
+ case ReleaseComparisonChartType.USER_COUNT:
|
|
|
|
+ return {
|
|
|
|
+ series: Object.values(
|
|
|
|
+ fillChartDataFromSessionsResponse({
|
|
|
|
+ response: releaseSessions,
|
|
|
|
+ field: SessionField.USERS,
|
|
|
|
+ groupBy: 'session.status',
|
|
|
|
+ chartData: initSessionsBreakdownChartData(),
|
|
|
|
+ })
|
|
|
|
+ ),
|
|
|
|
+ markLines,
|
|
|
|
+ };
|
|
|
|
+ default:
|
|
|
|
+ return {};
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
render() {
|
|
render() {
|
|
- const {
|
|
|
|
- series,
|
|
|
|
- previousSeries,
|
|
|
|
- chartType,
|
|
|
|
- router,
|
|
|
|
- period,
|
|
|
|
- start,
|
|
|
|
- end,
|
|
|
|
- utc,
|
|
|
|
- value,
|
|
|
|
- diff,
|
|
|
|
- loading,
|
|
|
|
- reloading,
|
|
|
|
- } = this.props;
|
|
|
|
|
|
+ const {chartType, router, period, start, end, utc, value, diff, loading, reloading} =
|
|
|
|
+ this.props;
|
|
|
|
|
|
const Chart = this.getChart();
|
|
const Chart = this.getChart();
|
|
|
|
+ const {series, previousSeries, markLines} = this.getSeries(chartType);
|
|
|
|
|
|
const legend = {
|
|
const legend = {
|
|
right: 10,
|
|
right: 10,
|
|
top: 0,
|
|
top: 0,
|
|
- // do not show adoption markers in the legend
|
|
|
|
- data: [...series, ...previousSeries]
|
|
|
|
- .filter(s => !s.markLine)
|
|
|
|
- .map(s => s.seriesName),
|
|
|
|
|
|
+ data: [...(series ?? []), ...(previousSeries ?? [])].map(s => s.seriesName),
|
|
};
|
|
};
|
|
|
|
|
|
return (
|
|
return (
|
|
@@ -215,8 +521,8 @@ class ReleaseSessionsChart extends React.Component<Props> {
|
|
{zoomRenderProps => (
|
|
{zoomRenderProps => (
|
|
<Chart
|
|
<Chart
|
|
legend={legend}
|
|
legend={legend}
|
|
- series={series}
|
|
|
|
- previousPeriod={previousSeries}
|
|
|
|
|
|
+ series={[...(series ?? []), ...(markLines ?? [])]}
|
|
|
|
+ previousPeriod={previousSeries ?? []}
|
|
{...zoomRenderProps}
|
|
{...zoomRenderProps}
|
|
grid={{
|
|
grid={{
|
|
left: '10px',
|
|
left: '10px',
|
|
@@ -224,7 +530,7 @@ class ReleaseSessionsChart extends React.Component<Props> {
|
|
top: '70px',
|
|
top: '70px',
|
|
bottom: '0px',
|
|
bottom: '0px',
|
|
}}
|
|
}}
|
|
- yAxis={this.configureYAxis()}
|
|
|
|
|
|
+ yAxis={this.getYAxis()}
|
|
tooltip={{valueFormatter: this.formatTooltipValue}}
|
|
tooltip={{valueFormatter: this.formatTooltipValue}}
|
|
colors={this.getColors()}
|
|
colors={this.getColors()}
|
|
transformSinglePointToBar
|
|
transformSinglePointToBar
|