index.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import type {Location} from 'history';
  2. import {t} from 'sentry/locale';
  3. import type {Organization} from 'sentry/types/organization';
  4. import type {Project} from 'sentry/types/project';
  5. import EventView from 'sentry/utils/discover/eventView';
  6. import {isAggregateField} from 'sentry/utils/discover/fields';
  7. import type {WebVital} from 'sentry/utils/fields';
  8. import {WEB_VITAL_DETAILS} from 'sentry/utils/performance/vitals/constants';
  9. import {decodeScalar} from 'sentry/utils/queryString';
  10. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  11. import withOrganization from 'sentry/utils/withOrganization';
  12. import withProjects from 'sentry/utils/withProjects';
  13. import PageLayout from '../pageLayout';
  14. import Tab from '../tabs';
  15. import {PERCENTILE, VITAL_GROUPS} from './constants';
  16. import VitalsContent from './content';
  17. type Props = {
  18. location: Location;
  19. organization: Organization;
  20. projects: Project[];
  21. };
  22. function TransactionVitals(props: Props) {
  23. const {location, organization, projects} = props;
  24. return (
  25. <PageLayout
  26. location={location}
  27. organization={organization}
  28. projects={projects}
  29. tab={Tab.WEB_VITALS}
  30. getDocumentTitle={getDocumentTitle}
  31. generateEventView={generateEventView}
  32. childComponent={VitalsContent}
  33. />
  34. );
  35. }
  36. function getDocumentTitle(transactionName: string): string {
  37. const hasTransactionName =
  38. typeof transactionName === 'string' && String(transactionName).trim().length > 0;
  39. if (hasTransactionName) {
  40. return [String(transactionName).trim(), t('Vitals')].join(' \u2014 ');
  41. }
  42. return [t('Summary'), t('Vitals')].join(' \u2014 ');
  43. }
  44. function generateEventView({
  45. location,
  46. transactionName,
  47. }: {
  48. location: Location;
  49. transactionName: string;
  50. }): EventView {
  51. const query = decodeScalar(location.query.query, '');
  52. const conditions = new MutableSearch(query);
  53. conditions.setFilterValues('event.type', ['transaction']);
  54. conditions.setFilterValues('transaction', [transactionName]);
  55. Object.keys(conditions.filters).forEach(field => {
  56. if (isAggregateField(field)) {
  57. conditions.removeFilter(field);
  58. }
  59. });
  60. const vitals = VITAL_GROUPS.reduce((allVitals: WebVital[], group) => {
  61. return allVitals.concat(group.vitals);
  62. }, []);
  63. return EventView.fromNewQueryWithLocation(
  64. {
  65. id: undefined,
  66. version: 2,
  67. name: transactionName,
  68. fields: [
  69. ...vitals.map(vital => `percentile(${vital}, ${PERCENTILE})`),
  70. ...vitals.map(vital => `count_at_least(${vital}, 0)`),
  71. ...vitals.map(
  72. vital => `count_at_least(${vital}, ${WEB_VITAL_DETAILS[vital].poorThreshold})`
  73. ),
  74. ],
  75. query: conditions.formatString(),
  76. projects: [],
  77. },
  78. location
  79. );
  80. }
  81. export default withProjects(withOrganization(TransactionVitals));