countChart.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. import {getInterval} from 'sentry/components/charts/utils';
  2. import {t} from 'sentry/locale';
  3. import {space} from 'sentry/styles/space';
  4. import type {Series} from 'sentry/types/echarts';
  5. import type {MultiSeriesEventsStats} from 'sentry/types/organization';
  6. import {defined} from 'sentry/utils';
  7. import {tooltipFormatterUsingAggregateOutputType} from 'sentry/utils/discover/charts';
  8. import EventView from 'sentry/utils/discover/eventView';
  9. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  10. import {decodeScalar} from 'sentry/utils/queryString';
  11. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  12. import {useLocation} from 'sentry/utils/useLocation';
  13. import usePageFilters from 'sentry/utils/usePageFilters';
  14. import {formatVersion} from 'sentry/utils/versions/formatVersion';
  15. import {
  16. PRIMARY_RELEASE_COLOR,
  17. SECONDARY_RELEASE_COLOR,
  18. } from 'sentry/views/insights/colors';
  19. import Chart, {ChartType} from 'sentry/views/insights/common/components/chart';
  20. import MiniChartPanel from 'sentry/views/insights/common/components/miniChartPanel';
  21. import {useReleaseSelection} from 'sentry/views/insights/common/queries/useReleases';
  22. import {STARFISH_CHART_INTERVAL_FIDELITY} from 'sentry/views/insights/common/utils/constants';
  23. import {appendReleaseFilters} from 'sentry/views/insights/common/utils/releaseComparison';
  24. import {useEventsStatsQuery} from 'sentry/views/insights/common/utils/useEventsStatsQuery';
  25. import {COLD_START_TYPE} from 'sentry/views/insights/mobile/appStarts/components/startTypeSelector';
  26. import useCrossPlatformProject from 'sentry/views/insights/mobile/common/queries/useCrossPlatformProject';
  27. import useTruncatedReleaseNames from 'sentry/views/insights/mobile/common/queries/useTruncatedRelease';
  28. import {OUTPUT_TYPE, YAxis} from 'sentry/views/insights/mobile/screenload/constants';
  29. import {SpanMetricsField} from 'sentry/views/insights/types';
  30. function transformData(data?: MultiSeriesEventsStats, primaryRelease?: string) {
  31. const transformedSeries: {[release: string]: Series} = {};
  32. // Check that 'meta' is not in the data object because that's a sign
  33. // that we did not get a multi-series response for comparison
  34. if (defined(data) && !('meta' in data)) {
  35. Object.keys(data).forEach(release => {
  36. transformedSeries[release] = {
  37. seriesName: release,
  38. data:
  39. data[release]?.data?.map(datum => {
  40. return {
  41. name: datum[0] * 1000,
  42. value: datum[1][0].count,
  43. };
  44. }) ?? [],
  45. ...(primaryRelease === release
  46. ? {color: PRIMARY_RELEASE_COLOR}
  47. : {
  48. color: SECONDARY_RELEASE_COLOR,
  49. lineStyle: {type: 'dashed'},
  50. }),
  51. };
  52. });
  53. }
  54. return transformedSeries;
  55. }
  56. interface Props {
  57. chartHeight?: number;
  58. }
  59. export function CountChart({chartHeight}: Props) {
  60. const location = useLocation();
  61. const pageFilter = usePageFilters();
  62. const {
  63. primaryRelease,
  64. secondaryRelease,
  65. isLoading: isReleasesLoading,
  66. } = useReleaseSelection();
  67. const {isProjectCrossPlatform, selectedPlatform} = useCrossPlatformProject();
  68. const appStartType =
  69. decodeScalar(location.query[SpanMetricsField.APP_START_TYPE]) ?? COLD_START_TYPE;
  70. const query = new MutableSearch([`span.op:app.start.${appStartType}`]);
  71. if (isProjectCrossPlatform) {
  72. query.addFilterValue('os.name', selectedPlatform);
  73. }
  74. const queryString = `${appendReleaseFilters(
  75. query,
  76. primaryRelease,
  77. secondaryRelease
  78. )} span.description:["Cold Start","Warm Start"]`;
  79. const {data: series, isPending: isSeriesLoading} = useEventsStatsQuery({
  80. eventView: EventView.fromNewQueryWithPageFilters(
  81. {
  82. name: '',
  83. topEvents: '2',
  84. fields: ['release', 'count()'],
  85. yAxis: ['count()'],
  86. query: queryString,
  87. dataset: DiscoverDatasets.SPANS_METRICS,
  88. version: 2,
  89. interval: getInterval(
  90. pageFilter.selection.datetime,
  91. STARFISH_CHART_INTERVAL_FIDELITY
  92. ),
  93. },
  94. pageFilter.selection
  95. ),
  96. enabled: !isReleasesLoading,
  97. referrer: 'api.starfish.mobile-startup-series',
  98. initialData: {},
  99. });
  100. const transformedSeries = Object.values(transformData(series, primaryRelease)).sort(
  101. (releaseA, _releaseB) => {
  102. return releaseA.seriesName === primaryRelease ? -1 : 1;
  103. }
  104. );
  105. const {truncatedPrimaryRelease, truncatedSecondaryRelease} = useTruncatedReleaseNames();
  106. const chartTitle =
  107. appStartType === COLD_START_TYPE ? t('Cold Start Count') : t('Warm Start Count');
  108. return (
  109. <MiniChartPanel
  110. title={chartTitle}
  111. subtitle={
  112. primaryRelease
  113. ? t(
  114. '%s v. %s',
  115. truncatedPrimaryRelease,
  116. secondaryRelease ? truncatedSecondaryRelease : ''
  117. )
  118. : ''
  119. }
  120. >
  121. <Chart
  122. data={transformedSeries}
  123. height={chartHeight}
  124. loading={isSeriesLoading || isReleasesLoading}
  125. grid={{
  126. left: '0',
  127. right: '0',
  128. top: space(2),
  129. bottom: '0',
  130. }}
  131. showLegend
  132. definedAxisTicks={2}
  133. type={ChartType.LINE}
  134. aggregateOutputFormat={OUTPUT_TYPE[YAxis.COUNT]}
  135. tooltipFormatterOptions={{
  136. valueFormatter: value =>
  137. tooltipFormatterUsingAggregateOutputType(value, OUTPUT_TYPE[YAxis.COUNT]),
  138. nameFormatter: value => formatVersion(value),
  139. }}
  140. legendFormatter={value => formatVersion(value)}
  141. />
  142. </MiniChartPanel>
  143. );
  144. }