content.tsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. import {Fragment} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import {Location} from 'history';
  5. import omit from 'lodash/omit';
  6. import DatePageFilter from 'sentry/components/datePageFilter';
  7. import DropdownControl, {DropdownItem} from 'sentry/components/dropdownControl';
  8. import EnvironmentPageFilter from 'sentry/components/environmentPageFilter';
  9. import SearchBar from 'sentry/components/events/searchBar';
  10. import * as Layout from 'sentry/components/layouts/thirds';
  11. import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
  12. import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
  13. import Pagination from 'sentry/components/pagination';
  14. import space from 'sentry/styles/space';
  15. import {Organization} from 'sentry/types';
  16. import {defined} from 'sentry/utils';
  17. import trackAdvancedAnalyticsEvent from 'sentry/utils/analytics/trackAdvancedAnalyticsEvent';
  18. import DiscoverQuery from 'sentry/utils/discover/discoverQuery';
  19. import EventView from 'sentry/utils/discover/eventView';
  20. import SuspectSpansQuery from 'sentry/utils/performance/suspectSpans/suspectSpansQuery';
  21. import {decodeScalar} from 'sentry/utils/queryString';
  22. import useProjects from 'sentry/utils/useProjects';
  23. import {SetStateAction} from '../types';
  24. import OpsFilter from './opsFilter';
  25. import {Actions} from './styles';
  26. import SuspectSpansTable from './suspectSpansTable';
  27. import {SpanSort, SpansTotalValues} from './types';
  28. import {
  29. getSuspectSpanSortFromEventView,
  30. getTotalsView,
  31. SPAN_SORT_OPTIONS,
  32. SPAN_SORT_TO_FIELDS,
  33. } from './utils';
  34. const ANALYTICS_VALUES = {
  35. spanOp: (organization: Organization, value: string | undefined) =>
  36. trackAdvancedAnalyticsEvent('performance_views.spans.change_op', {
  37. organization,
  38. operation_name: value,
  39. }),
  40. sort: (organization: Organization, value: string | undefined) =>
  41. trackAdvancedAnalyticsEvent('performance_views.spans.change_sort', {
  42. organization,
  43. sort_column: value,
  44. }),
  45. };
  46. type Props = {
  47. eventView: EventView;
  48. location: Location;
  49. organization: Organization;
  50. projectId: string;
  51. setError: SetStateAction<string | undefined>;
  52. transactionName: string;
  53. };
  54. function SpansContent(props: Props) {
  55. const {location, organization, eventView, projectId, transactionName} = props;
  56. const query = decodeScalar(location.query.query, '');
  57. function handleChange(key: string) {
  58. return function (value: string | undefined) {
  59. ANALYTICS_VALUES[key]?.(organization, value);
  60. const queryParams = normalizeDateTimeParams({
  61. ...(location.query || {}),
  62. [key]: value,
  63. });
  64. // do not propagate pagination when making a new search
  65. const toOmit = ['cursor'];
  66. if (!defined(value)) {
  67. toOmit.push(key);
  68. }
  69. const searchQueryParams = omit(queryParams, toOmit);
  70. browserHistory.push({
  71. ...location,
  72. query: searchQueryParams,
  73. });
  74. };
  75. }
  76. const spanOp = decodeScalar(location.query.spanOp);
  77. const spanGroup = decodeScalar(location.query.spanGroup);
  78. const sort = getSuspectSpanSortFromEventView(eventView);
  79. const spansView = getSpansEventView(eventView, sort.field);
  80. const totalsView = getTotalsView(eventView);
  81. const {projects} = useProjects();
  82. return (
  83. <Layout.Main fullWidth>
  84. <StyledPageFilterBar condensed>
  85. <EnvironmentPageFilter />
  86. <DatePageFilter alignDropdown="left" />
  87. </StyledPageFilterBar>
  88. <Actions>
  89. <OpsFilter
  90. location={location}
  91. eventView={eventView}
  92. organization={organization}
  93. handleOpChange={handleChange('spanOp')}
  94. transactionName={transactionName}
  95. />
  96. <SearchBar
  97. organization={organization}
  98. projectIds={eventView.project}
  99. query={query}
  100. fields={eventView.fields}
  101. onSearch={handleChange('query')}
  102. />
  103. <DropdownControl buttonProps={{prefix: sort.prefix}} label={sort.label}>
  104. {SPAN_SORT_OPTIONS.map(option => (
  105. <DropdownItem
  106. key={option.field}
  107. eventKey={option.field}
  108. isActive={option.field === sort.field}
  109. onSelect={handleChange('sort')}
  110. >
  111. {option.label}
  112. </DropdownItem>
  113. ))}
  114. </DropdownControl>
  115. </Actions>
  116. <DiscoverQuery
  117. eventView={totalsView}
  118. orgSlug={organization.slug}
  119. location={location}
  120. referrer="api.performance.transaction-spans"
  121. cursor="0:0:1"
  122. noPagination
  123. >
  124. {({tableData}) => {
  125. const totals: SpansTotalValues | null =
  126. (tableData?.data?.[0] as SpansTotalValues | undefined) ?? null;
  127. return (
  128. <SuspectSpansQuery
  129. location={location}
  130. orgSlug={organization.slug}
  131. eventView={spansView}
  132. limit={10}
  133. perSuspect={0}
  134. spanOps={defined(spanOp) ? [spanOp] : []}
  135. spanGroups={defined(spanGroup) ? [spanGroup] : []}
  136. >
  137. {({suspectSpans, isLoading, pageLinks}) => (
  138. <Fragment>
  139. <SuspectSpansTable
  140. location={location}
  141. organization={organization}
  142. transactionName={transactionName}
  143. project={projects.find(p => p.id === projectId)}
  144. isLoading={isLoading}
  145. suspectSpans={suspectSpans ?? []}
  146. totals={totals}
  147. sort={sort.field}
  148. />
  149. <Pagination pageLinks={pageLinks ?? null} />
  150. </Fragment>
  151. )}
  152. </SuspectSpansQuery>
  153. );
  154. }}
  155. </DiscoverQuery>
  156. </Layout.Main>
  157. );
  158. }
  159. const StyledPageFilterBar = styled(PageFilterBar)`
  160. margin-bottom: ${space(1)};
  161. `;
  162. function getSpansEventView(eventView: EventView, sort: SpanSort): EventView {
  163. eventView = eventView.clone();
  164. const fields = SPAN_SORT_TO_FIELDS[sort];
  165. eventView.fields = fields ? fields.map(field => ({field})) : [];
  166. return eventView;
  167. }
  168. export default SpansContent;