useReleases.tsx 4.3 KB

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