regressedProfileFunctions.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. import {useCallback, useMemo} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import LoadingIndicator from 'sentry/components/loadingIndicator';
  5. import Pagination from 'sentry/components/pagination';
  6. import {t} from 'sentry/locale';
  7. import {space} from 'sentry/styles/space';
  8. import {useProfileFunctionTrends} from 'sentry/utils/profiling/hooks/useProfileFunctionTrends';
  9. import {decodeScalar} from 'sentry/utils/queryString';
  10. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  11. import {useLocation} from 'sentry/utils/useLocation';
  12. const REGRESSED_FUNCTIONS_LIMIT = 5;
  13. const REGRESSED_FUNCTIONS_CURSOR = 'functionRegressionCursor';
  14. interface MostRegressedProfileFunctionsProps {
  15. transaction: string;
  16. }
  17. export function MostRegressedProfileFunctions(props: MostRegressedProfileFunctionsProps) {
  18. const location = useLocation();
  19. const fnTrendCursor = useMemo(
  20. () => decodeScalar(location.query[REGRESSED_FUNCTIONS_CURSOR]),
  21. [location.query]
  22. );
  23. const handleRegressedFunctionsCursor = useCallback((cursor, pathname, query) => {
  24. browserHistory.push({
  25. pathname,
  26. query: {...query, [REGRESSED_FUNCTIONS_CURSOR]: cursor},
  27. });
  28. }, []);
  29. const functionQuery = useMemo(() => {
  30. const conditions = new MutableSearch('');
  31. conditions.setFilterValues('is_application', ['1']);
  32. conditions.setFilterValues('transaction', [props.transaction]);
  33. return conditions.formatString();
  34. }, [props.transaction]);
  35. const trendsQuery = useProfileFunctionTrends({
  36. trendFunction: 'p95()',
  37. trendType: 'regression',
  38. query: functionQuery,
  39. limit: REGRESSED_FUNCTIONS_LIMIT,
  40. cursor: fnTrendCursor,
  41. });
  42. const trends = trendsQuery?.data ?? [];
  43. return (
  44. <RegressedFunctionsContainer>
  45. <RegressedFunctionsTitleContainer>
  46. <RegressedFunctionsTitle>{t('Most regressed functions')}</RegressedFunctionsTitle>
  47. <RegressedFunctionsPagination
  48. pageLinks={trendsQuery.getResponseHeader?.('Link')}
  49. onCursor={handleRegressedFunctionsCursor}
  50. size="xs"
  51. />
  52. </RegressedFunctionsTitleContainer>
  53. {trendsQuery.isLoading ? (
  54. <RegressedFunctionsQueryState>
  55. <LoadingIndicator size={36} />
  56. </RegressedFunctionsQueryState>
  57. ) : trendsQuery.isError ? (
  58. <RegressedFunctionsQueryState>
  59. {t('Failed to fetch regressed functions')}
  60. </RegressedFunctionsQueryState>
  61. ) : !trends.length ? (
  62. <RegressedFunctionsQueryState>
  63. {t('Horay, no regressed functions detected!')}
  64. </RegressedFunctionsQueryState>
  65. ) : (
  66. trends.map((f, i) => <div key={i}>{f.function}</div>)
  67. )}
  68. </RegressedFunctionsContainer>
  69. );
  70. }
  71. const RegressedFunctionsContainer = styled('div')`
  72. flex-basis: 80px;
  73. margin-top: ${space(0.5)};
  74. `;
  75. const RegressedFunctionsPagination = styled(Pagination)`
  76. margin: 0;
  77. `;
  78. const RegressedFunctionsTitleContainer = styled('div')`
  79. display: flex;
  80. align-items: center;
  81. justify-content: space-between;
  82. margin-bottom: ${space(0.5)};
  83. `;
  84. const RegressedFunctionsQueryState = styled('div')`
  85. text-align: center;
  86. padding: ${space(2)} ${space(0.5)};
  87. color: ${p => p.theme.subText};
  88. `;
  89. const RegressedFunctionsTitle = styled('div')`
  90. color: ${p => p.theme.textColor};
  91. font-size: ${p => p.theme.form.md.fontSize};
  92. font-weight: 700;
  93. `;