events.tsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. import {LocationDescriptor} from 'history';
  2. import pick from 'lodash/pick';
  3. import {Client, ResponseMeta} from 'sentry/api';
  4. import {canIncludePreviousPeriod} from 'sentry/components/charts/utils';
  5. import {
  6. DateString,
  7. EventsStats,
  8. MultiSeriesEventsStats,
  9. OrganizationSummary,
  10. } from 'sentry/types';
  11. import {LocationQuery} from 'sentry/utils/discover/eventView';
  12. import {getPeriod} from 'sentry/utils/getPeriod';
  13. import {PERFORMANCE_URL_PARAM} from 'sentry/utils/performance/constants';
  14. import {QueryBatching} from 'sentry/utils/performance/contexts/genericQueryBatcher';
  15. type Options = {
  16. organization: OrganizationSummary;
  17. partial: boolean;
  18. comparisonDelta?: number;
  19. end?: DateString;
  20. environment?: Readonly<string[]>;
  21. excludeOther?: boolean;
  22. field?: string[];
  23. generatePathname?: (org: OrganizationSummary) => string;
  24. includePrevious?: boolean;
  25. interval?: string;
  26. limit?: number;
  27. orderby?: string;
  28. period?: string | null;
  29. project?: Readonly<number[]>;
  30. query?: string;
  31. queryBatching?: QueryBatching;
  32. queryExtras?: Record<string, string>;
  33. referrer?: string;
  34. start?: DateString;
  35. team?: Readonly<string | string[]>;
  36. topEvents?: number;
  37. withoutZerofill?: boolean;
  38. yAxis?: string | string[];
  39. };
  40. /**
  41. * Make requests to `events-stats` endpoint
  42. *
  43. * @param {Object} api API client instance
  44. * @param {Object} options Request parameters
  45. * @param {Object} options.organization Organization object
  46. * @param {Number[]} options.project List of project ids
  47. * @param {String[]} options.environment List of environments to query for
  48. * @param {Boolean} options.excludeOther Exclude the "Other" series when making a topEvents query
  49. * @param {String[]} options.team List of teams to query for
  50. * @param {String} options.period Time period to query for, in the format: <integer><units> where units are "d" or "h"
  51. * @param {String} options.interval Time interval to group results in, in the format: <integer><units> where units are "d", "h", "m", "s"
  52. * @param {Number} options.comparisonDelta Comparison delta for change alert event stats to include comparison stats
  53. * @param {Boolean} options.includePrevious Should request also return reqsults for previous period?
  54. * @param {Number} options.limit The number of rows to return
  55. * @param {String} options.query Search query
  56. * @param {QueryBatching} options.queryBatching A container for batching functions from a provider
  57. * @param {Record<string, string>} options.queryExtras A list of extra query parameters
  58. * @param {(org: OrganizationSummary) => string} options.generatePathname A function that returns an override for the pathname
  59. */
  60. export const doEventsRequest = <IncludeAllArgsType extends boolean = false>(
  61. api: Client,
  62. {
  63. organization,
  64. project,
  65. environment,
  66. team,
  67. period,
  68. start,
  69. end,
  70. interval,
  71. comparisonDelta,
  72. includePrevious,
  73. query,
  74. yAxis,
  75. field,
  76. topEvents,
  77. orderby,
  78. partial,
  79. withoutZerofill,
  80. referrer,
  81. queryBatching,
  82. generatePathname,
  83. queryExtras,
  84. excludeOther,
  85. includeAllArgs,
  86. }: {includeAllArgs?: IncludeAllArgsType} & Options
  87. ): IncludeAllArgsType extends true
  88. ? Promise<
  89. [EventsStats | MultiSeriesEventsStats, string | undefined, ResponseMeta | undefined]
  90. >
  91. : Promise<EventsStats | MultiSeriesEventsStats> => {
  92. const pathname =
  93. generatePathname?.(organization) ??
  94. `/organizations/${organization.slug}/events-stats/`;
  95. const shouldDoublePeriod = canIncludePreviousPeriod(includePrevious, period);
  96. const urlQuery = Object.fromEntries(
  97. Object.entries({
  98. interval,
  99. comparisonDelta,
  100. project,
  101. environment,
  102. team,
  103. query,
  104. yAxis,
  105. field,
  106. topEvents,
  107. orderby,
  108. partial: partial ? '1' : undefined,
  109. withoutZerofill: withoutZerofill ? '1' : undefined,
  110. referrer: referrer ? referrer : 'api.organization-event-stats',
  111. excludeOther: excludeOther ? '1' : undefined,
  112. }).filter(([, value]) => typeof value !== 'undefined')
  113. );
  114. // Doubling period for absolute dates is not accurate unless starting and
  115. // ending times are the same (at least for daily intervals). This is
  116. // the tradeoff for now.
  117. const periodObj = getPeriod({period, start, end}, {shouldDoublePeriod});
  118. const queryObject = {
  119. includeAllArgs,
  120. query: {
  121. ...urlQuery,
  122. ...periodObj,
  123. ...queryExtras,
  124. },
  125. };
  126. if (queryBatching?.batchRequest) {
  127. return queryBatching.batchRequest(api, pathname, queryObject);
  128. }
  129. return api.requestPromise<IncludeAllArgsType>(pathname, queryObject);
  130. };
  131. export type EventQuery = {
  132. field: string[];
  133. query: string;
  134. environment?: string[];
  135. equation?: string[];
  136. noPagination?: boolean;
  137. per_page?: number;
  138. project?: string | string[];
  139. referrer?: string;
  140. sort?: string | string[];
  141. team?: string | string[];
  142. };
  143. export type TagSegment = {
  144. count: number;
  145. name: string;
  146. url: LocationDescriptor;
  147. value: string;
  148. isOther?: boolean;
  149. key?: string;
  150. };
  151. export type Tag = {
  152. key: string;
  153. topValues: Array<TagSegment>;
  154. };
  155. /**
  156. * Fetches tag facets for a query
  157. */
  158. export function fetchTagFacets(
  159. api: Client,
  160. orgSlug: string,
  161. query: EventQuery
  162. ): Promise<Tag[]> {
  163. const urlParams = pick(query, Object.values(PERFORMANCE_URL_PARAM));
  164. const queryOption = {...urlParams, query: query.query};
  165. return api.requestPromise(`/organizations/${orgSlug}/events-facets/`, {
  166. query: queryOption,
  167. });
  168. }
  169. /**
  170. * Fetches total count of events for a given query
  171. */
  172. export function fetchTotalCount(
  173. api: Client,
  174. orgSlug: string,
  175. query: EventQuery & LocationQuery
  176. ): Promise<number> {
  177. const urlParams = pick(query, Object.values(PERFORMANCE_URL_PARAM));
  178. const queryOption = {...urlParams, query: query.query};
  179. type Response = {
  180. count: number;
  181. };
  182. return api
  183. .requestPromise(`/organizations/${orgSlug}/events-meta/`, {
  184. query: queryOption,
  185. })
  186. .then((res: Response) => res.count);
  187. }