Files
2024-02-15 17:01:50 +00:00

138 lines
4.0 KiB
TypeScript

import {Fragment, useCallback, useMemo, useState} from 'react';
import {browserHistory} from 'react-router';
import styled from '@emotion/styled';
import {CompactSelect} from 'sentry/components/compactSelect';
import Pagination from 'sentry/components/pagination';
import {FunctionsTable} from 'sentry/components/profiling/suspectFunctions/functionsTable';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {Project} from 'sentry/types';
import {useProfileFunctions} from 'sentry/utils/profiling/hooks/useProfileFunctions';
import {formatSort} from 'sentry/utils/profiling/hooks/utils';
import {decodeScalar} from 'sentry/utils/queryString';
import {MutableSearch} from 'sentry/utils/tokenizeSearch';
import {useLocation} from 'sentry/utils/useLocation';
interface SuspectFunctionsTableProps {
analyticsPageSource: 'performance_transaction' | 'profiling_transaction';
project: Project | undefined;
transaction: string;
}
const FUNCTIONS_CURSOR_NAME = 'functionsCursor';
export function SuspectFunctionsTable({
analyticsPageSource,
project,
transaction,
}: SuspectFunctionsTableProps) {
const [functionType, setFunctionType] = useState<'application' | 'system' | 'all'>(
'application'
);
const location = useLocation();
const functionsCursor = useMemo(
() => decodeScalar(location.query[FUNCTIONS_CURSOR_NAME]),
[location.query]
);
const functionsSort = useMemo(
() =>
formatSort<FunctionsField>(
decodeScalar(location.query.functionsSort),
functionsFields,
{
key: 'sum()',
order: 'desc',
}
),
[location.query.functionsSort]
);
const handleFunctionsCursor = useCallback((cursor, pathname, query) => {
browserHistory.push({
pathname,
query: {...query, [FUNCTIONS_CURSOR_NAME]: cursor},
});
}, []);
const query = useMemo(() => {
const conditions = new MutableSearch('');
conditions.setFilterValues('transaction', [transaction]);
if (functionType === 'application') {
conditions.setFilterValues('is_application', ['1']);
} else if (functionType === 'system') {
conditions.setFilterValues('is_application', ['0']);
}
return conditions.formatString();
}, [functionType, transaction]);
const functionsQuery = useProfileFunctions<FunctionsField>({
fields: functionsFields,
referrer: 'api.profiling.profile-summary-functions-table',
sort: functionsSort,
query,
limit: 5,
cursor: functionsCursor,
});
return (
<Fragment>
<TableHeader>
<CompactSelect
triggerProps={{prefix: t('Slowest Functions'), size: 'xs'}}
value={functionType}
options={[
{
label: t('All'),
value: 'all' as const,
},
{
label: t('Application'),
value: 'application' as const,
},
{
label: t('System'),
value: 'system' as const,
},
]}
onChange={({value}) => setFunctionType(value)}
/>
<StyledPagination
pageLinks={functionsQuery.getResponseHeader?.('Link')}
onCursor={handleFunctionsCursor}
size="xs"
/>
</TableHeader>
<FunctionsTable
analyticsPageSource={analyticsPageSource}
error={functionsQuery.isError ? functionsQuery.error.message : null}
isLoading={functionsQuery.isLoading}
functions={functionsQuery.isFetched ? functionsQuery.data?.data ?? [] : []}
project={project}
sort={functionsSort}
/>
</Fragment>
);
}
const functionsFields = [
'package',
'function',
'count()',
'p75()',
'sum()',
'examples()',
] as const;
type FunctionsField = (typeof functionsFields)[number];
const TableHeader = styled('div')`
display: flex;
justify-content: space-between;
margin-bottom: ${space(1)};
`;
const StyledPagination = styled(Pagination)`
margin: 0 0 0 ${space(1)};
`;