metricsDataSwitcherAlert.tsx 6.1 KB

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