suspectSpans.tsx 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import {Fragment} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import {Location} from 'history';
  5. import {Button} from 'sentry/components/button';
  6. import {SectionHeading} from 'sentry/components/charts/styles';
  7. import Pagination, {CursorHandler} from 'sentry/components/pagination';
  8. import {t} from 'sentry/locale';
  9. import {space} from 'sentry/styles/space';
  10. import {Organization} from 'sentry/types';
  11. import EventView from 'sentry/utils/discover/eventView';
  12. import SuspectSpansQuery from 'sentry/utils/performance/suspectSpans/suspectSpansQuery';
  13. import {decodeScalar} from 'sentry/utils/queryString';
  14. import useProjects from 'sentry/utils/useProjects';
  15. import SuspectSpansTable from '../transactionSpans/suspectSpansTable';
  16. import {
  17. SpanSortOthers,
  18. SpanSortPercentiles,
  19. SpansTotalValues,
  20. } from '../transactionSpans/types';
  21. import {
  22. getSuspectSpanSortFromLocation,
  23. SPAN_SORT_TO_FIELDS,
  24. spansRouteWithQuery,
  25. } from '../transactionSpans/utils';
  26. const SPANS_CURSOR_NAME = 'spansCursor';
  27. type Props = {
  28. eventView: EventView;
  29. location: Location;
  30. organization: Organization;
  31. projectId: string;
  32. totals: SpansTotalValues | null;
  33. transactionName: string;
  34. };
  35. export default function SuspectSpans(props: Props) {
  36. const {location, organization, eventView, totals, projectId, transactionName} = props;
  37. const sort = getSuspectSpanSortFromLocation(location, 'spanSort');
  38. const cursor = decodeScalar(location.query?.[SPANS_CURSOR_NAME]);
  39. const sortedEventView = eventView
  40. .withColumns(
  41. [...Object.values(SpanSortOthers), ...Object.values(SpanSortPercentiles)].map(
  42. field => ({kind: 'field', field})
  43. )
  44. )
  45. .withSorts([{kind: 'desc', field: sort.field}]);
  46. const fields = SPAN_SORT_TO_FIELDS[sort.field];
  47. sortedEventView.fields = fields ? fields.map(field => ({field})) : [];
  48. const {projects} = useProjects();
  49. return (
  50. <SuspectSpansQuery
  51. location={location}
  52. orgSlug={organization.slug}
  53. eventView={sortedEventView}
  54. limit={4}
  55. perSuspect={0}
  56. cursor={cursor}
  57. >
  58. {({suspectSpans, isLoading, pageLinks}) => (
  59. <Fragment>
  60. <SuspectSpansHeader
  61. location={location}
  62. organization={organization}
  63. projectId={projectId}
  64. transactionName={transactionName}
  65. pageLinks={pageLinks}
  66. />
  67. <SuspectSpansTable
  68. location={location}
  69. organization={organization}
  70. transactionName={transactionName}
  71. project={projects.find(p => p.id === projectId)}
  72. isLoading={isLoading}
  73. suspectSpans={suspectSpans ?? []}
  74. totals={totals}
  75. sort={SpanSortOthers.SUM_EXCLUSIVE_TIME}
  76. />
  77. </Fragment>
  78. )}
  79. </SuspectSpansQuery>
  80. );
  81. }
  82. type HeaderProps = {
  83. location: Location;
  84. organization: Organization;
  85. pageLinks: string | null;
  86. projectId: string;
  87. transactionName: string;
  88. };
  89. function SuspectSpansHeader(props: HeaderProps) {
  90. const {location, organization, projectId, transactionName, pageLinks} = props;
  91. const viewAllTarget = spansRouteWithQuery({
  92. orgSlug: organization.slug,
  93. transaction: transactionName,
  94. projectID: projectId,
  95. query: location.query,
  96. });
  97. const handleCursor: CursorHandler = (cursor, pathname, query) => {
  98. browserHistory.push({
  99. pathname,
  100. query: {...query, [SPANS_CURSOR_NAME]: cursor},
  101. });
  102. };
  103. return (
  104. <Header>
  105. <SectionHeading>{t('Suspect Spans')}</SectionHeading>
  106. <Button to={viewAllTarget} size="xs" data-test-id="suspect-spans-open-tab">
  107. {t('View All Spans')}
  108. </Button>
  109. <StyledPagination pageLinks={pageLinks} onCursor={handleCursor} size="xs" />
  110. </Header>
  111. );
  112. }
  113. const Header = styled('div')`
  114. display: grid;
  115. grid-template-columns: 1fr auto auto;
  116. margin-bottom: ${space(1)};
  117. align-items: center;
  118. `;
  119. const StyledPagination = styled(Pagination)`
  120. margin: 0 0 0 ${space(1)};
  121. `;