import {Fragment, useMemo} from 'react';
import styled from '@emotion/styled';
import {CodeSnippet} from 'sentry/components/codeSnippet';
import LoadingIndicator from 'sentry/components/loadingIndicator';
import {space} from 'sentry/styles/space';
import {
MissingFrame,
StackTraceMiniFrame,
} from 'sentry/views/starfish/components/stackTraceMiniFrame';
import {useFullSpanFromTrace} from 'sentry/views/starfish/queries/useFullSpanFromTrace';
import {useIndexedSpans} from 'sentry/views/starfish/queries/useIndexedSpans';
import type {SpanIndexedFieldTypes} from 'sentry/views/starfish/types';
import {SpanIndexedField} from 'sentry/views/starfish/types';
import {SQLishFormatter} from 'sentry/views/starfish/utils/sqlish/SQLishFormatter';
interface Props {
groupId: SpanIndexedFieldTypes[SpanIndexedField.SPAN_GROUP];
op: SpanIndexedFieldTypes[SpanIndexedField.SPAN_OP];
preliminaryDescription?: string;
}
export function SpanDescription(props: Props) {
const {op, preliminaryDescription} = props;
if (op.startsWith('db')) {
return ;
}
return {preliminaryDescription ?? ''};
}
const formatter = new SQLishFormatter();
export function DatabaseSpanDescription({
groupId,
preliminaryDescription,
}: Omit) {
// TODO: we're using all SpanIndexedFields here, but maybe we should only use what we need?
// Truncate to 20 fields otherwise discover will complain.
const fields = Object.values(SpanIndexedField);
const {data: indexedSpans, isFetching: areIndexedSpansLoading} = useIndexedSpans({
filters: {'span.group': groupId},
sorts: [INDEXED_SPAN_SORT],
limit: 1,
fields,
referrer: 'api.starfish.span-description',
});
const indexedSpan = indexedSpans?.[0];
// NOTE: We only need this for `span.data`! If this info existed in indexed spans, we could skip it
const {data: rawSpan, isFetching: isRawSpanLoading} = useFullSpanFromTrace(
groupId,
[INDEXED_SPAN_SORT],
Boolean(indexedSpan)
);
const rawDescription =
rawSpan?.description || indexedSpan?.['span.description'] || preliminaryDescription;
const formatterDescription = useMemo(() => {
return formatter.toString(rawDescription ?? '');
}, [rawDescription]);
return (
{areIndexedSpansLoading ? (
) : (
{formatterDescription}
)}
{!areIndexedSpansLoading && !isRawSpanLoading && (
{rawSpan?.data?.['code.filepath'] ? (
) : (
)}
)}
);
}
const INDEXED_SPAN_SORT = {
field: 'span.self_time',
kind: 'desc' as const,
};
const Frame = styled('div')`
border: solid 1px ${p => p.theme.border};
border-radius: ${p => p.theme.borderRadius};
overflow: hidden;
`;
const WithPadding = styled('div')`
display: flex;
padding: ${space(1)} ${space(2)};
`;
const WordBreak = styled('div')`
word-break: break-word;
`;