utils.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. import {ReactText} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import {Location} from 'history';
  4. import {t} from 'app/locale';
  5. import {Organization, Project} from 'app/types';
  6. import EventView from 'app/utils/discover/eventView';
  7. import {
  8. formatAbbreviatedNumber,
  9. formatFloat,
  10. formatPercentage,
  11. getDuration,
  12. } from 'app/utils/formatters';
  13. import {HistogramData} from 'app/utils/performance/histogram/types';
  14. import {decodeScalar} from 'app/utils/queryString';
  15. import {MutableSearch} from 'app/utils/tokenizeSearch';
  16. import {AxisOption, getTermHelp, PERFORMANCE_TERM} from '../data';
  17. import {Rectangle} from '../transactionSummary/transactionVitals/types';
  18. import {platformToPerformanceType, PROJECT_PERFORMANCE_TYPE} from '../utils';
  19. export const LEFT_AXIS_QUERY_KEY = 'left';
  20. export const RIGHT_AXIS_QUERY_KEY = 'right';
  21. type LandingDisplay = {
  22. label: string;
  23. field: LandingDisplayField;
  24. };
  25. export enum LandingDisplayField {
  26. ALL = 'all',
  27. FRONTEND_PAGELOAD = 'frontend_pageload',
  28. FRONTEND_OTHER = 'frontend_other',
  29. BACKEND = 'backend',
  30. MOBILE = 'mobile',
  31. }
  32. export const LANDING_DISPLAYS = [
  33. {
  34. label: 'All Transactions',
  35. field: LandingDisplayField.ALL,
  36. },
  37. {
  38. label: 'Frontend (Pageload)',
  39. field: LandingDisplayField.FRONTEND_PAGELOAD,
  40. },
  41. {
  42. label: 'Frontend (Other)',
  43. field: LandingDisplayField.FRONTEND_OTHER,
  44. },
  45. {
  46. label: 'Backend',
  47. field: LandingDisplayField.BACKEND,
  48. },
  49. {
  50. label: 'Mobile',
  51. field: LandingDisplayField.MOBILE,
  52. isShown: (organization: Organization) =>
  53. organization.features.includes('performance-mobile-vitals'),
  54. badge: 'new' as const,
  55. },
  56. ];
  57. export function excludeTransaction(
  58. transaction: string | ReactText,
  59. props: {eventView: EventView; location: Location}
  60. ) {
  61. const {eventView, location} = props;
  62. const searchConditions = new MutableSearch(eventView.query);
  63. searchConditions.addFilterValues('!transaction', [`${transaction}`]);
  64. browserHistory.push({
  65. pathname: location.pathname,
  66. query: {
  67. ...location.query,
  68. cursor: undefined,
  69. query: searchConditions.formatString(),
  70. },
  71. });
  72. }
  73. export function getCurrentLandingDisplay(
  74. location: Location,
  75. projects: Project[],
  76. eventView?: EventView
  77. ): LandingDisplay {
  78. const landingField = decodeScalar(location?.query?.landingDisplay);
  79. const display = LANDING_DISPLAYS.find(({field}) => field === landingField);
  80. if (display) {
  81. return display;
  82. }
  83. const defaultDisplayField = getDefaultDisplayFieldForPlatform(projects, eventView);
  84. const defaultDisplay = LANDING_DISPLAYS.find(
  85. ({field}) => field === defaultDisplayField
  86. );
  87. return defaultDisplay || LANDING_DISPLAYS[0];
  88. }
  89. export function handleLandingDisplayChange(field: string, location: Location) {
  90. const newQuery = {...location.query};
  91. delete newQuery[LEFT_AXIS_QUERY_KEY];
  92. delete newQuery[RIGHT_AXIS_QUERY_KEY];
  93. // Transaction op can affect the display and show no results if it is explicitly set.
  94. const query = decodeScalar(location.query.query, '');
  95. const searchConditions = new MutableSearch(query);
  96. searchConditions.removeFilter('transaction.op');
  97. browserHistory.push({
  98. pathname: location.pathname,
  99. query: {
  100. ...newQuery,
  101. query: searchConditions.formatString(),
  102. landingDisplay: field,
  103. },
  104. });
  105. }
  106. export function getChartWidth(chartData: HistogramData, refPixelRect: Rectangle | null) {
  107. const distance = refPixelRect ? refPixelRect.point2.x - refPixelRect.point1.x : 0;
  108. const chartWidth = chartData.length * distance;
  109. return {
  110. chartWidth,
  111. };
  112. }
  113. export function getDefaultDisplayFieldForPlatform(
  114. projects: Project[],
  115. eventView?: EventView
  116. ) {
  117. if (!eventView) {
  118. return LandingDisplayField.ALL;
  119. }
  120. const projectIds = eventView.project;
  121. const performanceTypeToDisplay = {
  122. [PROJECT_PERFORMANCE_TYPE.ANY]: LandingDisplayField.ALL,
  123. [PROJECT_PERFORMANCE_TYPE.FRONTEND]: LandingDisplayField.FRONTEND_PAGELOAD,
  124. [PROJECT_PERFORMANCE_TYPE.BACKEND]: LandingDisplayField.BACKEND,
  125. [PROJECT_PERFORMANCE_TYPE.MOBILE]: LandingDisplayField.MOBILE,
  126. };
  127. const performanceType = platformToPerformanceType(projects, projectIds);
  128. const landingField =
  129. performanceTypeToDisplay[performanceType] ?? LandingDisplayField.ALL;
  130. return landingField;
  131. }
  132. type VitalCardDetail = {
  133. title: string;
  134. tooltip: string;
  135. formatter: (value: number) => string | number;
  136. };
  137. export const vitalCardDetails = (
  138. organization: Organization
  139. ): {[key: string]: VitalCardDetail | undefined} => {
  140. return {
  141. 'p75(transaction.duration)': {
  142. title: t('Duration (p75)'),
  143. tooltip: getTermHelp(organization, PERFORMANCE_TERM.P75),
  144. formatter: value => getDuration(value / 1000, value >= 1000 ? 3 : 0, true),
  145. },
  146. 'tpm()': {
  147. title: t('Throughput'),
  148. tooltip: getTermHelp(organization, PERFORMANCE_TERM.THROUGHPUT),
  149. formatter: formatAbbreviatedNumber,
  150. },
  151. 'failure_rate()': {
  152. title: t('Failure Rate'),
  153. tooltip: getTermHelp(organization, PERFORMANCE_TERM.FAILURE_RATE),
  154. formatter: value => formatPercentage(value, 2),
  155. },
  156. 'apdex()': {
  157. title: t('Apdex'),
  158. tooltip: getTermHelp(organization, PERFORMANCE_TERM.APDEX_NEW),
  159. formatter: value => formatFloat(value, 4),
  160. },
  161. 'p75(measurements.frames_slow_rate)': {
  162. title: t('Slow Frames (p75)'),
  163. tooltip: getTermHelp(organization, PERFORMANCE_TERM.SLOW_FRAMES),
  164. formatter: value => formatPercentage(value, 2),
  165. },
  166. 'p75(measurements.frames_frozen_rate)': {
  167. title: t('Frozen Frames (p75)'),
  168. tooltip: getTermHelp(organization, PERFORMANCE_TERM.FROZEN_FRAMES),
  169. formatter: value => formatPercentage(value, 2),
  170. },
  171. 'p75(measurements.app_start_cold)': {
  172. title: t('Cold Start (p75)'),
  173. tooltip: getTermHelp(organization, PERFORMANCE_TERM.APP_START_COLD),
  174. formatter: value => getDuration(value / 1000, value >= 1000 ? 3 : 0, true),
  175. },
  176. 'p75(measurements.app_start_warm)': {
  177. title: t('Warm Start (p75)'),
  178. tooltip: getTermHelp(organization, PERFORMANCE_TERM.APP_START_WARM),
  179. formatter: value => getDuration(value / 1000, value >= 1000 ? 3 : 0, true),
  180. },
  181. 'p75(measurements.stall_percentage)': {
  182. title: t('Stall Percentage (p75)'),
  183. tooltip: getTermHelp(organization, PERFORMANCE_TERM.STALL_PERCENTAGE),
  184. formatter: value => formatPercentage(value, 2),
  185. },
  186. };
  187. };
  188. export function getDisplayAxes(options: AxisOption[], location: Location) {
  189. const leftDefault = options.find(opt => opt.isLeftDefault) || options[0];
  190. const rightDefault = options.find(opt => opt.isRightDefault) || options[1];
  191. const leftAxis =
  192. options.find(opt => opt.value === location.query[LEFT_AXIS_QUERY_KEY]) || leftDefault;
  193. const rightAxis =
  194. options.find(opt => opt.value === location.query[RIGHT_AXIS_QUERY_KEY]) ||
  195. rightDefault;
  196. return {
  197. leftAxis,
  198. rightAxis,
  199. };
  200. }