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

262 lines
8.5 KiB
TypeScript

import trimStart from 'lodash/trimStart';
import type {Client, ResponseMeta} from 'sentry/api';
import type {SearchBarProps} from 'sentry/components/events/searchBar';
import type {Organization, PageFilters, SelectValue, TagCollection} from 'sentry/types';
import type {Series} from 'sentry/types/echarts';
import type {CustomMeasurementCollection} from 'sentry/utils/customMeasurements/customMeasurements';
import type {TableData} from 'sentry/utils/discover/discoverQuery';
import type {MetaType} from 'sentry/utils/discover/eventView';
import type {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
import type {AggregationOutputType, QueryFieldValue} from 'sentry/utils/discover/fields';
import {isEquation} from 'sentry/utils/discover/fields';
import type {MEPState} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
import type {OnDemandControlContext} from 'sentry/utils/performance/contexts/onDemandControl';
import type {FieldValueOption} from 'sentry/views/discover/table/queryField';
import type {FieldValue} from 'sentry/views/discover/table/types';
import type {DisplayType, Widget, WidgetQuery} from '../types';
import {WidgetType} from '../types';
import {getNumEquations} from '../utils';
import {ErrorsAndTransactionsConfig} from './errorsAndTransactions';
import {IssuesConfig} from './issues';
import {ReleasesConfig} from './releases';
export type WidgetBuilderSearchBarProps = {
getFilterWarning: SearchBarProps['getFilterWarning'];
onClose: SearchBarProps['onClose'];
onSearch: SearchBarProps['onSearch'];
organization: Organization;
pageFilters: PageFilters;
widgetQuery: WidgetQuery;
};
export interface DatasetConfig<SeriesResponse, TableResponse> {
/**
* Dataset specific search bar for the 'Filter' step in the
* widget builder.
*/
SearchBar: (props: WidgetBuilderSearchBarProps) => JSX.Element;
/**
* Default query to display when dataset is selected in the
* Widget Builder.
*/
defaultWidgetQuery: WidgetQuery;
enableEquations: boolean;
/**
* Field options to display in the Column selectors for
* Table display type.
*/
getTableFieldOptions: (
organization: Organization,
tags?: TagCollection,
customMeasurements?: CustomMeasurementCollection,
api?: Client
) => Record<string, SelectValue<FieldValue>>;
/**
* List of supported display types for dataset.
*/
supportedDisplayTypes: DisplayType[];
/**
* Transforms table API results into format that is used by
* table and big number components.
*/
transformTable: (
data: TableResponse,
widgetQuery: WidgetQuery,
organization: Organization,
pageFilters: PageFilters
) => TableData;
/**
* Configure enabling/disabling sort/direction options with an
* optional message for why it is disabled.
*/
disableSortOptions?: (widgetQuery: WidgetQuery) => {
disableSort: boolean;
disableSortDirection: boolean;
disableSortReason?: string;
};
/**
* Filter the options available to the parameters list
* of an aggregate function in QueryField component on the
* Widget Builder.
*/
filterAggregateParams?: (
option: FieldValueOption,
fieldValue?: QueryFieldValue
) => boolean;
/**
* Refine the options available in the sort options for timeseries
* displays on the 'Sort by' step of the Widget Builder.
*/
filterSeriesSortOptions?: (
columns: Set<string>
) => (option: FieldValueOption) => boolean;
/**
* Filter the primary options available in a table widget
* columns on the Widget Builder.
*/
filterTableOptions?: (option: FieldValueOption) => boolean;
/**
* Filter the options available to the parameters list
* of an aggregate function in QueryField component on the
* Widget Builder.
*/
filterYAxisAggregateParams?: (
fieldValue: QueryFieldValue,
displayType: DisplayType
) => (option: FieldValueOption) => boolean;
filterYAxisOptions?: (
displayType: DisplayType
) => (option: FieldValueOption) => boolean;
/**
* Used to select custom renderers for field types.
*/
getCustomFieldRenderer?: (
field: string,
meta: MetaType,
organization?: Organization
) => ReturnType<typeof getFieldRenderer> | null;
/**
* Generate field header used for mapping column
* names to more desirable values in tables.
*/
getFieldHeaderMap?: (widgetQuery?: WidgetQuery) => Record<string, string>;
/**
* Field options to display in the Group by selector.
*/
getGroupByFieldOptions?: (
organization: Organization,
tags?: TagCollection,
customMeasurements?: CustomMeasurementCollection,
api?: Client,
queries?: WidgetQuery[]
) => Record<string, SelectValue<FieldValue>>;
/**
* Generate the request promises for fetching
* series data.
*/
getSeriesRequest?: (
api: Client,
widget: Widget,
queryIndex: number,
organization: Organization,
pageFilters: PageFilters,
onDemandControlContext?: OnDemandControlContext,
referrer?: string,
mepSetting?: MEPState | null
) => Promise<[SeriesResponse, string | undefined, ResponseMeta | undefined]>;
/**
* Get the result type of the series. ie duration, size, percentage, etc
*/
getSeriesResultType?: (
data: SeriesResponse,
widgetQuery: WidgetQuery
) => Record<string, AggregationOutputType>;
/**
* Generate the request promises for fetching
* tabular data.
*/
getTableRequest?: (
api: Client,
widget: Widget,
query: WidgetQuery,
organization: Organization,
pageFilters: PageFilters,
onDemandControlContext?: OnDemandControlContext,
limit?: number,
cursor?: string,
referrer?: string,
mepSetting?: MEPState | null
) => Promise<[TableResponse, string | undefined, ResponseMeta | undefined]>;
/**
* Generate the list of sort options for table
* displays on the 'Sort by' step of the Widget Builder.
*/
getTableSortOptions?: (
organization: Organization,
widgetQuery: WidgetQuery
) => SelectValue<string>[];
/**
* Generate the list of sort options for timeseries
* displays on the 'Sort by' step of the Widget Builder.
*/
getTimeseriesSortOptions?: (
organization: Organization,
widgetQuery: WidgetQuery,
tags?: TagCollection
) => Record<string, SelectValue<FieldValue>>;
/**
* Apply dataset specific overrides to the logic that handles
* column updates for tables in the Widget Builder.
*/
handleColumnFieldChangeOverride?: (widgetQuery: WidgetQuery) => WidgetQuery;
/**
* Called on column or y-axis change in the Widget Builder
* to reset the orderby of the widget query.
*/
handleOrderByReset?: (widgetQuery: WidgetQuery, newFields: string[]) => WidgetQuery;
/**
* Transforms timeseries API results into series data that is
* ingestable by echarts for timeseries visualizations.
*/
transformSeries?: (
data: SeriesResponse,
widgetQuery: WidgetQuery,
organization: Organization
) => Series[];
}
export function getDatasetConfig<T extends WidgetType | undefined>(
widgetType: T
): T extends WidgetType.ISSUE
? typeof IssuesConfig
: T extends WidgetType.RELEASE
? typeof ReleasesConfig
: typeof ErrorsAndTransactionsConfig;
export function getDatasetConfig(
widgetType?: WidgetType
): typeof IssuesConfig | typeof ReleasesConfig | typeof ErrorsAndTransactionsConfig {
switch (widgetType) {
case WidgetType.ISSUE:
return IssuesConfig;
case WidgetType.RELEASE:
return ReleasesConfig;
case WidgetType.DISCOVER:
default:
return ErrorsAndTransactionsConfig;
}
}
/**
* A generic orderby reset helper function that updates the query's
* orderby based on new selected fields.
*/
export function handleOrderByReset(
widgetQuery: WidgetQuery,
newFields: string[]
): WidgetQuery {
const rawOrderby = trimStart(widgetQuery.orderby, '-');
const isDescending = widgetQuery.orderby.startsWith('-');
const orderbyPrefix = isDescending ? '-' : '';
if (!newFields.includes(rawOrderby) && widgetQuery.orderby !== '') {
const isFromAggregates = widgetQuery.aggregates.includes(rawOrderby);
const isCustomEquation = isEquation(rawOrderby);
const isUsedInGrouping = widgetQuery.columns.includes(rawOrderby);
const keepCurrentOrderby = isFromAggregates || isCustomEquation || isUsedInGrouping;
const firstAggregateAlias = isEquation(widgetQuery.aggregates[0] ?? '')
? `equation[${getNumEquations(widgetQuery.aggregates) - 1}]`
: newFields[0];
widgetQuery.orderby =
(keepCurrentOrderby && widgetQuery.orderby) ||
`${orderbyPrefix}${firstAggregateAlias}`;
}
return widgetQuery;
}