featureFlags.ts 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. import {logger} from '@sentry/core';
  2. import * as Sentry from '@sentry/react';
  3. import type {Organization} from 'sentry/types/organization';
  4. import type {Project} from 'sentry/types/project';
  5. /**
  6. * Returns a callback that can be used to track sentry flag evaluations through
  7. * the Sentry SDK, in the event context. If the FeatureFlagsIntegration is not
  8. * installed, the callback is a no-op.
  9. *
  10. * @param prefix - optionally specifies a prefix for flag names, before calling
  11. * the SDK hook
  12. */
  13. export function buildSentryFeaturesHandler(
  14. prefix?: string
  15. ): (name: string, value: unknown) => void {
  16. const featureFlagsIntegration =
  17. Sentry.getClient()?.getIntegrationByName<Sentry.FeatureFlagsIntegration>(
  18. 'FeatureFlags'
  19. );
  20. if (!featureFlagsIntegration || !('addFeatureFlag' in featureFlagsIntegration)) {
  21. logger.error(
  22. 'Unable to track flag evaluations because FeatureFlagsIntegration is not installed correctly.'
  23. );
  24. return (_name, _value) => {};
  25. }
  26. return (name: string, value: unknown) => {
  27. // Append `feature.organizations:` in front to match the Sentry options automator format
  28. featureFlagsIntegration?.addFeatureFlag((prefix ?? '') + name, value);
  29. };
  30. }
  31. /**
  32. * Registers a handler that processes feature names and values on each call to
  33. * organization.features.includes().
  34. */
  35. export function addOrganizationFeaturesHandler({
  36. organization,
  37. handler,
  38. }: {
  39. handler: (name: string, value: unknown) => void;
  40. organization: Organization;
  41. }) {
  42. const includesHandler = {
  43. apply: (includes: any, orgFeatures: string[], flagName: string[]) => {
  44. // Evaluate the result of .includes() and pass it to hook before returning
  45. const flagResult = includes.apply(orgFeatures, flagName);
  46. handler(flagName[0], flagResult);
  47. return flagResult;
  48. },
  49. };
  50. const proxy = new Proxy(organization.features.includes, includesHandler);
  51. organization.features.includes = proxy;
  52. }
  53. /**
  54. * Registers a handler that processes feature names and values on each call to
  55. * organization.features.includes().
  56. */
  57. export function addProjectFeaturesHandler({
  58. project,
  59. handler,
  60. }: {
  61. handler: (name: string, value: unknown) => void;
  62. project: Project;
  63. }) {
  64. const includesHandler = {
  65. apply: (includes: any, projFeatures: string[], flagName: string[]) => {
  66. // Evaluate the result of .includes() and pass it to hook before returning
  67. const flagResult = includes.apply(projFeatures, flagName);
  68. handler(flagName[0], flagResult);
  69. return flagResult;
  70. },
  71. };
  72. const proxy = new Proxy(project.features.includes, includesHandler);
  73. project.features.includes = proxy;
  74. }