transactions.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import {doEventsRequest} from 'sentry/actionCreators/events';
  2. import type {Client} from 'sentry/api';
  3. import type {PageFilters} from 'sentry/types/core';
  4. import type {Series} from 'sentry/types/echarts';
  5. import type {TagCollection} from 'sentry/types/group';
  6. import type {
  7. EventsStats,
  8. MultiSeriesEventsStats,
  9. Organization,
  10. } from 'sentry/types/organization';
  11. import {defined} from 'sentry/utils';
  12. import type {CustomMeasurementCollection} from 'sentry/utils/customMeasurements/customMeasurements';
  13. import type {EventsTableData, TableData} from 'sentry/utils/discover/discoverQuery';
  14. import {
  15. type QueryFieldValue,
  16. SPAN_OP_BREAKDOWN_FIELDS,
  17. TRANSACTION_FIELDS,
  18. } from 'sentry/utils/discover/fields';
  19. import type {
  20. DiscoverQueryExtras,
  21. DiscoverQueryRequestParams,
  22. } from 'sentry/utils/discover/genericDiscoverQuery';
  23. import {doDiscoverQuery} from 'sentry/utils/discover/genericDiscoverQuery';
  24. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  25. import {getMeasurements} from 'sentry/utils/measurements/measurements';
  26. import {MEPState} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
  27. import {
  28. type OnDemandControlContext,
  29. shouldUseOnDemandMetrics,
  30. } from 'sentry/utils/performance/contexts/onDemandControl';
  31. import {getSeriesRequestData} from 'sentry/views/dashboards/datasetConfig/utils/getSeriesRequestData';
  32. import {FieldValueKind} from 'sentry/views/discover/table/types';
  33. import {generateFieldOptions} from 'sentry/views/discover/utils';
  34. import type {Widget, WidgetQuery} from '../types';
  35. import {DisplayType} from '../types';
  36. import {eventViewFromWidget} from '../utils';
  37. import {EventsSearchBar} from '../widgetBuilder/buildSteps/filterResultsStep/eventsSearchBar';
  38. import {type DatasetConfig, handleOrderByReset} from './base';
  39. import {
  40. doOnDemandMetricsRequest,
  41. filterAggregateParams,
  42. filterSeriesSortOptions,
  43. filterYAxisAggregateParams,
  44. filterYAxisOptions,
  45. getCustomEventsFieldRenderer,
  46. getTableSortOptions,
  47. getTimeseriesSortOptions,
  48. transformEventsResponseToSeries,
  49. transformEventsResponseToTable,
  50. } from './errorsAndTransactions';
  51. const DEFAULT_WIDGET_QUERY: WidgetQuery = {
  52. name: '',
  53. fields: ['count()'],
  54. columns: [],
  55. fieldAliases: [],
  56. aggregates: ['count()'],
  57. conditions: '',
  58. orderby: '-count()',
  59. };
  60. const DEFAULT_FIELD: QueryFieldValue = {
  61. function: ['count', '', undefined, undefined],
  62. kind: FieldValueKind.FUNCTION,
  63. };
  64. export type SeriesWithOrdering = [order: number, series: Series];
  65. export const TransactionsConfig: DatasetConfig<
  66. EventsStats | MultiSeriesEventsStats,
  67. TableData | EventsTableData
  68. > = {
  69. defaultField: DEFAULT_FIELD,
  70. defaultWidgetQuery: DEFAULT_WIDGET_QUERY,
  71. enableEquations: true,
  72. getCustomFieldRenderer: getCustomEventsFieldRenderer,
  73. SearchBar: EventsSearchBar,
  74. filterSeriesSortOptions,
  75. filterYAxisAggregateParams,
  76. filterYAxisOptions,
  77. getTableFieldOptions: getEventsTableFieldOptions,
  78. getTimeseriesSortOptions,
  79. getTableSortOptions,
  80. getGroupByFieldOptions: getEventsTableFieldOptions,
  81. handleOrderByReset,
  82. supportedDisplayTypes: [
  83. DisplayType.AREA,
  84. DisplayType.BAR,
  85. DisplayType.BIG_NUMBER,
  86. DisplayType.LINE,
  87. DisplayType.TABLE,
  88. DisplayType.TOP_N,
  89. ],
  90. getTableRequest: (
  91. api: Client,
  92. widget: Widget,
  93. query: WidgetQuery,
  94. organization: Organization,
  95. pageFilters: PageFilters,
  96. onDemandControlContext?: OnDemandControlContext,
  97. limit?: number,
  98. cursor?: string,
  99. referrer?: string,
  100. mepSetting?: MEPState | null
  101. ) => {
  102. const useOnDemandMetrics = shouldUseOnDemandMetrics(
  103. organization,
  104. widget,
  105. onDemandControlContext
  106. );
  107. const queryExtras = {
  108. useOnDemandMetrics,
  109. onDemandType: 'dynamic_query',
  110. };
  111. return getEventsRequest(
  112. api,
  113. query,
  114. organization,
  115. pageFilters,
  116. limit,
  117. cursor,
  118. referrer,
  119. mepSetting,
  120. queryExtras
  121. );
  122. },
  123. getSeriesRequest: getEventsSeriesRequest,
  124. transformSeries: transformEventsResponseToSeries,
  125. transformTable: transformEventsResponseToTable,
  126. filterAggregateParams,
  127. };
  128. function getEventsTableFieldOptions(
  129. organization: Organization,
  130. tags?: TagCollection,
  131. customMeasurements?: CustomMeasurementCollection
  132. ) {
  133. const measurements = getMeasurements();
  134. return generateFieldOptions({
  135. organization,
  136. tagKeys: Object.values(tags ?? {}).map(({key}) => key),
  137. measurementKeys: Object.values(measurements).map(({key}) => key),
  138. spanOperationBreakdownKeys: SPAN_OP_BREAKDOWN_FIELDS,
  139. customMeasurements: Object.values(customMeasurements ?? {}).map(
  140. ({key, functions}) => ({
  141. key,
  142. functions,
  143. })
  144. ),
  145. fieldKeys: TRANSACTION_FIELDS,
  146. });
  147. }
  148. function getEventsRequest(
  149. api: Client,
  150. query: WidgetQuery,
  151. organization: Organization,
  152. pageFilters: PageFilters,
  153. limit?: number,
  154. cursor?: string,
  155. referrer?: string,
  156. mepSetting?: MEPState | null,
  157. queryExtras?: DiscoverQueryExtras
  158. ) {
  159. const isMEPEnabled = defined(mepSetting) && mepSetting !== MEPState.TRANSACTIONS_ONLY;
  160. const url = `/organizations/${organization.slug}/events/`;
  161. const eventView = eventViewFromWidget('', query, pageFilters);
  162. const params: DiscoverQueryRequestParams = {
  163. per_page: limit,
  164. cursor,
  165. referrer,
  166. dataset: isMEPEnabled
  167. ? DiscoverDatasets.METRICS_ENHANCED
  168. : DiscoverDatasets.TRANSACTIONS,
  169. ...queryExtras,
  170. };
  171. if (query.orderby) {
  172. params.sort = typeof query.orderby === 'string' ? [query.orderby] : query.orderby;
  173. }
  174. return doDiscoverQuery<EventsTableData>(
  175. api,
  176. url,
  177. {
  178. ...eventView.generateQueryStringObject(),
  179. ...params,
  180. },
  181. // Tries events request up to 3 times on rate limit
  182. {
  183. retry: {
  184. statusCodes: [429],
  185. tries: 3,
  186. },
  187. }
  188. );
  189. }
  190. function getEventsSeriesRequest(
  191. api: Client,
  192. widget: Widget,
  193. queryIndex: number,
  194. organization: Organization,
  195. pageFilters: PageFilters,
  196. onDemandControlContext?: OnDemandControlContext,
  197. referrer?: string,
  198. mepSetting?: MEPState | null
  199. ) {
  200. const isMEPEnabled = defined(mepSetting) && mepSetting !== MEPState.TRANSACTIONS_ONLY;
  201. const requestData = getSeriesRequestData(
  202. widget,
  203. queryIndex,
  204. organization,
  205. pageFilters,
  206. isMEPEnabled ? DiscoverDatasets.METRICS_ENHANCED : DiscoverDatasets.TRANSACTIONS,
  207. referrer
  208. );
  209. if (shouldUseOnDemandMetrics(organization, widget, onDemandControlContext)) {
  210. requestData.queryExtras = {
  211. ...requestData.queryExtras,
  212. ...{dataset: DiscoverDatasets.METRICS_ENHANCED},
  213. };
  214. return doOnDemandMetricsRequest(api, requestData, widget.widgetType);
  215. }
  216. return doEventsRequest<true>(api, requestData);
  217. }