Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions static/app/components/timeRangeSelector/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,32 @@ describe('TimeRangeSelector', () => {
expect(screen.queryByRole('option', {name: 'Last 90 days'})).not.toBeInTheDocument();
});

it('shows a 180-day default option for self-hosted flagged organizations', async () => {
ConfigStore.set('isSelfHosted', true);

render(getComponent(), {
organization: OrganizationFixture({
features: ['open-membership', 'visibility-increased-max-pickable-days'],
}),
});

await userEvent.click(screen.getByRole('button', {expanded: false}));

expect(screen.getByRole('option', {name: 'Last 180 days'})).toBeInTheDocument();
});

it('does not show a 180-day default option on SaaS', async () => {
render(getComponent(), {
organization: OrganizationFixture({
features: ['open-membership', 'visibility-increased-max-pickable-days'],
}),
});

await userEvent.click(screen.getByRole('button', {expanded: false}));

expect(screen.queryByRole('option', {name: 'Last 180 days'})).not.toBeInTheDocument();
});

it('respects maxPickableDays for arbitrary time ranges', async () => {
renderComponent({maxPickableDays: 30});

Expand Down
1 change: 1 addition & 0 deletions static/app/constants/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ export const DEFAULT_RELATIVE_PERIODS = {
'14d': t('Last 14 days'),
'30d': t('Last 30 days'),
'90d': t('Last 90 days'),
'180d': t('Last 180 days'),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfiltered 180d option leaks through relativeOptions callbacks

Medium Severity

Adding 180d unconditionally to DEFAULT_RELATIVE_PERIODS causes it to leak into TimeRangeSelector consumers that use relativeOptions as a function callback. The restrictedDefaultPeriods filtering (which respects maxPickableDays) only applies when relativeOptions is not a function. When it is a function, the raw unfiltered DEFAULT_RELATIVE_PERIODS is passed as defaultOptions. Callers like eventDetailsHeader.tsx and releases/detail/overview/index.tsx spread props.defaultOptions directly, so "Last 180 days" will appear on SaaS where maxPickableDays is 90.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a2b4572. Configure here.

};

const DEFAULT_STATS_INFO = {
Expand Down
4 changes: 3 additions & 1 deletion static/app/utils/profiling/hooks/useRelativeDateTime.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {useMemo} from 'react';

import {useTimezone} from 'sentry/components/timezoneProvider';
import type {PageFilters} from 'sentry/types/core';
import {useDefaultMaxPickableDays} from 'sentry/utils/useMaxPickableDays';

const DAY = 24 * 60 * 60 * 1000;

Expand All @@ -17,13 +18,14 @@ export function useRelativeDateTime({
retentionDays,
}: UseRelativeDateTimeOptions) {
const timezone = useTimezone();
const defaultMaxPickableDays = useDefaultMaxPickableDays();

const anchorTime = anchor * 1000;

// Make sure to memo this. Otherwise, each re-render will have
// a different min/max date time, causing the query to refetch.
const maxDateTime = useMemo(() => Date.now(), []);
const minDateTime = maxDateTime - (retentionDays ?? 90) * DAY;
const minDateTime = maxDateTime - (retentionDays ?? defaultMaxPickableDays) * DAY;

const beforeTime = anchorTime - relativeDays * DAY;
const beforeDateTime =
Expand Down
1 change: 1 addition & 0 deletions static/app/utils/useDatePageFilterProps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export function useDatePageFilterProps({
[14, '14d', t('Last 14 days')],
[30, '30d', t('Last 30 days')],
[90, '90d', t('Last 90 days')],
[180, '180d', t('Last 180 days')],
];

// find the relative options that should be enabled based on the maxPickableDays
Expand Down
52 changes: 51 additions & 1 deletion static/app/utils/useMaxPickableDays.spec.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,40 @@
import {ConfigFixture} from 'sentry-fixture/config';
import {OrganizationFixture} from 'sentry-fixture/organization';

import {renderHookWithProviders} from 'sentry-test/reactTestingLibrary';

import {ConfigStore} from 'sentry/stores/configStore';
import {DataCategory} from 'sentry/types/core';

import {useMaxPickableDays} from './useMaxPickableDays';
import {
getDefaultMaxPickableDays,
INCREASED_MAX_PICKABLE_DAYS,
useMaxPickableDays,
} from './useMaxPickableDays';

describe('useMaxPickableDays', () => {
beforeEach(() => {
ConfigStore.loadInitialData(ConfigFixture());
});

it('returns 180 for self-hosted flagged organizations', () => {
ConfigStore.set('isSelfHosted', true);

expect(
getDefaultMaxPickableDays(
OrganizationFixture({features: ['visibility-increased-max-pickable-days']})
)
).toBe(INCREASED_MAX_PICKABLE_DAYS);
});

it('returns 90 for SaaS organizations even with the flag', () => {
expect(
getDefaultMaxPickableDays(
OrganizationFixture({features: ['visibility-increased-max-pickable-days']})
)
).toBe(90);
});

it('returns 90/90 for transactions', () => {
const {result} = renderHookWithProviders(() =>
useMaxPickableDays({
Expand Down Expand Up @@ -79,6 +107,28 @@ describe('useMaxPickableDays', () => {
});
});

it('returns 180/180 days for trace metrics on self-hosted with the flag', () => {
ConfigStore.set('isSelfHosted', true);

const {result} = renderHookWithProviders(
() =>
useMaxPickableDays({
dataCategories: [DataCategory.TRACE_METRICS],
}),
{
organization: OrganizationFixture({
features: ['visibility-increased-max-pickable-days'],
}),
}
);

expect(result.current).toEqual({
defaultPeriod: '24h',
maxPickableDays: 180,
maxUpgradableDays: 180,
});
});

it('returns 30/30 days for logs', () => {
const {result} = renderHookWithProviders(() =>
useMaxPickableDays({
Expand Down
53 changes: 43 additions & 10 deletions static/app/utils/useMaxPickableDays.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,16 @@ import {HookOrDefault} from 'sentry/components/hookOrDefault';
import type {DatePageFilterProps} from 'sentry/components/pageFilters/date/datePageFilter';
import {MAX_PICKABLE_DAYS} from 'sentry/constants';
import {t} from 'sentry/locale';
import {ConfigStore} from 'sentry/stores/configStore';
import {HookStore} from 'sentry/stores/hookStore';
import {DataCategory} from 'sentry/types/core';
import type {Organization} from 'sentry/types/organization';
import {useOrganization} from 'sentry/utils/useOrganization';

export const INCREASED_MAX_PICKABLE_DAYS = 180;
export const INCREASED_MAX_PICKABLE_DAYS_FEATURE =
'visibility-increased-max-pickable-days';

/**
* This returns the default max pickable days for the current organization.
*
Expand All @@ -22,9 +27,29 @@ export function useDefaultMaxPickableDays(): number {
}

function useDefaultMaxPickableDaysImpl() {
const organization = useOrganization({allowNull: true});
return getDefaultMaxPickableDays(organization);
}

export function getDefaultMaxPickableDays(
organization: Organization | null | undefined
): number {
if (isIncreasedMaxPickableDaysEnabled(organization)) {
return INCREASED_MAX_PICKABLE_DAYS;
}

return MAX_PICKABLE_DAYS;
}

export function isIncreasedMaxPickableDaysEnabled(
organization: Organization | null | undefined
): boolean {
return Boolean(
ConfigStore.get('isSelfHosted') &&
organization?.features.includes(INCREASED_MAX_PICKABLE_DAYS_FEATURE)
);
}

export interface MaxPickableDaysOptions {
/**
* The maximum number of days the user is allowed to pick on the date page filter
Expand Down Expand Up @@ -97,26 +122,34 @@ export function getMaxPickableDays(
dataCategory: DataCategory,
organization: Organization
): MaxPickableDaysOptions {
const defaultMaxPickableDays = getDefaultMaxPickableDays(organization);
const hasIncreasedMaxPickableDays = isIncreasedMaxPickableDaysEnabled(organization);

switch (dataCategory) {
case DataCategory.SPANS:
case DataCategory.SPANS_INDEXED: {
const maxPickableDays = organization.features.includes(
'visibility-explore-range-high'
)
? MAX_PICKABLE_DAYS
: 30;
let maxPickableDays = 30;

if (hasIncreasedMaxPickableDays) {
maxPickableDays = defaultMaxPickableDays;
} else if (organization.features.includes('visibility-explore-range-high')) {
maxPickableDays = MAX_PICKABLE_DAYS;
}

return {
maxPickableDays,
maxUpgradableDays: MAX_PICKABLE_DAYS,
maxUpgradableDays: hasIncreasedMaxPickableDays
? defaultMaxPickableDays
: MAX_PICKABLE_DAYS,
upsellFooter: SpansUpsellFooter,
};
}
case DataCategory.TRACE_METRICS:
case DataCategory.LOG_BYTE:
case DataCategory.LOG_ITEM:
return {
maxPickableDays: 30,
maxUpgradableDays: 30,
maxPickableDays: hasIncreasedMaxPickableDays ? defaultMaxPickableDays : 30,
maxUpgradableDays: hasIncreasedMaxPickableDays ? defaultMaxPickableDays : 30,
defaultPeriod: '24h',
};
case DataCategory.PROFILE_CHUNKS:
Expand All @@ -126,8 +159,8 @@ export function getMaxPickableDays(
case DataCategory.TRANSACTIONS:
case DataCategory.REPLAYS:
return {
maxPickableDays: MAX_PICKABLE_DAYS,
maxUpgradableDays: MAX_PICKABLE_DAYS,
maxPickableDays: defaultMaxPickableDays,
maxUpgradableDays: defaultMaxPickableDays,
};
default:
throw new Error(
Expand Down
21 changes: 20 additions & 1 deletion static/app/views/alerts/rules/crons/details.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import {MonitorFixture} from 'sentry-fixture/monitor';
import {OrganizationFixture} from 'sentry-fixture/organization';
import {ProjectFixture} from 'sentry-fixture/project';

import {render, screen} from 'sentry-test/reactTestingLibrary';
import {render, screen, userEvent} from 'sentry-test/reactTestingLibrary';

import {ConfigStore} from 'sentry/stores/configStore';

import MonitorDetails from 'sentry/views/alerts/rules/crons/details';

Expand All @@ -21,6 +23,7 @@ describe('Monitor Details', () => {

beforeEach(() => {
MockApiClient.clearMockResponses();
ConfigStore.set('isSelfHosted', false);
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/monitors/${monitor.slug}/`,
body: {...monitor},
Expand Down Expand Up @@ -88,4 +91,20 @@ describe('Monitor Details', () => {
)
).toBeInTheDocument();
});

it('shows a 180-day filter option for self-hosted flagged organizations', async () => {
ConfigStore.set('isSelfHosted', true);

render(<MonitorDetails />, {
organization: OrganizationFixture({
features: ['visibility-increased-max-pickable-days'],
}),
initialRouterConfig,
});

await screen.findByText(monitor.slug, {exact: false});
await userEvent.click(screen.getByTestId('page-filter-timerange-selector'));

expect(screen.getByRole('option', {name: 'Last 180 days'})).toBeInTheDocument();
});
});
4 changes: 3 additions & 1 deletion static/app/views/alerts/rules/crons/details.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {t} from 'sentry/locale';
import {useApiQuery} from 'sentry/utils/queryClient';
import {useApi} from 'sentry/utils/useApi';
import {useLocation} from 'sentry/utils/useLocation';
import {useDefaultMaxPickableDays} from 'sentry/utils/useMaxPickableDays';
import {useOrganization} from 'sentry/utils/useOrganization';
import {useParams} from 'sentry/utils/useParams';
import {DetailsSidebar} from 'sentry/views/insights/crons/components/detailsSidebar';
Expand All @@ -43,6 +44,7 @@ function hasLastCheckIn(monitor: Monitor) {
export default function MonitorDetails() {
const api = useApi();
const organization = useOrganization();
const maxPickableDays = useDefaultMaxPickableDays();
const queryClient = useQueryClient();
const params = useParams<{monitorSlug: string; projectId: string}>();
const location = useLocation();
Expand Down Expand Up @@ -127,7 +129,7 @@ export default function MonitorDetails() {
<Layout.Main>
<Flex justify="between" align="center" gap="md">
<StyledPageFilterBar condensed>
<DatePageFilter maxPickableDays={30} />
<DatePageFilter maxPickableDays={maxPickableDays} />
<EnvironmentPageFilter />
</StyledPageFilterBar>
<TimezoneOverride
Expand Down
5 changes: 3 additions & 2 deletions static/app/views/explore/conversations/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ import {NoAccess} from 'sentry/components/noAccess';
import {NoProjectMessage} from 'sentry/components/noProjectMessage';
import {PageFiltersContainer} from 'sentry/components/pageFilters/container';
import {SentryDocumentTitle} from 'sentry/components/sentryDocumentTitle';
import {useDefaultMaxPickableDays} from 'sentry/utils/useMaxPickableDays';
import {normalizeUrl} from 'sentry/utils/url/normalizeUrl';
import {useOrganization} from 'sentry/utils/useOrganization';
import {useParams} from 'sentry/utils/useParams';
import {
CONVERSATIONS_LANDING_SUB_PATH,
CONVERSATIONS_LANDING_TITLE,
CONVERSATIONS_SIDEBAR_LABEL,
MAX_PICKABLE_DAYS,
} from 'sentry/views/explore/conversations/settings';
import {TopBar} from 'sentry/views/navigation/topBar';

Expand All @@ -45,14 +45,15 @@ function ConversationsLayout() {

function ConversationsLayoutContent() {
const organization = useOrganization();
const maxPickableDays = useDefaultMaxPickableDays();

return (
<SentryDocumentTitle title={CONVERSATIONS_LANDING_TITLE} orgSlug={organization.slug}>
<AnalyticsArea name="explore.conversations">
<Stack flex={1}>
<ConversationsHeader />
<NoProjectMessage organization={organization}>
<PageFiltersContainer maxPickableDays={MAX_PICKABLE_DAYS}>
<PageFiltersContainer maxPickableDays={maxPickableDays}>
<Outlet />
</PageFiltersContainer>
</NoProjectMessage>
Expand Down
7 changes: 4 additions & 3 deletions static/app/views/explore/conversations/overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
import {SearchQueryBuilderProvider} from 'sentry/components/searchQueryBuilder/context';
import {trackAnalytics} from 'sentry/utils/analytics';
import {useDatePageFilterProps} from 'sentry/utils/useDatePageFilterProps';
import {useDefaultMaxPickableDays} from 'sentry/utils/useMaxPickableDays';
import {useOrganization} from 'sentry/utils/useOrganization';
import {SchemaHintsList} from 'sentry/views/explore/components/schemaHints/schemaHintsList';
import {SchemaHintsSources} from 'sentry/views/explore/components/schemaHints/schemaHintsUtils';
Expand All @@ -28,7 +29,6 @@ import {useSpanItemAttributes} from 'sentry/views/explore/contexts/traceItemAttr
import {ConversationsTable} from 'sentry/views/explore/conversations/components/conversationsTable';
import {useShowConversationOnboarding} from 'sentry/views/explore/conversations/hooks/useShowConversationOnboarding';
import {ConversationOnboarding} from 'sentry/views/explore/conversations/onboarding';
import {MAX_PICKABLE_DAYS} from 'sentry/views/explore/conversations/settings';
import {AgentSelector} from 'sentry/views/insights/common/components/agentSelector';
import {useDefaultToAllProjects} from 'sentry/views/insights/common/utils/useDefaultToAllProjects';
import {useTableCursor} from 'sentry/views/insights/pages/agents/hooks/useTableCursor';
Expand All @@ -38,9 +38,10 @@ const DISABLE_AGGREGATES: never[] = [];

function ConversationsOverviewPage() {
const organization = useOrganization();
const maxPickableDays = useDefaultMaxPickableDays();
const datePageFilterProps = useDatePageFilterProps({
maxPickableDays: MAX_PICKABLE_DAYS,
maxUpgradableDays: MAX_PICKABLE_DAYS,
maxPickableDays,
maxUpgradableDays: maxPickableDays,
});
useDefaultToAllProjects();
const {
Expand Down
4 changes: 3 additions & 1 deletion static/app/views/insights/crons/views/overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {selectJsonWithHeaders} from 'sentry/utils/api/apiOptions';
import {decodeList, decodeScalar} from 'sentry/utils/queryString';
import {useRouteAnalyticsEventNames} from 'sentry/utils/routeAnalytics/useRouteAnalyticsEventNames';
import {useRouteAnalyticsParams} from 'sentry/utils/routeAnalytics/useRouteAnalyticsParams';
import {useDefaultMaxPickableDays} from 'sentry/utils/useMaxPickableDays';
import {useLocation} from 'sentry/utils/useLocation';
import {useNavigate} from 'sentry/utils/useNavigate';
import {useOrganization} from 'sentry/utils/useOrganization';
Expand All @@ -54,6 +55,7 @@ const CronsListPageHeader = HookOrDefault({

function CronsOverview() {
const organization = useOrganization();
const maxPickableDays = useDefaultMaxPickableDays();
const navigate = useNavigate();
const location = useLocation();
const {guideVisible} = useCronsUpsertGuideState();
Expand Down Expand Up @@ -174,7 +176,7 @@ function CronsOverview() {
<PageFilterBar>
<ProjectPageFilter resetParamsOnChange={['cursor']} />
<EnvironmentPageFilter resetParamsOnChange={['cursor']} />
<DatePageFilter maxPickableDays={30} />
<DatePageFilter maxPickableDays={maxPickableDays} />
</PageFilterBar>
<SearchBar
query={decodeScalar(qs.parse(location.search)?.query, '')}
Expand Down
Loading
Loading