123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188 |
- import type {Location} from 'history';
- import GridEditable, {
- COL_WIDTH_UNDEFINED,
- type GridColumnHeader,
- } from 'sentry/components/gridEditable';
- import Link from 'sentry/components/links/link';
- import {t} from 'sentry/locale';
- import type {Organization} from 'sentry/types/organization';
- import EventView, {type EventsMetaType} from 'sentry/utils/discover/eventView';
- import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
- import type {Sort} from 'sentry/utils/discover/fields';
- import {generateLinkToEventInTraceView} from 'sentry/utils/discover/urls';
- import {VisuallyCompleteWithData} from 'sentry/utils/performanceForSentry';
- import {decodeScalar, decodeSorts} from 'sentry/utils/queryString';
- import {MutableSearch} from 'sentry/utils/tokenizeSearch';
- import {useLocation} from 'sentry/utils/useLocation';
- import useOrganization from 'sentry/utils/useOrganization';
- import {renderHeadCell} from 'sentry/views/insights/common/components/tableCells/renderHeadCell';
- import {useSpansIndexed} from 'sentry/views/insights/common/queries/useDiscover';
- import {QueryParameterNames} from 'sentry/views/insights/common/views/queryParameters';
- import {SpanIndexedField} from 'sentry/views/insights/types';
- type Column = GridColumnHeader<
- | SpanIndexedField.ID
- | SpanIndexedField.SPAN_DURATION
- | SpanIndexedField.TIMESTAMP
- | SpanIndexedField.USER
- >;
- const COLUMN_ORDER: Column[] = [
- {
- key: SpanIndexedField.ID,
- name: t('Span ID'),
- width: COL_WIDTH_UNDEFINED,
- },
- {
- key: SpanIndexedField.USER,
- name: t('User'),
- width: COL_WIDTH_UNDEFINED,
- },
- {
- key: SpanIndexedField.TIMESTAMP,
- name: t('Timestamp'),
- width: COL_WIDTH_UNDEFINED,
- },
- {
- key: SpanIndexedField.SPAN_DURATION,
- name: t('Total duration'),
- width: 150,
- },
- ];
- const SORTABLE_FIELDS = [
- SpanIndexedField.ID,
- SpanIndexedField.SPAN_DURATION,
- SpanIndexedField.TIMESTAMP,
- ];
- type ValidSort = Sort & {
- field:
- | SpanIndexedField.ID
- | SpanIndexedField.SPAN_DURATION
- | SpanIndexedField.TIMESTAMP;
- };
- export function isAValidSort(sort: Sort): sort is ValidSort {
- return (SORTABLE_FIELDS as unknown as string[]).includes(sort.field);
- }
- interface Props {
- groupId: string;
- }
- export function PipelineSpansTable({groupId}: Props) {
- const location = useLocation();
- const organization = useOrganization();
- const sortField = decodeScalar(location.query?.[QueryParameterNames.SPANS_SORT]);
- let sort = decodeSorts(sortField).filter(isAValidSort)[0];
- if (!sort) {
- sort = {field: SpanIndexedField.TIMESTAMP, kind: 'desc'};
- }
- const {
- data: rawData,
- meta: rawMeta,
- error,
- isPending,
- } = useSpansIndexed(
- {
- limit: 30,
- sorts: [sort],
- fields: [
- SpanIndexedField.ID,
- SpanIndexedField.TRACE,
- SpanIndexedField.SPAN_DURATION,
- SpanIndexedField.TRANSACTION_ID,
- SpanIndexedField.USER,
- SpanIndexedField.TIMESTAMP,
- SpanIndexedField.PROJECT,
- ],
- search: new MutableSearch(`span.category:ai.pipeline span.group:"${groupId}"`),
- },
- 'api.ai-pipelines.view'
- );
- const data = rawData || [];
- const meta = rawMeta as EventsMetaType;
- return (
- <VisuallyCompleteWithData
- id="PipelineSpansTable"
- hasData={data.length > 0}
- isLoading={isPending}
- >
- <GridEditable
- isLoading={isPending}
- error={error}
- data={data}
- columnOrder={COLUMN_ORDER}
- columnSortBy={[
- {
- key: sort.field,
- order: sort.kind,
- },
- ]}
- grid={{
- renderHeadCell: column =>
- renderHeadCell({
- column,
- sort,
- location,
- sortParameterName: QueryParameterNames.SPANS_SORT,
- }),
- renderBodyCell: (column, row) =>
- renderBodyCell(column, row, meta, location, organization),
- }}
- />
- </VisuallyCompleteWithData>
- );
- }
- function renderBodyCell(
- column: Column,
- row: any,
- meta: EventsMetaType | undefined,
- location: Location,
- organization: Organization
- ) {
- if (column.key === SpanIndexedField.ID) {
- if (!row[SpanIndexedField.ID]) {
- return <span>(unknown)</span>;
- }
- if (!row[SpanIndexedField.TRACE]) {
- return <span>{row[SpanIndexedField.ID]}</span>;
- }
- return (
- <Link
- to={generateLinkToEventInTraceView({
- organization,
- eventId: row[SpanIndexedField.TRANSACTION_ID],
- projectSlug: row[SpanIndexedField.PROJECT],
- traceSlug: row[SpanIndexedField.TRACE],
- timestamp: row[SpanIndexedField.TIMESTAMP],
- location,
- eventView: EventView.fromLocation(location),
- spanId: row[SpanIndexedField.ID],
- })}
- >
- {row[SpanIndexedField.ID]}
- </Link>
- );
- }
- if (!meta || !meta?.fields) {
- return row[column.key];
- }
- const renderer = getFieldRenderer(column.key, meta.fields, false);
- const rendered = renderer(row, {
- location,
- organization,
- unit: meta.units?.[column.key],
- });
- return rendered;
- }
|