import {Component} from 'react'; import {RouteComponentProps} from 'react-router'; import {Client} from 'sentry/api'; import * as Layout from 'sentry/components/layouts/thirds'; import NoProjectMessage from 'sentry/components/noProjectMessage'; import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse'; import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle'; import {ALL_ACCESS_PROJECTS} from 'sentry/constants/pageFilters'; import {t} from 'sentry/locale'; import {Organization} from 'sentry/types'; import EventView from 'sentry/utils/discover/eventView'; import {QueryError} from 'sentry/utils/discover/genericDiscoverQuery'; import {TraceFullDetailedQuery} from 'sentry/utils/performance/quickTrace/traceFullQuery'; import TraceMetaQuery from 'sentry/utils/performance/quickTrace/traceMetaQuery'; import { TraceError, TraceFullDetailed, TraceMeta, TraceSplitResults, } from 'sentry/utils/performance/quickTrace/types'; import {isTraceSplitResult} from 'sentry/utils/performance/quickTrace/utils'; import {decodeScalar} from 'sentry/utils/queryString'; import withApi from 'sentry/utils/withApi'; import withOrganization from 'sentry/utils/withOrganization'; import TraceDetailsContent from './content'; type Props = RouteComponentProps<{traceSlug: string}, {}> & { api: Client; organization: Organization; }; class TraceSummary extends Component { getDocumentTitle(): string { return [t('Trace Details'), t('Performance')].join(' — '); } getTraceSlug(): string { const {traceSlug} = this.props.params; return typeof traceSlug === 'string' ? traceSlug.trim() : ''; } getDateSelection() { const {location} = this.props; const queryParams = normalizeDateTimeParams(location.query, { allowAbsolutePageDatetime: true, }); const start = decodeScalar(queryParams.start); const end = decodeScalar(queryParams.end); const statsPeriod = decodeScalar(queryParams.statsPeriod); return {start, end, statsPeriod}; } getTraceEventView() { const traceSlug = this.getTraceSlug(); const {start, end, statsPeriod} = this.getDateSelection(); return EventView.fromSavedQuery({ id: undefined, name: `Events with Trace ID ${traceSlug}`, fields: ['title', 'event.type', 'project', 'timestamp'], orderby: '-timestamp', query: `trace:${traceSlug}`, projects: [ALL_ACCESS_PROJECTS], version: 2, start, end, range: statsPeriod, }); } renderContent() { const {location, organization, params} = this.props; const traceSlug = this.getTraceSlug(); const {start, end, statsPeriod} = this.getDateSelection(); const dateSelected = Boolean(statsPeriod || (start && end)); const content = ({ isLoading, error, traces, meta, }: { error: QueryError | null; isLoading: boolean; meta: TraceMeta | null; traces: (TraceFullDetailed[] | TraceSplitResults) | null; }) => { let transactions: TraceFullDetailed[] | undefined; let orphanErrors: TraceError[] | undefined; if ( traces && organization.features.includes('performance-tracing-without-performance') && isTraceSplitResult, TraceFullDetailed[]>( traces ) ) { orphanErrors = traces.orphan_errors; transactions = traces.transactions; } return ( ); }; if (!dateSelected) { return content({ isLoading: false, error: new QueryError('date selection not specified'), traces: null, meta: null, }); } return ( {traceResults => ( {metaResults => content({ isLoading: traceResults.isLoading || metaResults.isLoading, error: traceResults.error || metaResults.error, traces: traceResults.traces, meta: metaResults.meta, }) } )} ); } render() { const {organization} = this.props; return ( {this.renderContent()} ); } } export default withOrganization(withApi(TraceSummary));