domainsTable.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. import {browserHistory} from 'react-router';
  2. import type {Location} from 'history';
  3. import type {GridColumnHeader} from 'sentry/components/gridEditable';
  4. import GridEditable, {COL_WIDTH_UNDEFINED} from 'sentry/components/gridEditable';
  5. import type {CursorHandler} from 'sentry/components/pagination';
  6. import Pagination from 'sentry/components/pagination';
  7. import {t} from 'sentry/locale';
  8. import type {Organization} from 'sentry/types';
  9. import type {EventsMetaType} from 'sentry/utils/discover/eventView';
  10. import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
  11. import type {Sort} from 'sentry/utils/discover/fields';
  12. import {RATE_UNIT_TITLE, RateUnit} from 'sentry/utils/discover/fields';
  13. import {VisuallyCompleteWithData} from 'sentry/utils/performanceForSentry';
  14. import {useLocation} from 'sentry/utils/useLocation';
  15. import useOrganization from 'sentry/utils/useOrganization';
  16. import {DomainCell} from 'sentry/views/performance/http/domainCell';
  17. import {ProjectIdCell} from 'sentry/views/performance/http/projectIdCell';
  18. import {renderHeadCell} from 'sentry/views/starfish/components/tableCells/renderHeadCell';
  19. import type {MetricsResponse} from 'sentry/views/starfish/types';
  20. import {QueryParameterNames} from 'sentry/views/starfish/views/queryParameters';
  21. import {DataTitles} from 'sentry/views/starfish/views/spans/types';
  22. type Row = Pick<
  23. MetricsResponse,
  24. | 'project.id'
  25. | 'span.domain'
  26. | 'spm()'
  27. | 'http_response_rate(3)'
  28. | 'http_response_rate(4)'
  29. | 'http_response_rate(5)'
  30. | 'avg(span.self_time)'
  31. | 'sum(span.self_time)'
  32. | 'time_spent_percentage()'
  33. >;
  34. type Column = GridColumnHeader<
  35. | 'span.domain'
  36. | 'project.id'
  37. | 'spm()'
  38. | 'http_response_rate(3)'
  39. | 'http_response_rate(4)'
  40. | 'http_response_rate(5)'
  41. | 'avg(span.self_time)'
  42. | 'time_spent_percentage()'
  43. >;
  44. const COLUMN_ORDER: Column[] = [
  45. {
  46. key: 'span.domain',
  47. name: t('Domain'),
  48. width: COL_WIDTH_UNDEFINED,
  49. },
  50. {
  51. key: 'project.id',
  52. name: t('Project'),
  53. width: COL_WIDTH_UNDEFINED,
  54. },
  55. {
  56. key: 'spm()',
  57. name: `${t('Requests')} ${RATE_UNIT_TITLE[RateUnit.PER_MINUTE]}`,
  58. width: COL_WIDTH_UNDEFINED,
  59. },
  60. {
  61. key: `http_response_rate(3)`,
  62. name: t('3XXs'),
  63. width: 50,
  64. },
  65. {
  66. key: `http_response_rate(4)`,
  67. name: t('4XXs'),
  68. width: 50,
  69. },
  70. {
  71. key: `http_response_rate(5)`,
  72. name: t('5XXs'),
  73. width: 50,
  74. },
  75. {
  76. key: `avg(span.self_time)`,
  77. name: DataTitles.avg,
  78. width: COL_WIDTH_UNDEFINED,
  79. },
  80. {
  81. key: 'time_spent_percentage()',
  82. name: DataTitles.timeSpent,
  83. width: COL_WIDTH_UNDEFINED,
  84. },
  85. ];
  86. const SORTABLE_FIELDS = [
  87. 'avg(span.self_time)',
  88. 'spm()',
  89. 'http_response_rate(3)',
  90. 'http_response_rate(4)',
  91. 'http_response_rate(5)',
  92. 'time_spent_percentage()',
  93. ] as const;
  94. type ValidSort = Sort & {
  95. field: (typeof SORTABLE_FIELDS)[number];
  96. };
  97. export function isAValidSort(sort: Sort): sort is ValidSort {
  98. return (SORTABLE_FIELDS as unknown as string[]).includes(sort.field);
  99. }
  100. interface Props {
  101. response: {
  102. data: Row[];
  103. isLoading: boolean;
  104. error?: Error | null;
  105. meta?: EventsMetaType;
  106. pageLinks?: string;
  107. };
  108. sort: ValidSort;
  109. }
  110. export function DomainsTable({response, sort}: Props) {
  111. const {data, isLoading, meta, pageLinks} = response;
  112. const location = useLocation();
  113. const organization = useOrganization();
  114. const handleCursor: CursorHandler = (newCursor, pathname, query) => {
  115. browserHistory.push({
  116. pathname,
  117. query: {...query, [QueryParameterNames.DOMAINS_CURSOR]: newCursor},
  118. });
  119. };
  120. return (
  121. <VisuallyCompleteWithData
  122. id="DomainsTable"
  123. hasData={data.length > 0}
  124. isLoading={isLoading}
  125. >
  126. <GridEditable
  127. aria-label={t('Domains')}
  128. isLoading={isLoading}
  129. error={response.error}
  130. data={data}
  131. columnOrder={COLUMN_ORDER}
  132. columnSortBy={[
  133. {
  134. key: sort.field,
  135. order: sort.kind,
  136. },
  137. ]}
  138. grid={{
  139. renderHeadCell: column =>
  140. renderHeadCell({
  141. column,
  142. sort,
  143. location,
  144. sortParameterName: QueryParameterNames.DOMAINS_SORT,
  145. }),
  146. renderBodyCell: (column, row) =>
  147. renderBodyCell(column, row, meta, location, organization),
  148. }}
  149. location={location}
  150. />
  151. <Pagination pageLinks={pageLinks} onCursor={handleCursor} />
  152. </VisuallyCompleteWithData>
  153. );
  154. }
  155. function renderBodyCell(
  156. column: Column,
  157. row: Row,
  158. meta: EventsMetaType | undefined,
  159. location: Location,
  160. organization: Organization
  161. ) {
  162. if (column.key === 'span.domain') {
  163. return (
  164. <DomainCell projectId={row['project.id']?.toString()} domain={row['span.domain']} />
  165. );
  166. }
  167. // TODO: Integrate this into `fieldRenderers`
  168. if (column.key === 'project.id') {
  169. return <ProjectIdCell projectId={row['project.id']?.toString()} />;
  170. }
  171. if (!meta?.fields) {
  172. return row[column.key];
  173. }
  174. const renderer = getFieldRenderer(column.key, meta.fields, false);
  175. return renderer(row, {
  176. location,
  177. organization,
  178. unit: meta.units?.[column.key],
  179. });
  180. }