import {Fragment} from 'react'; import * as Sentry from '@sentry/react'; import DateTime from 'sentry/components/dateTime'; import GridEditable, { COL_WIDTH_UNDEFINED, GridColumnOrder, } from 'sentry/components/gridEditable'; import ProjectBadge from 'sentry/components/idBadge/projectBadge'; import Link from 'sentry/components/links/link'; import PerformanceDuration from 'sentry/components/performanceDuration'; import {IconCheckmark, IconClose} from 'sentry/icons'; import {t} from 'sentry/locale'; import {Trace} from 'sentry/types/profiling/core'; import {defined} from 'sentry/utils'; import {Container, NumberContainer} from 'sentry/utils/discover/styles'; import {getShortEventId} from 'sentry/utils/events'; import { generateProfileFlamechartRoute, generateProfileSummaryRouteWithQuery, } from 'sentry/utils/profiling/routes'; import {renderTableHead} from 'sentry/utils/profiling/tableRenderer'; import {useLocation} from 'sentry/utils/useLocation'; import useOrganization from 'sentry/utils/useOrganization'; import useProjects from 'sentry/utils/useProjects'; const REQUIRE_PROJECT_COLUMNS: Set = new Set([ 'id', 'project_id', 'transaction_name', ]); interface ProfilesTableProps { error: string | null; isLoading: boolean; traces: Trace[]; columnOrder?: Readonly; } function ProfilesTable(props: ProfilesTableProps) { const location = useLocation(); return ( COLUMNS[key])} columnSortBy={[]} grid={{ renderHeadCell: renderTableHead({rightAlignedColumns: RIGHT_ALIGNED_COLUMNS}), renderBodyCell: renderProfilesTableCell, }} location={location} /> ); } const RIGHT_ALIGNED_COLUMNS = new Set(['trace_duration_ms']); function renderProfilesTableCell( column: TableColumn, dataRow: TableDataRow, rowIndex: number, columnIndex: number ) { return ( ); } interface ProfilesTableCellProps { column: TableColumn; columnIndex: number; dataRow: TableDataRow; rowIndex: number; } function ProfilesTableCell({column, dataRow}: ProfilesTableCellProps) { const organization = useOrganization(); const {projects} = useProjects(); const location = useLocation(); // Not all columns need the project, so small optimization to avoid // the linear lookup for every cell. const project = REQUIRE_PROJECT_COLUMNS.has(column.key) ? projects.find(proj => proj.id === dataRow.project_id) : undefined; if (REQUIRE_PROJECT_COLUMNS.has(column.key) && !defined(project)) { Sentry.withScope(scope => { scope.setFingerprint(['profiles table', 'cell', 'missing project']); scope.setTag('cell_key', column.key); scope.setTag('missing_project', dataRow.project_id); scope.setTag('available_project', projects.length); Sentry.captureMessage(`Project ${dataRow.project_id} missing for ${column.key}`); }); } const value = dataRow[column.key]; switch (column.key) { case 'id': if (!defined(project)) { // should never happen but just in case return {getShortEventId(dataRow.id)}; } const flamegraphTarget = generateProfileFlamechartRoute({ orgSlug: organization.slug, projectSlug: project.slug, profileId: dataRow.id, }); return ( {getShortEventId(dataRow.id)} ); case 'project_id': if (!defined(project)) { // should never happen but just in case return {t('n/a')}; } return ( ); case 'transaction_name': if (!defined(project)) { // should never happen but just in case return {t('n/a')}; } const profileSummaryTarget = generateProfileSummaryRouteWithQuery({ query: location.query, orgSlug: organization.slug, projectSlug: project.slug, transaction: dataRow.transaction_name, }); return ( {value} ); case 'version_name': return ( {dataRow.version_code ? t('%s (build %s)', value, dataRow.version_code) : value} ); case 'failed': return ( {value ? ( ) : ( )} ); case 'timestamp': return ( ); case 'trace_duration_ms': return ( ); default: return {value}; } } type TableColumnKey = keyof Trace; type NonTableColumnKey = | 'version_code' | 'device_locale' | 'device_manufacturer' | 'backtrace_available' | 'error_code' | 'error_code_name' | 'error_description' | 'span_annotations' | 'spans' | 'trace_annotations'; type TableColumn = GridColumnOrder; type TableDataRow = Omit, NonTableColumnKey> & Partial>; type TableColumnOrders = Omit, NonTableColumnKey>; const DEFAULT_COLUMN_ORDER: TableColumnKey[] = [ 'failed', 'id', 'project_id', 'transaction_name', 'version_name', 'timestamp', 'trace_duration_ms', 'device_model', 'device_classification', ]; const COLUMNS: TableColumnOrders = { id: { key: 'id', name: t('Profile ID'), width: COL_WIDTH_UNDEFINED, }, project_id: { key: 'project_id', name: t('Project'), width: COL_WIDTH_UNDEFINED, }, failed: { key: 'failed', name: t('Status'), width: 14, // make this as small as possible }, version_name: { key: 'version_name', name: t('Version'), width: COL_WIDTH_UNDEFINED, }, transaction_name: { key: 'transaction_name', name: t('Transaction Name'), width: COL_WIDTH_UNDEFINED, }, timestamp: { key: 'timestamp', name: t('Timestamp'), width: COL_WIDTH_UNDEFINED, }, trace_duration_ms: { key: 'trace_duration_ms', name: t('Duration'), width: COL_WIDTH_UNDEFINED, }, device_model: { key: 'device_model', name: t('Device Model'), width: COL_WIDTH_UNDEFINED, }, device_classification: { key: 'device_classification', name: t('Device Classification'), width: COL_WIDTH_UNDEFINED, }, device_os_version: { key: 'device_os_version', name: t('Device OS Version'), width: COL_WIDTH_UNDEFINED, }, }; export {ProfilesTable};