index.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. import {useCallback, useMemo, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import SearchBar from 'sentry/components/events/searchBar';
  4. import * as Layout from 'sentry/components/layouts/thirds';
  5. import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
  6. import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
  7. import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
  8. import {ProfileEventsTable} from 'sentry/components/profiling/profileEventsTable';
  9. import type {SmartSearchBarProps} from 'sentry/components/smartSearchBar';
  10. import SmartSearchBar from 'sentry/components/smartSearchBar';
  11. import {MAX_QUERY_LENGTH} from 'sentry/constants';
  12. import {t} from 'sentry/locale';
  13. import {space} from 'sentry/styles/space';
  14. import {defined} from 'sentry/utils';
  15. import {browserHistory} from 'sentry/utils/browserHistory';
  16. import EventView from 'sentry/utils/discover/eventView';
  17. import {useProfileEvents} from 'sentry/utils/profiling/hooks/useProfileEvents';
  18. import {useProfileFilters} from 'sentry/utils/profiling/hooks/useProfileFilters';
  19. import {formatSort} from 'sentry/utils/profiling/hooks/utils';
  20. import {decodeScalar} from 'sentry/utils/queryString';
  21. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  22. import {useLocation} from 'sentry/utils/useLocation';
  23. import useOrganization from 'sentry/utils/useOrganization';
  24. import usePageFilters from 'sentry/utils/usePageFilters';
  25. import useProjects from 'sentry/utils/useProjects';
  26. import Tab from 'sentry/views/performance/transactionSummary/tabs';
  27. import type {ProfilingFieldType} from 'sentry/views/profiling/profileSummary/content';
  28. import {getProfilesTableFields} from 'sentry/views/profiling/profileSummary/content';
  29. import PageLayout from '../pageLayout';
  30. function Profiles(): React.ReactElement {
  31. const location = useLocation();
  32. const organization = useOrganization();
  33. const projects = useProjects();
  34. const {selection} = usePageFilters();
  35. const profilesCursor = useMemo(
  36. () => decodeScalar(location.query.cursor),
  37. [location.query.cursor]
  38. );
  39. const project = projects.projects.find(p => p.id === location.query.project);
  40. const fields = getProfilesTableFields(project?.platform);
  41. const sortableFields = useMemo(() => new Set(fields), [fields]);
  42. const sort = formatSort<ProfilingFieldType>(decodeScalar(location.query.sort), fields, {
  43. key: 'timestamp',
  44. order: 'desc',
  45. });
  46. const [query, setQuery] = useState(() => {
  47. // The search fields from the URL differ between profiling and
  48. // events dataset. For now, just drop everything except transaction
  49. const search = new MutableSearch('');
  50. const transaction = decodeScalar(location.query.transaction);
  51. if (defined(transaction)) {
  52. search.setFilterValues('transaction', [transaction]);
  53. }
  54. return search;
  55. });
  56. const profiles = useProfileEvents<ProfilingFieldType>({
  57. cursor: profilesCursor,
  58. fields,
  59. query: query.formatString(),
  60. sort,
  61. limit: 30,
  62. referrer: 'api.profiling.transactions-profiles-table',
  63. });
  64. const handleSearch: SmartSearchBarProps['onSearch'] = useCallback(
  65. (searchQuery: string) => {
  66. setQuery(new MutableSearch(searchQuery));
  67. browserHistory.push({
  68. ...location,
  69. query: {
  70. ...location.query,
  71. cursor: undefined,
  72. query: searchQuery || undefined,
  73. },
  74. });
  75. },
  76. [location]
  77. );
  78. const profilingUsingTransactions = organization.features.includes(
  79. 'profiling-using-transactions'
  80. );
  81. const profileFilters = useProfileFilters({
  82. query: '',
  83. selection,
  84. disabled: profilingUsingTransactions,
  85. });
  86. const transaction = decodeScalar(location.query.transaction);
  87. return (
  88. <PageLayout
  89. location={location}
  90. organization={organization}
  91. projects={projects.projects}
  92. tab={Tab.PROFILING}
  93. generateEventView={() => EventView.fromLocation(location)}
  94. getDocumentTitle={() => t(`Profile: %s`, transaction)}
  95. childComponent={() => {
  96. return (
  97. <Layout.Main fullWidth>
  98. <FilterActions>
  99. <PageFilterBar condensed>
  100. <EnvironmentPageFilter />
  101. <DatePageFilter />
  102. </PageFilterBar>
  103. {profilingUsingTransactions ? (
  104. <SearchBar
  105. searchSource="profile_landing"
  106. organization={organization}
  107. projectIds={projects.projects.map(p => parseInt(p.id, 10))}
  108. query={query.formatString()}
  109. onSearch={handleSearch}
  110. maxQueryLength={MAX_QUERY_LENGTH}
  111. />
  112. ) : (
  113. <SmartSearchBar
  114. organization={organization}
  115. hasRecentSearches
  116. projectIds={projects.projects.map(p => parseInt(p.id, 10))}
  117. searchSource="profile_landing"
  118. supportedTags={profileFilters}
  119. query={query.formatString()}
  120. onSearch={handleSearch}
  121. maxQueryLength={MAX_QUERY_LENGTH}
  122. />
  123. )}
  124. </FilterActions>
  125. <ProfileEventsTable
  126. columns={fields}
  127. data={profiles.status === 'success' ? profiles.data : null}
  128. error={profiles.status === 'error' ? t('Unable to load profiles') : null}
  129. isLoading={profiles.status === 'loading'}
  130. sort={sort}
  131. sortableColumns={sortableFields}
  132. />
  133. </Layout.Main>
  134. );
  135. }}
  136. />
  137. );
  138. }
  139. const FilterActions = styled('div')`
  140. margin-bottom: ${space(2)};
  141. gap: ${space(2)};
  142. display: grid;
  143. grid-template-columns: min-content 1fr;
  144. `;
  145. export default Profiles;