useReleases.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import chunk from 'lodash/chunk';
  2. import {ReleasesSortOption} from 'sentry/constants/releases';
  3. import type {NewQuery} from 'sentry/types/organization';
  4. import type {Release} from 'sentry/types/release';
  5. import type {TableData} from 'sentry/utils/discover/discoverQuery';
  6. import EventView from 'sentry/utils/discover/eventView';
  7. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  8. import type {ApiQueryKey} from 'sentry/utils/queryClient';
  9. import {useApiQuery, useQueries} from 'sentry/utils/queryClient';
  10. import {decodeScalar} from 'sentry/utils/queryString';
  11. import {escapeFilterValue} from 'sentry/utils/tokenizeSearch';
  12. import useApi from 'sentry/utils/useApi';
  13. import {useLocation} from 'sentry/utils/useLocation';
  14. import useOrganization from 'sentry/utils/useOrganization';
  15. import usePageFilters from 'sentry/utils/usePageFilters';
  16. import type {ReleasesSortByOption} from 'sentry/views/insights/common/components/releasesSort';
  17. export function useReleases(
  18. searchTerm: string | undefined,
  19. sortBy: ReleasesSortByOption | undefined
  20. ) {
  21. const organization = useOrganization();
  22. const location = useLocation();
  23. const {selection, isReady} = usePageFilters();
  24. const {environments, projects} = selection;
  25. const api = useApi();
  26. const activeSort = sortBy ?? ReleasesSortOption.DATE;
  27. const releaseResults = useApiQuery<Release[]>(
  28. [
  29. `/organizations/${organization.slug}/releases/`,
  30. {
  31. query: {
  32. project: projects,
  33. per_page: 50,
  34. environment: environments,
  35. query: searchTerm,
  36. sort: activeSort,
  37. // Depending on the selected sortBy option, 'flatten' is needed or we get an error from the backend.
  38. // A similar logic can be found in https://github.com/getsentry/sentry/blob/6209d6fbf55839bb7a2f93ef65decbf495a64974/static/app/views/releases/list/index.tsx#L106
  39. flatten: activeSort === ReleasesSortOption.DATE ? 0 : 1,
  40. },
  41. },
  42. ],
  43. {staleTime: Infinity, enabled: isReady, retry: false}
  44. );
  45. const chunks = releaseResults.data?.length ? chunk(releaseResults.data, 10) : [];
  46. const releaseMetrics = useQueries({
  47. queries: chunks.map(releases => {
  48. const newQuery: NewQuery = {
  49. name: '',
  50. fields: ['release', 'count()'],
  51. query: `transaction.op:ui.load ${escapeFilterValue(
  52. `release:[${releases.map(r => `"${r.version}"`).join()}]`
  53. )}`,
  54. dataset: DiscoverDatasets.METRICS,
  55. version: 2,
  56. projects: selection.projects,
  57. };
  58. const eventView = EventView.fromNewQueryWithPageFilters(newQuery, selection);
  59. const queryKey = [
  60. `/organizations/${organization.slug}/events/`,
  61. {
  62. query: {
  63. ...eventView.getEventsAPIPayload(location),
  64. referrer: 'api.starfish.mobile-release-selector',
  65. },
  66. },
  67. ] as ApiQueryKey;
  68. return {
  69. queryKey,
  70. queryFn: () =>
  71. api.requestPromise(queryKey[0], {
  72. method: 'GET',
  73. query: queryKey[1]?.query,
  74. }) as Promise<TableData>,
  75. staleTime: Infinity,
  76. enabled: isReady && !releaseResults.isPending,
  77. retry: false,
  78. };
  79. }),
  80. });
  81. const metricsFetched = releaseMetrics.every(result => result.isFetched);
  82. const metricsStats: {[version: string]: {count: number}} = {};
  83. if (metricsFetched) {
  84. releaseMetrics.forEach(c =>
  85. c.data?.data?.forEach(release => {
  86. metricsStats[release.release!] = {count: release['count()'] as number};
  87. })
  88. );
  89. }
  90. const releaseStats: {
  91. dateCreated: string;
  92. version: string;
  93. count?: number;
  94. }[] =
  95. releaseResults.data?.length && metricsFetched
  96. ? releaseResults.data.flatMap(release => {
  97. const releaseVersion = release.version;
  98. const dateCreated = release.dateCreated;
  99. if (metricsStats[releaseVersion]?.count) {
  100. return {
  101. dateCreated,
  102. version: releaseVersion,
  103. count: metricsStats[releaseVersion]?.count,
  104. };
  105. }
  106. return [];
  107. })
  108. : [];
  109. return {
  110. ...releaseResults,
  111. data: releaseStats,
  112. isLoading: !metricsFetched || releaseResults.isPending,
  113. };
  114. }
  115. export function useReleaseSelection(): {
  116. isLoading: boolean;
  117. primaryRelease: string | undefined;
  118. secondaryRelease: string | undefined;
  119. } {
  120. const location = useLocation();
  121. const {data: releases, isLoading} = useReleases(undefined, undefined);
  122. // If there are more than 1 release, the first one should be the older one
  123. const primaryRelease =
  124. decodeScalar(location.query.primaryRelease) ??
  125. (releases && releases.length > 1 ? releases?.[1]?.version : releases?.[0]?.version);
  126. // If there are more than 1 release, the second one should be the newest one
  127. const secondaryRelease =
  128. decodeScalar(location.query.secondaryRelease) ??
  129. (releases && releases.length > 1 ? releases?.[0]?.version : undefined);
  130. return {primaryRelease, secondaryRelease, isLoading};
  131. }