utils.tsx 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import type {Location, Query} from 'history';
  2. import {t} from 'sentry/locale';
  3. import type {TableDataRow} from 'sentry/utils/discover/discoverQuery';
  4. import type EventView from 'sentry/utils/discover/eventView';
  5. import type {QueryFieldValue} from 'sentry/utils/discover/fields';
  6. import {decodeScalar} from 'sentry/utils/queryString';
  7. import type {SpanOperationBreakdownFilter} from '../filter';
  8. import {filterToField} from '../filter';
  9. import {TransactionFilterOptions} from '../utils';
  10. export enum EventsDisplayFilterName {
  11. P50 = 'p50',
  12. P75 = 'p75',
  13. P95 = 'p95',
  14. P99 = 'p99',
  15. P100 = 'p100',
  16. }
  17. export type PercentileValues = Record<EventsDisplayFilterName, number>;
  18. export type EventsDisplayFilter = {
  19. label: string;
  20. name: EventsDisplayFilterName;
  21. query?: string[][];
  22. sort?: {field: string; kind: 'desc' | 'asc'};
  23. };
  24. export type EventsFilterOptions = {
  25. [name in EventsDisplayFilterName]: EventsDisplayFilter;
  26. };
  27. export type EventsFilterPercentileValues = {
  28. [name in Exclude<EventsDisplayFilterName, EventsDisplayFilterName.P100>]: number;
  29. };
  30. export function getEventsFilterOptions(
  31. spanOperationBreakdownFilter: SpanOperationBreakdownFilter,
  32. percentileValues?: EventsFilterPercentileValues
  33. ): EventsFilterOptions {
  34. const {p99, p95, p75, p50} = percentileValues
  35. ? percentileValues
  36. : {p99: 0, p95: 0, p75: 0, p50: 0};
  37. return {
  38. [EventsDisplayFilterName.P50]: {
  39. name: EventsDisplayFilterName.P50,
  40. query: p50 ? [['transaction.duration', `<=${p50.toFixed(0)}`]] : undefined,
  41. sort: {
  42. kind: 'desc',
  43. field: filterToField(spanOperationBreakdownFilter) || 'transaction.duration',
  44. },
  45. label: t('p50'),
  46. },
  47. [EventsDisplayFilterName.P75]: {
  48. name: EventsDisplayFilterName.P75,
  49. query: p75 ? [['transaction.duration', `<=${p75.toFixed(0)}`]] : undefined,
  50. sort: {
  51. kind: 'desc',
  52. field: filterToField(spanOperationBreakdownFilter) || 'transaction.duration',
  53. },
  54. label: t('p75'),
  55. },
  56. [EventsDisplayFilterName.P95]: {
  57. name: EventsDisplayFilterName.P95,
  58. query: p95 ? [['transaction.duration', `<=${p95.toFixed(0)}`]] : undefined,
  59. sort: {
  60. kind: 'desc',
  61. field: filterToField(spanOperationBreakdownFilter) || 'transaction.duration',
  62. },
  63. label: t('p95'),
  64. },
  65. [EventsDisplayFilterName.P99]: {
  66. name: EventsDisplayFilterName.P99,
  67. query: p99 ? [['transaction.duration', `<=${p99.toFixed(0)}`]] : undefined,
  68. sort: {
  69. kind: 'desc',
  70. field: filterToField(spanOperationBreakdownFilter) || 'transaction.duration',
  71. },
  72. label: t('p99'),
  73. },
  74. [EventsDisplayFilterName.P100]: {
  75. name: EventsDisplayFilterName.P100,
  76. label: t('p100'),
  77. },
  78. };
  79. }
  80. export function eventsRouteWithQuery({
  81. orgSlug,
  82. transaction,
  83. projectID,
  84. query,
  85. }: {
  86. orgSlug: string;
  87. query: Query;
  88. transaction: string;
  89. projectID?: string | string[];
  90. }) {
  91. const pathname = `/organizations/${orgSlug}/performance/summary/events/`;
  92. return {
  93. pathname,
  94. query: {
  95. transaction,
  96. project: projectID,
  97. environment: query.environment,
  98. statsPeriod: query.statsPeriod,
  99. start: query.start,
  100. end: query.end,
  101. query: query.query,
  102. },
  103. };
  104. }
  105. function stringToFilter(option: string) {
  106. if (
  107. Object.values(EventsDisplayFilterName).includes(option as EventsDisplayFilterName)
  108. ) {
  109. return option as EventsDisplayFilterName;
  110. }
  111. return EventsDisplayFilterName.P100;
  112. }
  113. export function decodeEventsDisplayFilterFromLocation(location: Location) {
  114. return stringToFilter(
  115. decodeScalar(location.query.showTransactions, EventsDisplayFilterName.P100)
  116. );
  117. }
  118. export function filterEventsDisplayToLocationQuery(
  119. option: EventsDisplayFilterName,
  120. spanOperationBreakdownFilter: SpanOperationBreakdownFilter
  121. ) {
  122. const eventsFilterOptions = getEventsFilterOptions(spanOperationBreakdownFilter);
  123. const kind = eventsFilterOptions[option].sort?.kind;
  124. const field = eventsFilterOptions[option].sort?.field;
  125. const query: {showTransactions: string; sort?: string} = {
  126. showTransactions: option,
  127. };
  128. if (kind && field) {
  129. query.sort = `${kind === 'desc' ? '-' : ''}${field}`;
  130. }
  131. return query;
  132. }
  133. export function mapShowTransactionToPercentile(
  134. showTransaction
  135. ): EventsDisplayFilterName | undefined {
  136. switch (showTransaction) {
  137. case TransactionFilterOptions.OUTLIER:
  138. return EventsDisplayFilterName.P100;
  139. case TransactionFilterOptions.SLOW:
  140. return EventsDisplayFilterName.P95;
  141. default:
  142. return undefined;
  143. }
  144. }
  145. export function mapPercentileValues(percentileData?: TableDataRow | null) {
  146. return {
  147. p100: percentileData?.['p100()'],
  148. p99: percentileData?.['p99()'],
  149. p95: percentileData?.['p95()'],
  150. p75: percentileData?.['p75()'],
  151. p50: percentileData?.['p50()'],
  152. } as PercentileValues;
  153. }
  154. export function getPercentilesEventView(eventView: EventView): EventView {
  155. const percentileColumns: QueryFieldValue[] = [
  156. {
  157. kind: 'function',
  158. function: ['p100', '', undefined, undefined],
  159. },
  160. {
  161. kind: 'function',
  162. function: ['p99', '', undefined, undefined],
  163. },
  164. {
  165. kind: 'function',
  166. function: ['p95', '', undefined, undefined],
  167. },
  168. {
  169. kind: 'function',
  170. function: ['p75', '', undefined, undefined],
  171. },
  172. {
  173. kind: 'function',
  174. function: ['p50', '', undefined, undefined],
  175. },
  176. ];
  177. return eventView.withColumns(percentileColumns);
  178. }