useExperiment.tsx 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
  1. import {useCallback, useEffect} from 'react';
  2. import * as Sentry from '@sentry/react';
  3. import {experimentConfig, unassignedValue} from 'sentry/data/experimentConfig';
  4. import type {
  5. ExperimentAssignment,
  6. ExperimentKey,
  7. OrgExperiments,
  8. UserExperiments,
  9. } from 'sentry/types/experiments';
  10. import {ExperimentType} from 'sentry/types/experiments';
  11. import {defined} from 'sentry/utils';
  12. import type {UseExperiment} from 'sentry/utils/useExperiment';
  13. import useOrganization from 'sentry/utils/useOrganization';
  14. import {useUser} from 'sentry/utils/useUser';
  15. import logExperimentAnalytics from 'getsentry/utils/logExperiment';
  16. function useExperimentAssignment<E extends ExperimentKey>(
  17. experiment: E
  18. ): ExperimentAssignment[E] | typeof unassignedValue {
  19. const organization = useOrganization();
  20. const user = useUser();
  21. const config = experimentConfig[experiment];
  22. if (!config) {
  23. Sentry.withScope(scope => {
  24. scope.setExtra('experiment', experiment);
  25. Sentry.captureMessage(
  26. 'useExperiment called with an experiment that does not exist in the config.'
  27. );
  28. });
  29. return unassignedValue;
  30. }
  31. if (config.type === ExperimentType.ORGANIZATION) {
  32. const key = experiment as keyof OrgExperiments;
  33. const assignment = organization.experiments?.[key];
  34. if (!defined(assignment)) {
  35. Sentry.withScope(scope => {
  36. scope.setExtra('experiment', experiment);
  37. scope.setExtra('orgExperiments', organization.experiments);
  38. Sentry.captureMessage(
  39. 'useExperiment called with org experiment but no matching experiment exists on the org.'
  40. );
  41. });
  42. return unassignedValue;
  43. }
  44. return assignment as ExperimentAssignment[E];
  45. }
  46. if (config.type === ExperimentType.USER) {
  47. const key = experiment as keyof UserExperiments;
  48. const assignment = user?.experiments?.[key];
  49. if (!defined(assignment)) {
  50. Sentry.withScope(scope => {
  51. scope.setExtra('experiment', experiment);
  52. scope.setExtra('userExperiments', user?.experiments);
  53. Sentry.captureMessage(
  54. 'useExperiment called with user experiment but no matching experiment exists on the user.'
  55. );
  56. });
  57. return unassignedValue;
  58. }
  59. return assignment as ExperimentAssignment[E];
  60. }
  61. return unassignedValue;
  62. }
  63. export const useExperiment: UseExperiment = (
  64. experiment,
  65. {logExperimentOnMount = true} = {}
  66. ) => {
  67. const organization = useOrganization();
  68. const logExperiment = useCallback(() => {
  69. logExperimentAnalytics({
  70. key: experiment,
  71. organization,
  72. });
  73. }, [experiment, organization]);
  74. useEffect(() => {
  75. if (logExperimentOnMount) {
  76. logExperiment();
  77. }
  78. }, [logExperiment, logExperimentOnMount]);
  79. const experimentAssignment = useExperimentAssignment(experiment);
  80. return {
  81. experimentAssignment,
  82. logExperiment,
  83. };
  84. };