useFlagSeries.tsx 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899
  1. import {useTheme} from '@emotion/react';
  2. import moment from 'moment-timezone';
  3. import MarkLine from 'sentry/components/charts/components/markLine';
  4. import {t} from 'sentry/locale';
  5. import type {Event} from 'sentry/types/event';
  6. import {getFormattedDate} from 'sentry/utils/dates';
  7. import useOrganization from 'sentry/utils/useOrganization';
  8. import usePageFilters from 'sentry/utils/usePageFilters';
  9. import {hydrateToFlagSeries} from 'sentry/views/issueDetails/streamline/featureFlagUtils';
  10. import {useOrganizationFlagLog} from 'sentry/views/issueDetails/streamline/hooks/useOrganizationFlagLog';
  11. interface FlagSeriesProps {
  12. event: Event | undefined;
  13. query: Record<string, any>;
  14. }
  15. export default function useFlagSeries({query = {}, event}: FlagSeriesProps) {
  16. const theme = useTheme();
  17. const organization = useOrganization();
  18. const {
  19. data: rawFlagData,
  20. isError,
  21. isPending,
  22. } = useOrganizationFlagLog({organization, query});
  23. const {selection} = usePageFilters();
  24. if (!rawFlagData || !rawFlagData.data.length || isError || isPending) {
  25. return {
  26. seriesName: t('Feature Flags'),
  27. markLine: {},
  28. data: [],
  29. };
  30. }
  31. const hydratedFlagData = hydrateToFlagSeries(rawFlagData);
  32. const evaluatedFlagNames = event?.contexts?.flags?.values?.map(f => f.flag);
  33. const intersectionFlags = hydratedFlagData.filter(f =>
  34. evaluatedFlagNames?.includes(f.name)
  35. );
  36. if (!intersectionFlags.length) {
  37. return {
  38. seriesName: t('Feature Flags'),
  39. markLine: {},
  40. data: [],
  41. };
  42. }
  43. // create a markline series using hydrated flag data
  44. const markLine = MarkLine({
  45. animation: false,
  46. lineStyle: {
  47. color: theme.blue300,
  48. opacity: 0.3,
  49. type: 'solid',
  50. },
  51. label: {
  52. show: false,
  53. },
  54. data: intersectionFlags,
  55. tooltip: {
  56. trigger: 'item',
  57. formatter: ({data}: any) => {
  58. const time = getFormattedDate(data.xAxis, 'MMM D, YYYY LT z', {
  59. local: !selection.datetime.utc,
  60. });
  61. const eventIsBefore = moment(event?.dateCreated).isBefore(moment(time));
  62. const formattedDate = moment(time).from(event?.dateCreated, true);
  63. const suffix = eventIsBefore
  64. ? t(' (%s after this event)', formattedDate)
  65. : t(' (%s before this event)', formattedDate);
  66. return [
  67. '<div class="tooltip-series">',
  68. `<div><span class="tooltip-label"><strong>${t(
  69. 'Feature Flag'
  70. )}</strong></span></div>`,
  71. `<span class="tooltip-label-align-start"><code class="tooltip-code-no-margin">${data.name}</code>${data.label.formatter()}</span>`,
  72. '</div>',
  73. '<div class="tooltip-footer">',
  74. time,
  75. event?.dateCreated && suffix,
  76. '</div>',
  77. '<div class="tooltip-arrow"></div>',
  78. ].join('');
  79. },
  80. },
  81. });
  82. return {
  83. seriesName: t('Feature Flags'),
  84. data: [],
  85. color: theme.blue200,
  86. markLine,
  87. type: 'line', // use this type so the bar chart doesn't shrink/grow
  88. };
  89. }