metricsEnhancedSetting.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import {Dispatch, ReactNode, useCallback, useReducer} from 'react';
  2. import {browserHistory} from 'react-router';
  3. import {Location} from 'history';
  4. import {Organization} from 'sentry/types';
  5. import localStorage from 'sentry/utils/localStorage';
  6. import {decodeScalar} from 'sentry/utils/queryString';
  7. import useOrganization from 'sentry/utils/useOrganization';
  8. import {createDefinedContext} from './utils';
  9. export interface MetricsEnhancedSettingContext {
  10. autoSampleState: AutoSampleState;
  11. memoizationKey: string;
  12. metricSettingState: MEPState | null;
  13. setAutoSampleState: Dispatch<AutoSampleState>;
  14. setMetricSettingState: Dispatch<MEPState>;
  15. shouldQueryProvideMEPAutoParams: boolean;
  16. shouldQueryProvideMEPMetricParams: boolean;
  17. shouldQueryProvideMEPTransactionParams: boolean;
  18. }
  19. const [_MEPSettingProvider, _useMEPSettingContext, _MEPSettingContext] =
  20. createDefinedContext<MetricsEnhancedSettingContext>({
  21. name: 'MetricsEnhancedSettingContext',
  22. });
  23. export const MEPConsumer = _MEPSettingContext.Consumer;
  24. /**
  25. * These will be called something else in the copy, but functionally the data is coming from metrics / transactions.
  26. * "Unset" should be the initial state before any queries return for the first time.
  27. */
  28. export enum AutoSampleState {
  29. unset = 'unset',
  30. metrics = 'metrics',
  31. transactions = 'transactions',
  32. }
  33. /**
  34. * Metrics/transactions will be called something else in the copy, but functionally the data is coming from metrics / transactions.
  35. */
  36. export enum MEPState {
  37. auto = 'auto',
  38. metricsOnly = 'metricsOnly',
  39. transactionsOnly = 'transactionsOnly',
  40. }
  41. export const METRIC_SETTING_PARAM = 'metricSetting';
  42. export const METRIC_SEARCH_SETTING_PARAM = 'metricSearchSetting'; // TODO: Clean this up since we don't need multiple params in practice.
  43. const storageKey = 'performance.metrics-enhanced-setting';
  44. export class MEPSetting {
  45. static get(): MEPState | null {
  46. const value = localStorage.getItem(storageKey);
  47. if (value) {
  48. if (!(value in MEPState)) {
  49. localStorage.removeItem(storageKey);
  50. return null;
  51. }
  52. return MEPState[value];
  53. }
  54. return null;
  55. }
  56. static set(value: MEPState) {
  57. localStorage.setItem(storageKey, value);
  58. }
  59. }
  60. export function canUseMetricsDevUI(organization: Organization) {
  61. return organization.features.includes('performance-use-metrics');
  62. }
  63. export function canUseMetricsData(organization: Organization) {
  64. const isDevFlagOn = canUseMetricsDevUI(organization); // Forces metrics data on as well.
  65. const isInternalViewOn = organization.features.includes(
  66. 'performance-transaction-name-only-search'
  67. ); // TODO: Swap this flag out.
  68. const samplingRolloutFlag = organization.features.includes('server-side-sampling');
  69. const isRollingOut =
  70. samplingRolloutFlag && organization.features.includes('mep-rollout-flag');
  71. return isDevFlagOn || isInternalViewOn || isRollingOut;
  72. }
  73. export const MEPSettingProvider = ({
  74. children,
  75. location,
  76. _hasMEPState,
  77. forceTransactions,
  78. }: {
  79. children: ReactNode;
  80. _hasMEPState?: MEPState;
  81. forceTransactions?: boolean;
  82. location?: Location;
  83. }) => {
  84. const organization = useOrganization();
  85. const canUseMEP = canUseMetricsData(organization);
  86. const allowedStates = [MEPState.metricsOnly, MEPState.transactionsOnly];
  87. const _metricSettingFromParam = location
  88. ? decodeScalar(location.query[METRIC_SETTING_PARAM])
  89. : MEPState.metricsOnly;
  90. let defaultMetricsState = MEPState.metricsOnly;
  91. if (forceTransactions) {
  92. defaultMetricsState = MEPState.transactionsOnly;
  93. }
  94. const metricSettingFromParam =
  95. allowedStates.find(s => s === _metricSettingFromParam) ?? defaultMetricsState;
  96. const isControlledMEP = typeof _hasMEPState !== 'undefined';
  97. const [_metricSettingState, _setMetricSettingState] = useReducer(
  98. (_: MEPState, next: MEPState) => next,
  99. metricSettingFromParam
  100. );
  101. const setMetricSettingState = useCallback(
  102. (settingState: MEPState) => {
  103. if (!location) {
  104. return;
  105. }
  106. browserHistory.replace({
  107. ...location,
  108. query: {
  109. ...location.query,
  110. [METRIC_SETTING_PARAM]: settingState,
  111. },
  112. });
  113. _setMetricSettingState(settingState);
  114. },
  115. [location, _setMetricSettingState]
  116. );
  117. const [autoSampleState, setAutoSampleState] = useReducer(
  118. (_: AutoSampleState, next: AutoSampleState) => next,
  119. AutoSampleState.unset
  120. );
  121. const metricSettingState = isControlledMEP ? _hasMEPState : _metricSettingState;
  122. const shouldQueryProvideMEPAutoParams =
  123. canUseMEP && metricSettingState === MEPState.auto;
  124. const shouldQueryProvideMEPMetricParams =
  125. canUseMEP && metricSettingState === MEPState.metricsOnly;
  126. const shouldQueryProvideMEPTransactionParams =
  127. canUseMEP && metricSettingState === MEPState.transactionsOnly;
  128. const memoizationKey = `${metricSettingState}`;
  129. return (
  130. <_MEPSettingProvider
  131. value={{
  132. autoSampleState,
  133. metricSettingState,
  134. shouldQueryProvideMEPAutoParams,
  135. shouldQueryProvideMEPMetricParams,
  136. shouldQueryProvideMEPTransactionParams,
  137. memoizationKey,
  138. setMetricSettingState,
  139. setAutoSampleState,
  140. }}
  141. >
  142. {children}
  143. </_MEPSettingProvider>
  144. );
  145. };
  146. export const useMEPSettingContext = _useMEPSettingContext;