groupStatsProvider.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. import {createContext, useContext, useEffect} from 'react';
  2. import {dropUndefinedKeys} from '@sentry/utils';
  3. import * as reactQuery from '@tanstack/react-query';
  4. import {ApiResult} from 'sentry/api';
  5. import type {Group, GroupStats, Organization, PageFilters} from 'sentry/types';
  6. import {getUtcDateString} from 'sentry/utils/dates';
  7. import {UseQueryResult} from 'sentry/utils/queryClient';
  8. import RequestError from 'sentry/utils/requestError/requestError';
  9. import useApi from 'sentry/utils/useApi';
  10. function getEndpointParams(
  11. p: Pick<GroupStatsProviderProps, 'selection' | 'period' | 'query' | 'groupIds'>
  12. ): StatEndpointParams {
  13. const params: StatEndpointParams = {
  14. project: p.selection.projects,
  15. environment: p.selection.environments,
  16. groupStatsPeriod: p.period,
  17. query: p.query,
  18. groups: p.groupIds,
  19. ...p.selection.datetime,
  20. };
  21. if (p.selection.datetime.period) {
  22. delete params.period;
  23. params.statsPeriod = p.selection.datetime.period;
  24. }
  25. if (params.end) {
  26. params.end = getUtcDateString(params.end);
  27. }
  28. if (params.start) {
  29. params.start = getUtcDateString(params.start);
  30. }
  31. return dropUndefinedKeys(params);
  32. }
  33. const GroupStatsContext = createContext<UseQueryResult<
  34. Record<string, GroupStats>
  35. > | null>(null);
  36. export function useGroupStats(group: Group): GroupStats {
  37. const ctx = useContext(GroupStatsContext);
  38. if (!ctx) {
  39. return group;
  40. }
  41. return ctx.data?.[group.id] ?? group;
  42. }
  43. interface StatEndpointParams extends Partial<PageFilters['datetime']> {
  44. environment: string[];
  45. groups: Group['id'][];
  46. project: number[];
  47. cursor?: string;
  48. expand?: string | string[];
  49. groupStatsPeriod?: string | null;
  50. page?: number | string;
  51. query?: string | undefined;
  52. sort?: string;
  53. statsPeriod?: string | null;
  54. }
  55. export type GroupStatsQuery = UseQueryResult<Record<string, GroupStats>, RequestError>;
  56. export interface GroupStatsProviderProps {
  57. children: React.ReactNode;
  58. groupIds: Group['id'][];
  59. organization: Organization;
  60. period: string;
  61. selection: PageFilters;
  62. onStatsQuery?: (query: GroupStatsQuery) => void;
  63. query?: string;
  64. }
  65. export function GroupStatsProvider(props: GroupStatsProviderProps) {
  66. const api = useApi();
  67. const queryFn = (): Promise<Record<string, GroupStats>> => {
  68. const promise = api
  69. .requestPromise<true>(`/organizations/${props.organization.slug}/issues-stats/`, {
  70. method: 'GET',
  71. query: getEndpointParams({
  72. selection: props.selection,
  73. period: props.period,
  74. query: props.query,
  75. groupIds: props.groupIds,
  76. }),
  77. includeAllArgs: true,
  78. })
  79. .then((resp: ApiResult<GroupStats[]>): Record<string, GroupStats> => {
  80. const map: Record<string, GroupStats> = {};
  81. if (!resp || !Array.isArray(resp[0])) {
  82. return map;
  83. }
  84. for (const stat of resp[0]) {
  85. map[stat.id] = stat;
  86. }
  87. return map;
  88. });
  89. return promise;
  90. };
  91. const statsQuery = reactQuery.useQuery<Record<string, GroupStats>, RequestError>(
  92. [
  93. `/organizations/${props.organization.slug}/issues-stats/`,
  94. props.selection,
  95. props.period,
  96. props.query,
  97. props.groupIds,
  98. ],
  99. queryFn,
  100. {
  101. enabled: props.groupIds.length > 0,
  102. staleTime: Infinity,
  103. }
  104. );
  105. const onStatsQuery = props.onStatsQuery;
  106. useEffect(() => {
  107. onStatsQuery?.(statsQuery);
  108. // We only want to fire the observer when the status changes
  109. // eslint-disable-next-line react-hooks/exhaustive-deps
  110. }, [statsQuery.status, onStatsQuery]);
  111. return (
  112. <GroupStatsContext.Provider value={statsQuery}>
  113. {props.children}
  114. </GroupStatsContext.Provider>
  115. );
  116. }