utils.tsx 5.3 KB

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