mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
230 lines
7.2 KiB
TypeScript
230 lines
7.2 KiB
TypeScript
import {useEffect, useMemo, useState} from 'react';
|
|
import type {Location} from 'history';
|
|
|
|
import {getSampleEventQuery} from 'sentry/components/events/eventStatisticalDetector/eventComparison/eventDisplay';
|
|
import LoadingError from 'sentry/components/loadingError';
|
|
import {
|
|
PlatformCategory,
|
|
profiling as PROFILING_PLATFORMS,
|
|
} from 'sentry/data/platformCategories';
|
|
import {t} from 'sentry/locale';
|
|
import type {EventTransaction, Group, Organization, PlatformKey} from 'sentry/types';
|
|
import {IssueCategory, IssueType} from 'sentry/types';
|
|
import EventView, {decodeSorts} from 'sentry/utils/discover/eventView';
|
|
import {DiscoverDatasets} from 'sentry/utils/discover/types';
|
|
import {getConfigForIssueType} from 'sentry/utils/issueTypeConfig';
|
|
import {platformToCategory} from 'sentry/utils/platform';
|
|
import {useApiQuery} from 'sentry/utils/queryClient';
|
|
import {projectCanLinkToReplay} from 'sentry/utils/replays/projectSupportsReplay';
|
|
import {useRoutes} from 'sentry/utils/useRoutes';
|
|
import EventsTable from 'sentry/views/performance/transactionSummary/transactionEvents/eventsTable';
|
|
|
|
interface Props {
|
|
group: Group;
|
|
issueId: string;
|
|
location: Location;
|
|
organization: Organization;
|
|
excludedTags?: string[];
|
|
}
|
|
|
|
const makeGroupPreviewRequestUrl = ({groupId}: {groupId: string}) => {
|
|
return `/issues/${groupId}/events/latest/`;
|
|
};
|
|
|
|
function AllEventsTable(props: Props) {
|
|
const {location, organization, issueId, excludedTags, group} = props;
|
|
const config = getConfigForIssueType(props.group, group.project);
|
|
const [error, setError] = useState<string>('');
|
|
const routes = useRoutes();
|
|
const {fields, columnTitles} = getColumns(group, organization);
|
|
const now = useMemo(() => Date.now(), []);
|
|
|
|
const endpointUrl = makeGroupPreviewRequestUrl({
|
|
groupId: group.id,
|
|
});
|
|
|
|
const queryEnabled = group.issueCategory === IssueCategory.PERFORMANCE;
|
|
const {data, isLoading, isLoadingError} = useApiQuery<EventTransaction>([endpointUrl], {
|
|
staleTime: 60000,
|
|
enabled: queryEnabled,
|
|
});
|
|
|
|
// TODO: this is a temporary way to check whether
|
|
// perf issue is backed by occurrences or transactions
|
|
// Once migration to the issue platform is complete a call to /latest should be removed
|
|
const groupIsOccurrenceBacked = !!data?.occurrence;
|
|
|
|
const eventView: EventView = EventView.fromLocation(props.location);
|
|
if (
|
|
config.usesIssuePlatform ||
|
|
(group.issueCategory === IssueCategory.PERFORMANCE && groupIsOccurrenceBacked)
|
|
) {
|
|
eventView.dataset = DiscoverDatasets.ISSUE_PLATFORM;
|
|
}
|
|
eventView.fields = fields.map(fieldName => ({field: fieldName}));
|
|
|
|
eventView.sorts = decodeSorts(location).filter(sort => fields.includes(sort.field));
|
|
|
|
useEffect(() => {
|
|
setError('');
|
|
}, [eventView.query]);
|
|
|
|
if (!eventView.sorts.length) {
|
|
eventView.sorts = [{field: 'timestamp', kind: 'desc'}];
|
|
}
|
|
|
|
eventView.statsPeriod = '90d';
|
|
|
|
const isRegressionIssue =
|
|
group.issueType === IssueType.PERFORMANCE_DURATION_REGRESSION ||
|
|
group.issueType === IssueType.PERFORMANCE_ENDPOINT_REGRESSION;
|
|
|
|
let idQuery = `issue.id:${issueId}`;
|
|
if (group.issueCategory === IssueCategory.PERFORMANCE && !groupIsOccurrenceBacked) {
|
|
idQuery = `performance.issue_ids:${issueId} event.type:transaction`;
|
|
} else if (isRegressionIssue && groupIsOccurrenceBacked) {
|
|
const {transaction, aggregateRange2, breakpoint} =
|
|
data?.occurrence?.evidenceData ?? {};
|
|
|
|
// Surface the "bad" events that occur after the breakpoint
|
|
idQuery = getSampleEventQuery({
|
|
transaction,
|
|
durationBaseline: aggregateRange2,
|
|
addUpperBound: false,
|
|
});
|
|
|
|
eventView.dataset = DiscoverDatasets.DISCOVER;
|
|
eventView.start = new Date(breakpoint * 1000).toISOString();
|
|
eventView.end = new Date(now).toISOString();
|
|
eventView.statsPeriod = undefined;
|
|
}
|
|
eventView.project = [parseInt(group.project.id, 10)];
|
|
eventView.query = `${idQuery} ${props.location.query.query || ''}`;
|
|
|
|
if (error || isLoadingError) {
|
|
return (
|
|
<LoadingError message={error || isLoadingError} onRetry={() => setError('')} />
|
|
);
|
|
}
|
|
|
|
return (
|
|
<EventsTable
|
|
eventView={eventView}
|
|
location={location}
|
|
issueId={issueId}
|
|
isRegressionIssue={isRegressionIssue}
|
|
organization={organization}
|
|
routes={routes}
|
|
excludedTags={excludedTags}
|
|
projectSlug={group.project.slug}
|
|
customColumns={['minidump']}
|
|
setError={(msg: string | undefined) => setError(msg ?? '')}
|
|
transactionName=""
|
|
columnTitles={columnTitles.slice()}
|
|
referrer="api.issues.issue_events"
|
|
isEventLoading={queryEnabled ? isLoading : false}
|
|
/>
|
|
);
|
|
}
|
|
|
|
type ColumnInfo = {columnTitles: string[]; fields: string[]};
|
|
|
|
const getColumns = (group: Group, organization: Organization): ColumnInfo => {
|
|
const isPerfIssue = group.issueCategory === IssueCategory.PERFORMANCE;
|
|
const isReplayEnabled =
|
|
organization.features.includes('session-replay') &&
|
|
projectCanLinkToReplay(group.project);
|
|
|
|
// profiles only exist on transactions, so this only works with
|
|
// performance issues, and not errors
|
|
const isProfilingEnabled = isPerfIssue && organization.features.includes('profiling');
|
|
|
|
const {fields: platformSpecificFields, columnTitles: platformSpecificColumnTitles} =
|
|
getPlatformColumns(group.project.platform ?? group.platform, {
|
|
isProfilingEnabled,
|
|
isReplayEnabled,
|
|
});
|
|
|
|
const fields: string[] = [
|
|
'id',
|
|
'transaction',
|
|
'title',
|
|
'release',
|
|
'environment',
|
|
'user.display',
|
|
'device',
|
|
'os',
|
|
...platformSpecificFields,
|
|
...(isPerfIssue ? ['transaction.duration'] : []),
|
|
'timestamp',
|
|
];
|
|
|
|
const columnTitles: string[] = [
|
|
t('event id'),
|
|
t('transaction'),
|
|
t('title'),
|
|
t('release'),
|
|
t('environment'),
|
|
t('user'),
|
|
t('device'),
|
|
t('os'),
|
|
...platformSpecificColumnTitles,
|
|
...(isPerfIssue ? [t('total duration')] : []),
|
|
t('timestamp'),
|
|
t('minidump'),
|
|
];
|
|
|
|
return {
|
|
fields,
|
|
columnTitles,
|
|
};
|
|
};
|
|
|
|
const getPlatformColumns = (
|
|
platform: PlatformKey | undefined,
|
|
options: {isProfilingEnabled: boolean; isReplayEnabled: boolean}
|
|
): ColumnInfo => {
|
|
const backendServerlessColumnInfo = {
|
|
fields: ['url', 'runtime'],
|
|
columnTitles: [t('url'), t('runtime')],
|
|
};
|
|
|
|
const categoryToColumnMap: Record<PlatformCategory, ColumnInfo> = {
|
|
[PlatformCategory.BACKEND]: backendServerlessColumnInfo,
|
|
[PlatformCategory.SERVERLESS]: backendServerlessColumnInfo,
|
|
[PlatformCategory.FRONTEND]: {
|
|
fields: ['url', 'browser'],
|
|
columnTitles: [t('url'), t('browser')],
|
|
},
|
|
[PlatformCategory.MOBILE]: {
|
|
fields: ['url'],
|
|
columnTitles: [t('url')],
|
|
},
|
|
[PlatformCategory.DESKTOP]: {
|
|
fields: [],
|
|
columnTitles: [],
|
|
},
|
|
[PlatformCategory.OTHER]: {
|
|
fields: [],
|
|
columnTitles: [],
|
|
},
|
|
};
|
|
|
|
const platformCategory = platformToCategory(platform);
|
|
const platformColumns = categoryToColumnMap[platformCategory];
|
|
|
|
if (options.isReplayEnabled) {
|
|
platformColumns.fields.push('replayId');
|
|
platformColumns.columnTitles.push(t('replay'));
|
|
}
|
|
|
|
if (options.isProfilingEnabled && platform && PROFILING_PLATFORMS.includes(platform)) {
|
|
platformColumns.columnTitles.push(t('profile'));
|
|
platformColumns.fields.push('profile.id');
|
|
}
|
|
|
|
return platformColumns;
|
|
};
|
|
|
|
export default AllEventsTable;
|