metricsDataSwitcherAlert.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import {useCallback, useMemo} from 'react';
  2. import {WithRouterProps} from 'react-router';
  3. import {Location} from 'history';
  4. import {updateProjects} from 'sentry/actionCreators/pageFilters';
  5. import {Alert} from 'sentry/components/alert';
  6. import ExternalLink from 'sentry/components/links/externalLink';
  7. import Link from 'sentry/components/links/link';
  8. import {SidebarPanelKey} from 'sentry/components/sidebar/types';
  9. import {t, tct} from 'sentry/locale';
  10. import SidebarPanelStore from 'sentry/stores/sidebarPanelStore';
  11. import {Organization, Project} from 'sentry/types';
  12. import EventView from 'sentry/utils/discover/eventView';
  13. import {MetricDataSwitcherOutcome} from 'sentry/utils/performance/contexts/metricsCardinality';
  14. import {
  15. areMultipleProjectsSelected,
  16. createUnnamedTransactionsDiscoverTarget,
  17. DiscoverQueryPageSource,
  18. getSelectedProjectPlatformsArray,
  19. } from '../utils';
  20. interface MetricEnhancedDataAlertProps extends MetricDataSwitcherOutcome {
  21. eventView: EventView;
  22. location: Location;
  23. organization: Organization;
  24. projects: Project[];
  25. router: WithRouterProps['router'];
  26. source?: DiscoverQueryPageSource;
  27. }
  28. /**
  29. * From
  30. * https://github.com/getsentry/sentry-docs/blob/master/src/platforms/common/enriching-events/transaction-name.mdx
  31. */
  32. const SUPPORTED_TRANSACTION_NAME_DOCS = [
  33. 'javascript',
  34. 'node',
  35. 'python',
  36. 'ruby',
  37. 'native',
  38. 'react-native',
  39. 'dotnet',
  40. 'unity',
  41. 'flutter',
  42. 'dart',
  43. 'java',
  44. 'android',
  45. ];
  46. const UNSUPPORTED_TRANSACTION_NAME_DOCS = [
  47. 'javascript.cordova',
  48. 'javascript.nextjs',
  49. 'native.minidumps',
  50. ];
  51. export function MetricsDataSwitcherAlert(
  52. props: MetricEnhancedDataAlertProps
  53. ): React.ReactElement | null {
  54. const isOnFallbackThresolds = props.organization.features.includes(
  55. 'performance-mep-bannerless-ui'
  56. );
  57. const handleReviewUpdatesClick = useCallback(() => {
  58. SidebarPanelStore.activatePanel(SidebarPanelKey.Broadcasts);
  59. }, []);
  60. const docsLink = useMemo(() => {
  61. const platforms = getSelectedProjectPlatformsArray(props.location, props.projects);
  62. if (platforms.length < 1) {
  63. return null;
  64. }
  65. const platform = platforms[0];
  66. if (UNSUPPORTED_TRANSACTION_NAME_DOCS.includes(platform)) {
  67. return null;
  68. }
  69. const supportedPlatform = SUPPORTED_TRANSACTION_NAME_DOCS.find(platformBase =>
  70. platform.includes(platformBase)
  71. );
  72. if (!supportedPlatform) {
  73. return null;
  74. }
  75. return `https://docs.sentry.io/platforms/${supportedPlatform}/enriching-events/transaction-name/`;
  76. }, [props.location, props.projects]);
  77. const handleSwitchToCompatibleProjects = useCallback(() => {
  78. updateProjects(props.compatibleProjects || [], props.router);
  79. }, [props.compatibleProjects, props.router]);
  80. if (!props.shouldNotifyUnnamedTransactions && !props.shouldWarnIncompatibleSDK) {
  81. // Control showing generic sdk-alert here since stacking alerts is noisy.
  82. return null;
  83. }
  84. const discoverTarget = createUnnamedTransactionsDiscoverTarget(props);
  85. if (isOnFallbackThresolds) {
  86. return null;
  87. }
  88. if (props.shouldWarnIncompatibleSDK) {
  89. const updateSDK = (
  90. <Link to="" onClick={handleReviewUpdatesClick}>
  91. {t('update your SDK version')}
  92. </Link>
  93. );
  94. if (areMultipleProjectsSelected(props.eventView)) {
  95. if ((props.compatibleProjects ?? []).length === 0) {
  96. return (
  97. <Alert
  98. type="warning"
  99. showIcon
  100. data-test-id="landing-mep-alert-multi-project-all-incompatible"
  101. >
  102. {tct(
  103. `A few projects are incompatible with dynamic sampling. To enable this feature [updateSDK].`,
  104. {
  105. updateSDK,
  106. }
  107. )}
  108. </Alert>
  109. );
  110. }
  111. return (
  112. <Alert
  113. type="warning"
  114. showIcon
  115. data-test-id="landing-mep-alert-multi-project-incompatible"
  116. >
  117. {tct(
  118. `A few projects are incompatible with dynamic sampling. You can either [updateSDK] or [onlyViewCompatible]`,
  119. {
  120. updateSDK,
  121. onlyViewCompatible: (
  122. <Link to="" onClick={handleSwitchToCompatibleProjects}>
  123. {t('only view compatible projects.')}
  124. </Link>
  125. ),
  126. }
  127. )}
  128. </Alert>
  129. );
  130. }
  131. return (
  132. <Alert
  133. type="warning"
  134. showIcon
  135. data-test-id="landing-mep-alert-single-project-incompatible"
  136. >
  137. {tct(
  138. `Your project has an outdated SDK which is incompatible with dynamic sampling. To enable this feature [updateSDK].`,
  139. {
  140. updateSDK,
  141. }
  142. )}
  143. </Alert>
  144. );
  145. }
  146. if (props.shouldNotifyUnnamedTransactions) {
  147. const discover = <Link to={discoverTarget}>{t('open them in Discover.')}</Link>;
  148. if (!docsLink) {
  149. return (
  150. <Alert type="warning" showIcon data-test-id="landing-mep-alert-unnamed-discover">
  151. {tct(
  152. `You have some unparameterized transactions which are incompatible with dynamic sampling. You can [discover]`,
  153. {
  154. discover,
  155. }
  156. )}
  157. </Alert>
  158. );
  159. }
  160. return (
  161. <Alert
  162. type="warning"
  163. showIcon
  164. data-test-id="landing-mep-alert-unnamed-discover-or-set"
  165. >
  166. {tct(
  167. `You have some unparameterized transactions which are incompatible with dynamic sampling. You can either [setNames] or [discover]`,
  168. {
  169. setNames: (
  170. <ExternalLink href={docsLink}>{t('set names manually')}</ExternalLink>
  171. ),
  172. discover,
  173. }
  174. )}
  175. </Alert>
  176. );
  177. }
  178. return null;
  179. }