diff --git a/static/app/components/quickTrace/index.tsx b/static/app/components/quickTrace/index.tsx
index 28629a1e48e53f..b3ee9a264f4e3a 100644
--- a/static/app/components/quickTrace/index.tsx
+++ b/static/app/components/quickTrace/index.tsx
@@ -18,7 +18,6 @@ import {backend, frontend, mobile, serverless} from 'sentry/data/platformCategor
import {IconFire} from 'sentry/icons';
import {t, tct, tn} from 'sentry/locale';
import type {Event} from 'sentry/types/event';
-import type {OrganizationSummary} from 'sentry/types/organization';
import {trackAnalytics} from 'sentry/utils/analytics';
import {getDocsPlatform} from 'sentry/utils/docs';
import getDuration from 'sentry/utils/duration/getDuration';
@@ -35,6 +34,7 @@ import Projects from 'sentry/utils/projects';
const FRONTEND_PLATFORMS: string[] = [...frontend, ...mobile];
const BACKEND_PLATFORMS: string[] = [...backend, ...serverless];
+import type {Organization} from 'sentry/types/organization';
import {generateLinkToEventInTraceView} from 'sentry/utils/discover/urls';
import {
@@ -67,7 +67,7 @@ type QuickTraceProps = Pick<
> & {
event: Event;
location: Location;
- organization: OrganizationSummary;
+ organization: Organization;
quickTrace: QuickTraceType;
};
@@ -288,18 +288,14 @@ export default function QuickTrace({
return {nodes};
}
-function handleNode(key: string, organization: OrganizationSummary) {
+function handleNode(key: string, organization: Organization) {
trackAnalytics('quick_trace.node.clicked', {
organization: organization.id,
node_key: key,
});
}
-function handleDropdownItem(
- key: string,
- organization: OrganizationSummary,
- extra: boolean
-) {
+function handleDropdownItem(key: string, organization: Organization, extra: boolean) {
const eventKey = extra
? 'quick_trace.dropdown.clicked_extra'
: 'quick_trace.dropdown.clicked';
@@ -316,7 +312,7 @@ type EventNodeSelectorProps = {
events: QuickTraceEvent[];
location: Location;
nodeKey: keyof typeof TOOLTIP_PREFIX;
- organization: OrganizationSummary;
+ organization: Organization;
text: React.ReactNode;
traceSlug: string;
transactionDest: TransactionDestination;
@@ -410,10 +406,7 @@ function EventNodeSelector({
projectSlug: events[0].project_slug,
timestamp: events[0].timestamp,
location,
- organization: {
- slug: organization.slug,
- features: organization.features,
- },
+ organization,
transactionName: events[0].transaction,
type: transactionDest,
});
@@ -484,10 +477,7 @@ function EventNodeSelector({
projectSlug: event.project_slug,
eventId: event.event_id,
location,
- organization: {
- slug: organization.slug,
- features: organization.features,
- },
+ organization,
type: transactionDest,
transactionName: event.transaction,
});
@@ -525,7 +515,7 @@ function EventNodeSelector({
type DropdownNodeProps = {
anchor: 'left' | 'right';
event: TraceError | QuickTraceEvent | TracePerformanceIssue;
- organization: OrganizationSummary;
+ organization: Organization;
allowDefaultEvent?: boolean;
onSelect?: (eventKey: any) => void;
subtext?: string;
diff --git a/static/app/components/quickTrace/utils.tsx b/static/app/components/quickTrace/utils.tsx
index 22dc490743e77e..6f9676ef9f8323 100644
--- a/static/app/components/quickTrace/utils.tsx
+++ b/static/app/components/quickTrace/utils.tsx
@@ -3,7 +3,7 @@ import type {Location, LocationDescriptor} from 'history';
import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters';
import type {Event} from 'sentry/types/event';
-import type {OrganizationSummary} from 'sentry/types/organization';
+import type {Organization} from 'sentry/types/organization';
import {defined} from 'sentry/utils';
import EventView from 'sentry/utils/discover/eventView';
import {
@@ -32,7 +32,7 @@ export type TransactionDestination = 'discover' | 'performance';
export function generateIssueEventTarget(
event: TraceError | TracePerformanceIssue,
- organization: OrganizationSummary,
+ organization: Organization,
referrer?: string
): LocationDescriptor {
const queryParams = referrer ? '?referrer=' + referrer : '';
@@ -41,7 +41,7 @@ export function generateIssueEventTarget(
function generateDiscoverEventTarget(
event: EventLite | TraceError | TracePerformanceIssue,
- organization: OrganizationSummary,
+ organization: Organization,
location: Location,
referrer?: string
): LocationDescriptor {
@@ -67,7 +67,7 @@ function generateDiscoverEventTarget(
export function generateSingleErrorTarget(
event: TraceError | TracePerformanceIssue,
- organization: OrganizationSummary,
+ organization: Organization,
location: Location,
destination: ErrorDestination,
referrer?: string
@@ -84,7 +84,7 @@ export function generateSingleErrorTarget(
export function generateMultiTransactionsTarget(
currentEvent: Event,
events: EventLite[],
- organization: OrganizationSummary,
+ organization: Organization,
groupType: 'Ancestor' | 'Children' | 'Descendant'
): LocationDescriptor {
const queryResults = new MutableSearch([]);
@@ -134,7 +134,7 @@ export function getEventTimestamp(event: Event): string | number | undefined {
export function generateTraceTarget(
event: Event,
- organization: OrganizationSummary,
+ organization: Organization,
location: Location,
source?: string
): LocationDescriptor {
diff --git a/static/app/types/organization.tsx b/static/app/types/organization.tsx
index fcc0eb85885f1e..0d714cdbac3cd0 100644
--- a/static/app/types/organization.tsx
+++ b/static/app/types/organization.tsx
@@ -48,7 +48,8 @@ export interface Organization extends OrganizationSummary {
allowJoinRequests: boolean;
allowSharedIssues: boolean;
attachmentsRole: string;
- availableRoles: {id: string; name: string}[]; // Deprecated, use orgRoleList
+ /** @deprecated use orgRoleList instead. */
+ availableRoles: {id: string; name: string}[];
dataScrubber: boolean;
dataScrubberDefaults: boolean;
debugFilesRole: string;
@@ -79,6 +80,12 @@ export interface Organization extends OrganizationSummary {
trustedRelays: Relay[];
desiredSampleRate?: number | null;
effectiveSampleRate?: number | null;
+ extraOptions?: {
+ traces: {
+ checkSpanExtractionDate: boolean;
+ spansExtractionDate: number;
+ };
+ };
orgRole?: string;
planSampleRate?: number | null;
}
diff --git a/static/app/utils/discover/urls.tsx b/static/app/utils/discover/urls.tsx
index e136d8a80fcf10..15d210add131ad 100644
--- a/static/app/utils/discover/urls.tsx
+++ b/static/app/utils/discover/urls.tsx
@@ -57,7 +57,7 @@ export function generateLinkToEventInTraceView({
}: {
eventId: string;
location: Location;
- organization: Pick;
+ organization: Organization;
projectSlug: string;
timestamp: string | number;
traceSlug: string;
diff --git a/static/app/views/performance/traceDetails/TraceDetailsRouting.tsx b/static/app/views/performance/traceDetails/TraceDetailsRouting.tsx
index 6d2274a9fee5af..384c04d3e8c729 100644
--- a/static/app/views/performance/traceDetails/TraceDetailsRouting.tsx
+++ b/static/app/views/performance/traceDetails/TraceDetailsRouting.tsx
@@ -7,7 +7,7 @@ import {browserHistory} from 'sentry/utils/browserHistory';
import {useLocation} from 'sentry/utils/useLocation';
import useOrganization from 'sentry/utils/useOrganization';
-import {getTraceDetailsUrl} from './utils';
+import {getTraceDetailsUrl, shouldForceRouteToOldView} from './utils';
type Props = {
children: JSX.Element;
@@ -25,7 +25,10 @@ function TraceDetailsRouting(props: Props) {
return children;
}
- if (organization.features.includes('trace-view-v1')) {
+ if (
+ organization.features.includes('trace-view-v1') &&
+ !shouldForceRouteToOldView(organization, getEventTimestamp(event))
+ ) {
if (event?.groupID && event?.eventID) {
const issuesLocation = `/organizations/${organization.slug}/issues/${event.groupID}/events/${event.eventID}`;
browserHistory.replace({
diff --git a/static/app/views/performance/traceDetails/utils.tsx b/static/app/views/performance/traceDetails/utils.tsx
index e4c4a5a44ebd48..001c9d8f3f17a6 100644
--- a/static/app/views/performance/traceDetails/utils.tsx
+++ b/static/app/views/performance/traceDetails/utils.tsx
@@ -1,7 +1,7 @@
import type {Location, LocationDescriptorObject} from 'history';
import {PAGE_URL_PARAM} from 'sentry/constants/pageFilters';
-import type {Organization, OrganizationSummary} from 'sentry/types';
+import type {Organization} from 'sentry/types';
import {getTimeStampFromTableDateField} from 'sentry/utils/dates';
import type {
EventLite,
@@ -29,7 +29,7 @@ export function getTraceDetailsUrl({
}: {
dateSelection;
location: Location;
- organization: Pick;
+ organization: Organization;
traceSlug: string;
demo?: string;
eventId?: string;
@@ -46,6 +46,17 @@ export function getTraceDetailsUrl({
[PAGE_URL_PARAM.PAGE_END]: end,
};
+ const oldTraceUrl = {
+ pathname: normalizeUrl(
+ `/organizations/${organization.slug}/performance/trace/${traceSlug}/`
+ ),
+ query: queryParams,
+ };
+
+ if (shouldForceRouteToOldView(organization, timestamp)) {
+ return oldTraceUrl;
+ }
+
if (organization.features.includes('trace-view-v1')) {
if (spanId) {
queryParams.node = [`span-${spanId}`, `txn-${eventId}`];
@@ -68,12 +79,28 @@ export function getTraceDetailsUrl({
queryParams.limit = DEFAULT_TRACE_ROWS_LIMIT;
}
- return {
- pathname: normalizeUrl(
- `/organizations/${organization.slug}/performance/trace/${traceSlug}/`
- ),
- query: queryParams,
- };
+ return oldTraceUrl;
+}
+
+/**
+ * Single tenant, on-premise etc. users may not have span extraction enabled.
+ *
+ * This code can be removed at the time we're sure all STs have rolled out span extraction.
+ */
+export function shouldForceRouteToOldView(
+ organization: Organization,
+ timestamp: string | number | undefined
+) {
+ const usableTimestamp = getTimeStampFromTableDateField(timestamp);
+ if (!usableTimestamp) {
+ // Timestamps must always be provided for the new view, if it doesn't exist, fall back to the old view.
+ return true;
+ }
+
+ return (
+ organization.extraOptions?.traces.checkSpanExtractionDate &&
+ organization.extraOptions?.traces.spansExtractionDate <= usableTimestamp
+ );
}
function transactionVisitor() {