123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278 |
- import styled from '@emotion/styled';
- import type {Location, LocationDescriptor, Query} from 'history';
- import {space} from 'sentry/styles/space';
- import type {PlainRoute} from 'sentry/types/legacyReactRouter';
- import type {Organization} from 'sentry/types/organization';
- import {getDateFromTimestamp} from 'sentry/utils/dates';
- import type {TableDataRow} from 'sentry/utils/discover/discoverQuery';
- import {generateLinkToEventInTraceView} from 'sentry/utils/discover/urls';
- import getRouteStringFromRoutes from 'sentry/utils/getRouteStringFromRoutes';
- import {
- generateContinuousProfileFlamechartRouteWithQuery,
- generateProfileFlamechartRoute,
- } from 'sentry/utils/profiling/routes';
- import {MutableSearch} from 'sentry/utils/tokenizeSearch';
- import normalizeUrl from 'sentry/utils/url/normalizeUrl';
- import type {DomainView} from 'sentry/views/insights/pages/useFilters';
- import {getTraceDetailsUrl} from 'sentry/views/performance/traceDetails/utils';
- import {getPerformanceBaseUrl} from 'sentry/views/performance/utils';
- import {TraceViewSources} from '../newTraceDetails/traceHeader/breadcrumbs';
- export enum DisplayModes {
- DURATION_PERCENTILE = 'durationpercentile',
- DURATION = 'duration',
- LATENCY = 'latency',
- TREND = 'trend',
- VITALS = 'vitals',
- USER_MISERY = 'usermisery',
- }
- export enum TransactionFilterOptions {
- FASTEST = 'fastest',
- SLOW = 'slow',
- OUTLIER = 'outlier',
- RECENT = 'recent',
- }
- export function generateTransactionSummaryRoute({
- orgSlug,
- subPath,
- view,
- }: {
- orgSlug: string;
- subPath?: string;
- view?: DomainView; // TODO - this should be mantatory once we release domain view
- }): string {
- return `${getTransactionSummaryBaseUrl(orgSlug, view)}/${subPath ? `${subPath}/` : ''}`;
- }
- // normalizes search conditions by removing any redundant search conditions before presenting them in:
- // - query strings
- // - search UI
- export function normalizeSearchConditions(query: string): MutableSearch {
- const filterParams = normalizeSearchConditionsWithTransactionName(query);
- // no need to include transaction as its already in the query params
- filterParams.removeFilter('transaction');
- return filterParams;
- }
- // normalizes search conditions by removing any redundant search conditions, but retains any transaction name
- export function normalizeSearchConditionsWithTransactionName(
- query: string
- ): MutableSearch {
- const filterParams = new MutableSearch(query);
- // remove any event.type queries since it is implied to apply to only transactions
- filterParams.removeFilter('event.type');
- return filterParams;
- }
- export function transactionSummaryRouteWithQuery({
- orgSlug,
- transaction,
- projectID,
- query,
- unselectedSeries = ['p100()', 'avg()'],
- display,
- trendFunction,
- trendColumn,
- showTransactions,
- additionalQuery,
- subPath,
- view,
- }: {
- orgSlug: string;
- query: Query;
- transaction: string;
- additionalQuery?: Record<string, string | undefined>;
- display?: DisplayModes;
- projectID?: string | string[];
- showTransactions?: TransactionFilterOptions;
- subPath?: string;
- trendColumn?: string;
- trendFunction?: string;
- unselectedSeries?: string | string[];
- view?: DomainView;
- }) {
- const pathname = generateTransactionSummaryRoute({
- orgSlug,
- subPath,
- view,
- });
- let searchFilter: typeof query.query;
- if (typeof query.query === 'string') {
- searchFilter = normalizeSearchConditions(query.query).formatString();
- } else {
- searchFilter = query.query;
- }
- return {
- pathname,
- query: {
- transaction,
- project: projectID,
- environment: query.environment,
- statsPeriod: query.statsPeriod,
- start: query.start,
- end: query.end,
- query: searchFilter,
- unselectedSeries,
- showTransactions,
- display,
- trendFunction,
- trendColumn,
- referrer: 'performance-transaction-summary',
- ...additionalQuery,
- },
- };
- }
- export function generateTraceLink(dateSelection, view?: DomainView) {
- return (
- organization: Organization,
- tableRow: TableDataRow,
- location: Location
- ): LocationDescriptor => {
- const traceId = `${tableRow.trace}`;
- if (!traceId) {
- return {};
- }
- return getTraceDetailsUrl({
- organization,
- traceSlug: traceId,
- dateSelection,
- timestamp: tableRow.timestamp,
- location,
- source: TraceViewSources.PERFORMANCE_TRANSACTION_SUMMARY,
- view,
- });
- };
- }
- export function generateTransactionIdLink(transactionName?: string, view?: DomainView) {
- return (
- organization: Organization,
- tableRow: TableDataRow,
- location: Location,
- spanId?: string
- ): LocationDescriptor => {
- return generateLinkToEventInTraceView({
- eventId: tableRow.id,
- timestamp: tableRow.timestamp,
- traceSlug: tableRow.trace?.toString(),
- projectSlug: tableRow['project.name']?.toString(),
- location,
- organization,
- spanId,
- transactionName,
- source: TraceViewSources.PERFORMANCE_TRANSACTION_SUMMARY,
- view,
- });
- };
- }
- export function generateProfileLink() {
- return (
- organization: Organization,
- tableRow: TableDataRow,
- _location: Location | undefined
- ) => {
- const projectSlug = tableRow['project.name'];
- const profileId = tableRow['profile.id'];
- if (projectSlug && profileId) {
- return generateProfileFlamechartRoute({
- orgSlug: organization.slug,
- projectSlug: String(tableRow['project.name']),
- profileId: String(profileId),
- });
- }
- const profilerId = tableRow['profiler.id'];
- const threadId = tableRow['thread.id'];
- const start =
- typeof tableRow['precise.start_ts'] === 'number'
- ? getDateFromTimestamp(tableRow['precise.start_ts'] * 1000)
- : null;
- const finish =
- typeof tableRow['precise.finish_ts'] === 'number'
- ? getDateFromTimestamp(tableRow['precise.finish_ts'] * 1000)
- : null;
- if (projectSlug && profilerId && threadId && start && finish) {
- const query: Record<string, string> = {tid: String(threadId)};
- if (tableRow.id && tableRow.trace) {
- query.eventId = String(tableRow.id);
- query.traceId = String(tableRow.trace);
- }
- return generateContinuousProfileFlamechartRouteWithQuery({
- orgSlug: organization.slug,
- projectSlug: String(projectSlug),
- profilerId: String(profilerId),
- start: start.toISOString(),
- end: finish.toISOString(),
- query,
- });
- }
- return {};
- };
- }
- export function generateReplayLink(routes: PlainRoute<any>[]) {
- const referrer = getRouteStringFromRoutes(routes);
- return (
- organization: Organization,
- tableRow: TableDataRow,
- _location: Location | undefined
- ): LocationDescriptor => {
- const replayId = tableRow.replayId;
- if (!replayId) {
- return {};
- }
- if (!tableRow.timestamp) {
- return {
- pathname: normalizeUrl(
- `/organizations/${organization.slug}/replays/${replayId}/`
- ),
- query: {
- referrer,
- },
- };
- }
- const transactionTimestamp = new Date(tableRow.timestamp).getTime();
- const transactionStartTimestamp = tableRow['transaction.duration']
- ? transactionTimestamp - (tableRow['transaction.duration'] as number)
- : undefined;
- return {
- pathname: normalizeUrl(`/organizations/${organization.slug}/replays/${replayId}/`),
- query: {
- event_t: transactionStartTimestamp,
- referrer,
- },
- };
- };
- }
- export function getTransactionSummaryBaseUrl(
- orgSlug: string,
- view?: DomainView,
- bare: boolean = false
- ) {
- return `${getPerformanceBaseUrl(orgSlug, view, bare)}/summary`;
- }
- export const SidebarSpacer = styled('div')`
- margin-top: ${space(3)};
- `;
|