Skip to content
53 changes: 53 additions & 0 deletions static/app/views/dashboards/datasetConfig/spans.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import {
isEquation,
isEquationAlias,
type Aggregation,
type AggregationOutputType,
type DataUnit,
type QueryFieldValue,
} from 'sentry/utils/discover/fields';
import {
Expand Down Expand Up @@ -238,6 +240,57 @@ export const SpansConfig: DatasetConfig<
}
return getFieldRenderer(field, meta, false, widget, dashboardFilters);
},
getSeriesResultUnit: (data, widgetQuery) => {
const resultUnits: Record<string, DataUnit> = {};
widgetQuery.fieldMeta?.forEach((meta, index) => {
if (meta && widgetQuery.fields) {
resultUnits[widgetQuery.fields[index]!] = meta.valueUnit;
}
});
const isMultiSeriesStats = isMultiSeriesEventsStats(data);

// if there's only one aggregate and more then one group by the series names are the name of the group, not the aggregate name
// But we can just assume the units is for all the series
// TODO: This doesn't work with multiple aggregates
const firstMeta = widgetQuery.fieldMeta?.find(meta => meta !== null);
if (
isMultiSeriesStats &&
firstMeta &&
widgetQuery.aggregates?.length === 1 &&
widgetQuery.columns?.length > 0
) {
Object.keys(data).forEach(seriesName => {
resultUnits[seriesName] = firstMeta.valueUnit;
});
}
return resultUnits;
},
getSeriesResultType: (data, widgetQuery) => {
const resultTypes: Record<string, AggregationOutputType> = {};
widgetQuery.fieldMeta?.forEach((meta, index) => {
if (meta && widgetQuery.fields) {
resultTypes[widgetQuery.fields[index]!] = meta.valueType as AggregationOutputType;
}
});

const isMultiSeriesStats = isMultiSeriesEventsStats(data);

// if there's only one aggregate and more then one group by the series names are the name of the group, not the aggregate name
// But we can just assume the units is for all the series
// TODO: This doesn't work with multiple aggregates
const firstMeta = widgetQuery.fieldMeta?.find(meta => meta !== null);
if (
isMultiSeriesStats &&
firstMeta &&
widgetQuery.aggregates?.length === 1 &&
widgetQuery.columns?.length > 0
) {
Object.keys(data).forEach(seriesName => {
resultTypes[seriesName] = firstMeta.valueType as AggregationOutputType;
});
}
return resultTypes;
},
};

