123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135 |
- import {useTheme} from '@emotion/react';
- import moment from 'moment-timezone';
- import MarkLine from 'sentry/components/charts/components/markLine';
- import {t} from 'sentry/locale';
- import type {Event} from 'sentry/types/event';
- import type {Organization} from 'sentry/types/organization';
- import {getFormattedDate} from 'sentry/utils/dates';
- import {useApiQuery} from 'sentry/utils/queryClient';
- import useOrganization from 'sentry/utils/useOrganization';
- import usePageFilters from 'sentry/utils/usePageFilters';
- type RawFlag = {
- action: string;
- created_at: string;
- created_by: string;
- created_by_type: string;
- flag: string;
- id: number;
- tags: Record<string, any>;
- };
- export type RawFlagData = {data: RawFlag[]};
- type FlagSeriesDatapoint = {
- // flag action
- label: {formatter: () => string};
- // flag name
- name: string;
- // unix timestamp
- xAxis: number;
- };
- interface FlagSeriesProps {
- event: Event;
- query: Record<string, any>;
- }
- function useOrganizationFlagLog({
- organization,
- query,
- }: {
- organization: Organization;
- query: Record<string, any>;
- }) {
- const {data, isError, isPending} = useApiQuery<RawFlagData>(
- [`/organizations/${organization.slug}/flags/logs/`, {query}],
- {
- staleTime: 0,
- enabled: organization.features?.includes('feature-flag-ui'),
- }
- );
- return {data, isError, isPending};
- }
- function hydrateFlagData({
- rawFlagData,
- }: {
- rawFlagData: RawFlagData;
- }): FlagSeriesDatapoint[] {
- // transform raw flag data into series data
- // each data point needs to be type FlagSeriesDatapoint
- const flagData = rawFlagData.data.map(f => {
- return {
- xAxis: Date.parse(f.created_at),
- label: {formatter: () => f.action},
- name: `${f.flag}`,
- };
- });
- return flagData;
- }
- export default function useFlagSeries({query = {}, event}: FlagSeriesProps) {
- const theme = useTheme();
- const organization = useOrganization();
- const {
- data: rawFlagData,
- isError,
- isPending,
- } = useOrganizationFlagLog({organization, query});
- const {selection} = usePageFilters();
- if (!rawFlagData || isError || isPending) {
- return {
- seriesName: t('Feature Flags'),
- markLine: {},
- data: [],
- };
- }
- const hydratedFlagData: FlagSeriesDatapoint[] = hydrateFlagData({rawFlagData});
- // create a markline series using hydrated flag data
- const markLine = MarkLine({
- animation: false,
- lineStyle: {
- color: theme.purple300,
- opacity: 0.3,
- type: 'solid',
- },
- label: {
- show: false,
- },
- data: hydratedFlagData,
- tooltip: {
- trigger: 'item',
- formatter: ({data}: any) => {
- const time = getFormattedDate(data.xAxis, 'MMM D, YYYY LT z', {
- local: !selection.datetime.utc,
- });
- return [
- '<div class="tooltip-series">',
- `<div><span class="tooltip-label"><strong>${t(
- 'Feature Flag'
- )}</strong></span></div>`,
- `<span class="tooltip-label-align-start"><code class="tooltip-code-no-margin">${data.name}</code>${data.label.formatter()}</span>`,
- '</div>',
- '<div class="tooltip-footer">',
- time,
- event.dateCreated &&
- ` (${moment(time).from(event.dateCreated, true)} ${t('before this event')})`,
- '</div>',
- '<div class="tooltip-arrow"></div>',
- ].join('');
- },
- },
- });
- return {
- seriesName: t('Feature Flags'),
- data: [],
- markLine,
- type: 'line', // use this type so the bar chart doesn't shrink/grow
- };
- }
|