123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122 |
- import {useCallback, useEffect} from 'react';
- import * as Sentry from '@sentry/react';
- import {experimentConfig, unassignedValue} from 'sentry/data/experimentConfig';
- import ConfigStore from 'sentry/stores/configStore';
- import {useLegacyStore} from 'sentry/stores/useLegacyStore';
- import {
- ExperimentAssignment,
- ExperimentKey,
- ExperimentType,
- OrgExperiments,
- UserExperiments,
- } from 'sentry/types/experiments';
- import {defined} from 'sentry/utils';
- import {logExperiment as logExperimentAnalytics} from 'sentry/utils/analytics';
- import useOrganization from 'sentry/utils/useOrganization';
- type UseExperimentOptions = {
- /**
- * By default this hook will log the exposure of the experiment upon mounting
- * of the component.
- *
- * If this is undesirable, for example if the experiment is hidden behind
- * some user action beyond this component being mounted, then you will want
- * to customize when exposure to the experiment has been logged.
- *
- * NOTE: If set to false, YOU ARE RESPONSIBLE for logging exposure of the
- * experiment!! If you do not log exposure your experiment will not be
- * correct!!
- */
- logExperimentOnMount?: boolean;
- };
- type UseExperimentReturnValue<E extends ExperimentKey> = {
- experimentAssignment: ExperimentAssignment[E];
- /**
- * Call this method when the user has been exposed to the experiment.
- * You do not need to call this unless you have disabled logging on mount.
- */
- logExperiment: () => void;
- };
- export type UseExperiment = <E extends ExperimentKey>(
- experiment: E,
- options?: UseExperimentOptions
- ) => UseExperimentReturnValue<E>;
- function useExperimentAssignment(experiment: ExperimentKey) {
- const organization = useOrganization();
- const {user} = useLegacyStore(ConfigStore);
- const config = experimentConfig[experiment];
- if (!config) {
- Sentry.withScope(scope => {
- scope.setExtra('experiment', experiment);
- Sentry.captureMessage(
- 'useExperiment called with an experiment that does not exist in the config.'
- );
- });
- return unassignedValue;
- }
- if (config.type === ExperimentType.Organization) {
- const key = experiment as keyof OrgExperiments;
- const assignment = organization.experiments?.[key];
- if (!defined(assignment)) {
- Sentry.withScope(scope => {
- scope.setExtra('experiment', experiment);
- scope.setExtra('orgExperiments', organization.experiments);
- Sentry.captureMessage(
- 'useExperiment called with org experiment but no matching experiment exists on the org.'
- );
- });
- }
- return assignment ?? unassignedValue;
- }
- if (config.type === ExperimentType.User) {
- const key = experiment as keyof UserExperiments;
- const assignment = user?.experiments?.[key];
- if (!defined(assignment)) {
- Sentry.withScope(scope => {
- scope.setExtra('experiment', experiment);
- scope.setExtra('userExperiments', user?.experiments);
- Sentry.captureMessage(
- 'useExperiment called with user experiment but no matching experiment exists on the user.'
- );
- });
- }
- return assignment ?? unassignedValue;
- }
- return unassignedValue;
- }
- export const useExperiment: UseExperiment = (
- experiment,
- {logExperimentOnMount = true} = {}
- ) => {
- const organization = useOrganization();
- const logExperiment = useCallback(() => {
- logExperimentAnalytics({
- key: experiment,
- organization,
- });
- }, [experiment, organization]);
- useEffect(() => {
- if (logExperimentOnMount) {
- logExperiment();
- }
- }, [logExperiment, logExperimentOnMount]);
- const experimentAssignment = useExperimentAssignment(experiment);
- return {
- experimentAssignment,
- logExperiment,
- };
- };
|