function getPrimaryFieldOptions(
Expand Down
3 changes: 3 additions & 0 deletions static/app/views/dashboards/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {Tag} from 'sentry/types/group';
import type {User} from 'sentry/types/user';
import {SavedQueryDatasets, type DatasetSource} from 'sentry/utils/discover/types';
import type {PrebuiltDashboardId} from 'sentry/views/dashboards/utils/prebuiltConfigs';
import type {TimeSeriesMeta} from 'sentry/views/dashboards/widgets/common/types';

import type {ThresholdsConfig} from './widgetBuilder/buildSteps/thresholdsStep/thresholds';

Expand Down Expand Up @@ -95,6 +96,8 @@ export type WidgetQuery = {
// Table column alias.
// We may want to have alias for y-axis in the future too
fieldAliases?: string[];
// Used to define the units of the fields in the widget queries, currently not saved
fieldMeta?: Array<Pick<TimeSeriesMeta, 'valueType' | 'valueUnit'> | null>;
// Fields is replaced with aggregates + columns. It
// is currently used to track column order on table
// widgets.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ const FIRST_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
`count(${SpanFields.SPAN_DURATION})`,
`equation|count_if(${SpanFields.TRACE_STATUS},equals,internal_error) / count(${SpanFields.SPAN_DURATION})`,
],
fieldMeta: [null, {valueType: 'percentage', valueUnit: null}],
columns: [],
fieldAliases: [],
conditions: `${SpanFields.SPAN_OP}:http.server`,
Expand Down Expand Up @@ -129,6 +130,7 @@ const SECOND_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
`equation|count_if(${SpanFields.TRACE_STATUS},equals,internal_error) / count(${SpanFields.SPAN_DURATION})`,
],
columns: [],
fieldMeta: [null, {valueType: 'percentage', valueUnit: null}],
fieldAliases: [],
conditions: `${SpanFields.SPAN_OP}:queue.process`,
orderby: `count(${SpanFields.SPAN_DURATION})`,
Expand Down Expand Up @@ -171,13 +173,13 @@ const SECOND_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
{
name: '',
fields: [
SpanFields.TRANSACTION,
`equation|count_if(${SpanFields.CACHE_HIT},equals,false) / count(${SpanFields.SPAN_DURATION})`,
],
aggregates: [
`equation|count_if(${SpanFields.CACHE_HIT},equals,false) / count(${SpanFields.SPAN_DURATION})`,
],
columns: [SpanFields.TRANSACTION],
fieldMeta: [{valueType: 'percentage', valueUnit: null}],
fieldAliases: [''],
conditions: `${SpanFields.SPAN_OP}:[cache.get,cache.get_item]`,
orderby: `-equation|count_if(${SpanFields.CACHE_HIT},equals,false) / count(${SpanFields.SPAN_DURATION})`,
Expand Down Expand Up @@ -211,6 +213,7 @@ const THIRD_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
`equation|count_if(${SpanFields.TRACE_STATUS},equals,internal_error) / count(${SpanFields.SPAN_DURATION})`,
],
columns: [],
fieldMeta: [null, {valueType: 'percentage', valueUnit: null}],
fieldAliases: ['Jobs', 'Error Rate'],
conditions: `${SpanFields.SPAN_OP}:queue.process`,
orderby: `-count(${SpanFields.SPAN_DURATION})`,
Expand Down Expand Up @@ -271,6 +274,7 @@ const THIRD_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
`equation|count_if(${SpanFields.CACHE_HIT},equals,false) / count(${SpanFields.SPAN_DURATION})`,
],
columns: [SpanFields.TRANSACTION],
fieldMeta: [null, null, null, {valueType: 'percentage', valueUnit: null}],
fieldAliases: ['', 'Cache Misses', 'Cache Calls', 'Cache Miss Rate'],
conditions: `${SpanFields.SPAN_OP}:[cache.get,cache.get_item]`,
orderby: '-equation[1]',
Expand Down Expand Up @@ -329,6 +333,16 @@ const TRANSACTIONS_TABLE: Widget = {
'Users',
'Time Spent',
],
fieldMeta: [
null,
null,
null,
null,
null,
null,
null,
{valueType: 'percentage', valueUnit: null},
],
conditions: TABLE_QUERY.formatString(),
orderby: '-sum(span.duration)',
linkedDashboards: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ const BIG_NUMBER_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
aggregates: [PERCENTAGE_3XX],
columns: [],
orderby: PERCENTAGE_3XX,
fieldMeta: [{valueType: 'percentage', valueUnit: null}],
},
],
},
Expand All @@ -129,6 +130,7 @@ const BIG_NUMBER_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
aggregates: [PERCENTAGE_4XX],
columns: [],
orderby: PERCENTAGE_4XX,
fieldMeta: [{valueType: 'percentage', valueUnit: null}],
},
],
},
Expand All @@ -146,6 +148,7 @@ const BIG_NUMBER_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
aggregates: [PERCENTAGE_5XX],
columns: [],
orderby: PERCENTAGE_5XX,
fieldMeta: [{valueType: 'percentage', valueUnit: null}],
},
],
},
Expand Down Expand Up @@ -221,6 +224,7 @@ const CHART_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
aggregates: [PERCENTAGE_3XX],
columns: [],
orderby: PERCENTAGE_3XX,
fieldMeta: [{valueType: 'percentage', valueUnit: null}],
},
{
name: '4XX',
Expand All @@ -229,6 +233,7 @@ const CHART_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
aggregates: [PERCENTAGE_4XX],
columns: [],
orderby: PERCENTAGE_4XX,
fieldMeta: [{valueType: 'percentage', valueUnit: null}],
},
{
name: '5XX',
Expand All @@ -237,6 +242,7 @@ const CHART_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
aggregates: [PERCENTAGE_5XX],
columns: [],
orderby: PERCENTAGE_5XX,
fieldMeta: [{valueType: 'percentage', valueUnit: null}],
},
],
},
Expand Down Expand Up @@ -279,6 +285,13 @@ const TRANSACTIONS_TABLE: Widget = {
DataTitles.avg,
DataTitles.timeSpent,
],
fieldMeta: [
null,
null,
{valueType: 'percentage', valueUnit: null},
{valueType: 'percentage', valueUnit: null},
{valueType: 'percentage', valueUnit: null},
],
conditions: FILTER_STRING,
name: '',
orderby: '-sum(span.self_time)',
Expand Down
11 changes: 11 additions & 0 deletions static/app/views/dashboards/utils/prebuiltConfigs/http/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const FIRST_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
fields: [PERCENTAGE_3XX],
aggregates: [PERCENTAGE_3XX],
columns: [],
fieldMeta: [{valueType: 'percentage', valueUnit: null}],
orderby: PERCENTAGE_3XX,
},
{
Expand All @@ -79,6 +80,7 @@ const FIRST_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
fields: [PERCENTAGE_4XX],
aggregates: [PERCENTAGE_4XX],
columns: [],
fieldMeta: [{valueType: 'percentage', valueUnit: null}],
orderby: PERCENTAGE_4XX,
},
{
Expand All @@ -87,6 +89,7 @@ const FIRST_ROW_WIDGETS: Widget[] = spaceWidgetsEquallyOnRow(
fields: [PERCENTAGE_5XX],
aggregates: [PERCENTAGE_5XX],
columns: [],
fieldMeta: [{valueType: 'percentage', valueUnit: null}],
orderby: PERCENTAGE_5XX,
},
],
Expand Down Expand Up @@ -139,6 +142,14 @@ const DOMAIN_TABLE: Widget = {
staticDashboardId: 5,
},
],
fieldMeta: [
null,
null,
null,
{valueType: 'percentage', valueUnit: null},
{valueType: 'percentage', valueUnit: null},
{valueType: 'percentage', valueUnit: null},
],
conditions: FILTER_STRING,
name: '',
orderby: '-sum(span.self_time)',
Expand Down
30 changes: 28 additions & 2 deletions static/app/views/dashboards/widgetCard/genericWidgetQueries.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,18 @@ class GenericWidgetQueries<SeriesResponse, TableResponse> extends Component<
) as TableDataWithTitle;
transformedData.title = widget.queries[i]?.name ?? '';

