errors.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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 type {CustomMeasurementCollection} from 'sentry/utils/customMeasurements/customMeasurements';
  12. import type {EventsTableData, TableData} from 'sentry/utils/discover/discoverQuery';
  13. import type {MetaType} from 'sentry/utils/discover/eventView';
  14. import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
  15. import {
  16. ERROR_FIELDS,
  17. ERRORS_AGGREGATION_FUNCTIONS,
  18. getAggregations,
  19. } from 'sentry/utils/discover/fields';
  20. import type {DiscoverQueryRequestParams} from 'sentry/utils/discover/genericDiscoverQuery';
  21. import {doDiscoverQuery} from 'sentry/utils/discover/genericDiscoverQuery';
  22. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  23. import type {AggregationKey} from 'sentry/utils/fields';
  24. import type {MEPState} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
  25. import type {OnDemandControlContext} from 'sentry/utils/performance/contexts/onDemandControl';
  26. import {getSeriesRequestData} from 'sentry/views/dashboards/datasetConfig/utils/getSeriesRequestData';
  27. import type {FieldValueOption} from 'sentry/views/discover/table/queryField';
  28. import {generateFieldOptions} from 'sentry/views/discover/utils';
  29. import type {Widget, WidgetQuery} from '../types';
  30. import {DisplayType} from '../types';
  31. import {eventViewFromWidget} from '../utils';
  32. import {EventsSearchBar} from '../widgetBuilder/buildSteps/filterResultsStep/eventsSearchBar';
  33. import {type DatasetConfig, handleOrderByReset} from './base';
  34. import {
  35. filterAggregateParams,
  36. filterSeriesSortOptions,
  37. filterYAxisAggregateParams, // TODO: Does this need to be overridden?
  38. getTableSortOptions,
  39. getTimeseriesSortOptions,
  40. renderEventIdAsLinkable,
  41. renderTraceAsLinkable,
  42. transformEventsResponseToSeries,
  43. transformEventsResponseToTable,
  44. } from './errorsAndTransactions';
  45. const DEFAULT_WIDGET_QUERY: WidgetQuery = {
  46. name: '',
  47. fields: ['count()'],
  48. columns: [],
  49. fieldAliases: [],
  50. aggregates: ['count()'],
  51. conditions: '',
  52. orderby: '-count()',
  53. };
  54. export type SeriesWithOrdering = [order: number, series: Series];
  55. export const ErrorsConfig: DatasetConfig<
  56. EventsStats | MultiSeriesEventsStats,
  57. TableData | EventsTableData
  58. > = {
  59. defaultWidgetQuery: DEFAULT_WIDGET_QUERY,
  60. enableEquations: true,
  61. getCustomFieldRenderer: getCustomEventsFieldRenderer,
  62. SearchBar: EventsSearchBar,
  63. filterSeriesSortOptions,
  64. filterYAxisAggregateParams,
  65. filterYAxisOptions,
  66. getTableFieldOptions: getEventsTableFieldOptions,
  67. getTimeseriesSortOptions: (organization, widgetQuery, tags) =>
  68. getTimeseriesSortOptions(organization, widgetQuery, tags, getEventsTableFieldOptions),
  69. getTableSortOptions,
  70. getGroupByFieldOptions: getEventsTableFieldOptions,
  71. handleOrderByReset,
  72. supportedDisplayTypes: [
  73. DisplayType.AREA,
  74. DisplayType.BAR,
  75. DisplayType.BIG_NUMBER,
  76. DisplayType.LINE,
  77. DisplayType.TABLE,
  78. DisplayType.TOP_N,
  79. ],
  80. getTableRequest: (
  81. api: Client,
  82. _widget: Widget,
  83. query: WidgetQuery,
  84. organization: Organization,
  85. pageFilters: PageFilters,
  86. _onDemandControlContext?: OnDemandControlContext,
  87. limit?: number,
  88. cursor?: string,
  89. referrer?: string,
  90. _mepSetting?: MEPState | null
  91. ) => {
  92. return getEventsRequest(
  93. api,
  94. query,
  95. organization,
  96. pageFilters,
  97. limit,
  98. cursor,
  99. referrer
  100. );
  101. },
  102. getSeriesRequest: getErrorsSeriesRequest,
  103. transformTable: transformEventsResponseToTable,
  104. transformSeries: transformEventsResponseToSeries,
  105. filterAggregateParams,
  106. };
  107. function getEventsTableFieldOptions(
  108. organization: Organization,
  109. tags?: TagCollection,
  110. _customMeasurements?: CustomMeasurementCollection
  111. ) {
  112. const aggregates = getAggregations(DiscoverDatasets.ERRORS);
  113. return generateFieldOptions({
  114. organization,
  115. tagKeys: Object.values(tags ?? {}).map(({key}) => key),
  116. aggregations: Object.keys(aggregates)
  117. .filter(key => ERRORS_AGGREGATION_FUNCTIONS.includes(key as AggregationKey))
  118. .reduce((obj, key) => {
  119. obj[key] = aggregates[key];
  120. return obj;
  121. }, {}),
  122. fieldKeys: ERROR_FIELDS,
  123. });
  124. }
  125. export function getCustomEventsFieldRenderer(field: string, meta: MetaType) {
  126. if (field === 'id') {
  127. return renderEventIdAsLinkable;
  128. }
  129. if (field === 'trace') {
  130. return renderTraceAsLinkable;
  131. }
  132. return getFieldRenderer(field, meta, false);
  133. }
  134. export function getEventsRequest(
  135. api: Client,
  136. query: WidgetQuery,
  137. organization: Organization,
  138. pageFilters: PageFilters,
  139. limit?: number,
  140. cursor?: string,
  141. referrer?: string
  142. ) {
  143. const url = `/organizations/${organization.slug}/events/`;
  144. const eventView = eventViewFromWidget('', query, pageFilters);
  145. const params: DiscoverQueryRequestParams = {
  146. per_page: limit,
  147. cursor,
  148. referrer,
  149. dataset: DiscoverDatasets.ERRORS,
  150. };
  151. if (query.orderby) {
  152. params.sort = typeof query.orderby === 'string' ? [query.orderby] : query.orderby;
  153. }
  154. return doDiscoverQuery<EventsTableData>(
  155. api,
  156. url,
  157. {
  158. ...eventView.generateQueryStringObject(),
  159. ...params,
  160. },
  161. // Tries events request up to 3 times on rate limit
  162. {
  163. retry: {
  164. statusCodes: [429],
  165. tries: 3,
  166. },
  167. }
  168. );
  169. }
  170. // The y-axis options are a strict set of available aggregates
  171. export function filterYAxisOptions(_displayType: DisplayType) {
  172. return (option: FieldValueOption) => {
  173. return ERRORS_AGGREGATION_FUNCTIONS.includes(
  174. option.value.meta.name as AggregationKey
  175. );
  176. };
  177. }
  178. function getErrorsSeriesRequest(
  179. api: Client,
  180. widget: Widget,
  181. queryIndex: number,
  182. organization: Organization,
  183. pageFilters: PageFilters,
  184. _onDemandControlContext?: OnDemandControlContext,
  185. referrer?: string,
  186. _mepSetting?: MEPState | null
  187. ) {
  188. const requestData = getSeriesRequestData(
  189. widget,
  190. queryIndex,
  191. organization,
  192. pageFilters,
  193. DiscoverDatasets.ERRORS,
  194. referrer
  195. );
  196. return doEventsRequest<true>(api, requestData);
  197. }