utils.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. import type {PlainRoute} from 'react-router';
  2. import styled from '@emotion/styled';
  3. import type {Location, LocationDescriptor, Query} from 'history';
  4. import {space} from 'sentry/styles/space';
  5. import type {Organization} from 'sentry/types';
  6. import type {TableDataRow} from 'sentry/utils/discover/discoverQuery';
  7. import EventView from 'sentry/utils/discover/eventView';
  8. import {
  9. generateEventSlug,
  10. generateLinkToEventInTraceView,
  11. } from 'sentry/utils/discover/urls';
  12. import getRouteStringFromRoutes from 'sentry/utils/getRouteStringFromRoutes';
  13. import {generateProfileFlamechartRoute} from 'sentry/utils/profiling/routes';
  14. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  15. import {normalizeUrl} from 'sentry/utils/withDomainRequired';
  16. import {getTraceDetailsUrl} from 'sentry/views/performance/traceDetails/utils';
  17. export enum DisplayModes {
  18. DURATION_PERCENTILE = 'durationpercentile',
  19. DURATION = 'duration',
  20. LATENCY = 'latency',
  21. TREND = 'trend',
  22. VITALS = 'vitals',
  23. USER_MISERY = 'usermisery',
  24. }
  25. export enum TransactionFilterOptions {
  26. FASTEST = 'fastest',
  27. SLOW = 'slow',
  28. OUTLIER = 'outlier',
  29. RECENT = 'recent',
  30. }
  31. export function generateTransactionSummaryRoute({
  32. orgSlug,
  33. subPath,
  34. }: {
  35. orgSlug: string;
  36. subPath?: string;
  37. }): string {
  38. return `/organizations/${orgSlug}/performance/summary/${subPath ? `${subPath}/` : ''}`;
  39. }
  40. // normalizes search conditions by removing any redundant search conditions before presenting them in:
  41. // - query strings
  42. // - search UI
  43. export function normalizeSearchConditions(query: string): MutableSearch {
  44. const filterParams = normalizeSearchConditionsWithTransactionName(query);
  45. // no need to include transaction as its already in the query params
  46. filterParams.removeFilter('transaction');
  47. return filterParams;
  48. }
  49. // normalizes search conditions by removing any redundant search conditions, but retains any transaction name
  50. export function normalizeSearchConditionsWithTransactionName(
  51. query: string
  52. ): MutableSearch {
  53. const filterParams = new MutableSearch(query);
  54. // remove any event.type queries since it is implied to apply to only transactions
  55. filterParams.removeFilter('event.type');
  56. return filterParams;
  57. }
  58. export function transactionSummaryRouteWithQuery({
  59. orgSlug,
  60. transaction,
  61. projectID,
  62. query,
  63. unselectedSeries = ['p100()', 'avg()'],
  64. display,
  65. trendFunction,
  66. trendColumn,
  67. showTransactions,
  68. additionalQuery,
  69. subPath,
  70. }: {
  71. orgSlug: string;
  72. query: Query;
  73. transaction: string;
  74. additionalQuery?: Record<string, string | undefined>;
  75. display?: DisplayModes;
  76. projectID?: string | string[];
  77. showTransactions?: TransactionFilterOptions;
  78. subPath?: string;
  79. trendColumn?: string;
  80. trendFunction?: string;
  81. unselectedSeries?: string | string[];
  82. }) {
  83. const pathname = generateTransactionSummaryRoute({
  84. orgSlug,
  85. subPath,
  86. });
  87. let searchFilter: typeof query.query;
  88. if (typeof query.query === 'string') {
  89. searchFilter = normalizeSearchConditions(query.query).formatString();
  90. } else {
  91. searchFilter = query.query;
  92. }
  93. return {
  94. pathname,
  95. query: {
  96. transaction,
  97. project: projectID,
  98. environment: query.environment,
  99. statsPeriod: query.statsPeriod,
  100. start: query.start,
  101. end: query.end,
  102. query: searchFilter,
  103. unselectedSeries,
  104. showTransactions,
  105. display,
  106. trendFunction,
  107. trendColumn,
  108. referrer: 'performance-transaction-summary',
  109. ...additionalQuery,
  110. },
  111. };
  112. }
  113. export function generateTraceLink(dateSelection) {
  114. return (
  115. organization: Organization,
  116. tableRow: TableDataRow,
  117. _location: Location
  118. ): LocationDescriptor => {
  119. const traceId = `${tableRow.trace}`;
  120. if (!traceId) {
  121. return {};
  122. }
  123. return getTraceDetailsUrl(
  124. organization,
  125. traceId,
  126. dateSelection,
  127. {},
  128. tableRow.timestamp
  129. );
  130. };
  131. }
  132. export function generateTransactionIdLink(transactionName?: string) {
  133. return (
  134. organization: Organization,
  135. tableRow: TableDataRow,
  136. location: Location,
  137. spanId?: string
  138. ): LocationDescriptor => {
  139. return generateLinkToEventInTraceView({
  140. eventSlug: generateEventSlug(tableRow),
  141. dataRow: tableRow,
  142. eventView: EventView.fromLocation(location),
  143. location,
  144. organization,
  145. spanId,
  146. transactionName,
  147. });
  148. };
  149. }
  150. export function generateProfileLink() {
  151. return (
  152. organization: Organization,
  153. tableRow: TableDataRow,
  154. _location: Location | undefined
  155. ) => {
  156. const profileId = tableRow['profile.id'];
  157. if (!profileId) {
  158. return {};
  159. }
  160. return generateProfileFlamechartRoute({
  161. orgSlug: organization.slug,
  162. projectSlug: String(tableRow['project.name']),
  163. profileId: String(profileId),
  164. });
  165. };
  166. }
  167. export function generateReplayLink(routes: PlainRoute<any>[]) {
  168. const referrer = getRouteStringFromRoutes(routes);
  169. return (
  170. organization: Organization,
  171. tableRow: TableDataRow,
  172. _location: Location | undefined
  173. ): LocationDescriptor => {
  174. const replayId = tableRow.replayId;
  175. if (!replayId) {
  176. return {};
  177. }
  178. if (!tableRow.timestamp) {
  179. return {
  180. pathname: normalizeUrl(
  181. `/organizations/${organization.slug}/replays/${replayId}/`
  182. ),
  183. query: {
  184. referrer,
  185. },
  186. };
  187. }
  188. const transactionTimestamp = new Date(tableRow.timestamp).getTime();
  189. const transactionStartTimestamp = tableRow['transaction.duration']
  190. ? transactionTimestamp - (tableRow['transaction.duration'] as number)
  191. : undefined;
  192. return {
  193. pathname: normalizeUrl(`/organizations/${organization.slug}/replays/${replayId}/`),
  194. query: {
  195. event_t: transactionStartTimestamp,
  196. referrer,
  197. },
  198. };
  199. };
  200. }
  201. export const SidebarSpacer = styled('div')`
  202. margin-top: ${space(3)};
  203. `;