const meta = transformedData.meta;
const fieldMeta = widget?.queries?.[i]?.fieldMeta;
if (fieldMeta && meta) {
fieldMeta.forEach((m, index) => {
const field = widget.queries?.[i]?.fields?.[index];
if (m && field) {
meta.units![field] = m.valueUnit ?? '';
meta.fields![field] = m.valueType;
}
});
}

// Overwrite the local var to work around state being stale in tests.
transformedTableResults = [...transformedTableResults, transformedData];

Expand Down Expand Up @@ -390,14 +402,28 @@ class GenericWidgetQueries<SeriesResponse, TableResponse> extends Component<
// to derive the types and units since they share the same aggregations and fields
const timeseriesResultsTypes = responses.reduce(
(acc, response) => {
acc = {...acc, ...config.getSeriesResultType?.(response[0], widget.queries[0]!)};
let allResultTypes: Record<string, AggregationOutputType> = {};
widget.queries.forEach(query => {
allResultTypes = {
...allResultTypes,
...config.getSeriesResultType?.(response[0], query),
};
});
acc = {...acc, ...allResultTypes};
return acc;
},
{} as Record<string, AggregationOutputType>
);
const timeseriesResultsUnits = responses.reduce(
(acc, response) => {
acc = {...acc, ...config.getSeriesResultUnit?.(response[0], widget.queries[0]!)};
let allResultUnits: Record<string, DataUnit> = {};
widget.queries.forEach(query => {
allResultUnits = {
...allResultUnits,
...config.getSeriesResultUnit?.(response[0], query),
};
});
acc = {...acc, ...allResultUnits};
return acc;
},
{} as Record<string, DataUnit>
Expand Down
Loading