base.tsx 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254
  1. import trimStart from 'lodash/trimStart';
  2. import {Client, ResponseMeta} from 'sentry/api';
  3. import {SearchBarProps} from 'sentry/components/events/searchBar';
  4. import {Organization, PageFilters, SelectValue, TagCollection} from 'sentry/types';
  5. import {Series} from 'sentry/types/echarts';
  6. import {CustomMeasurementCollection} from 'sentry/utils/customMeasurements/customMeasurements';
  7. import {TableData} from 'sentry/utils/discover/discoverQuery';
  8. import {MetaType} from 'sentry/utils/discover/eventView';
  9. import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
  10. import {
  11. AggregationOutputType,
  12. isEquation,
  13. QueryFieldValue,
  14. } from 'sentry/utils/discover/fields';
  15. import {MEPState} from 'sentry/utils/performance/contexts/metricsEnhancedSetting';
  16. import {FieldValueOption} from 'sentry/views/discover/table/queryField';
  17. import {FieldValue} from 'sentry/views/discover/table/types';
  18. import {DisplayType, Widget, WidgetQuery, WidgetType} from '../types';
  19. import {getNumEquations} from '../utils';
  20. import {ErrorsAndTransactionsConfig} from './errorsAndTransactions';
  21. import {IssuesConfig} from './issues';
  22. import {ReleasesConfig} from './releases';
  23. export type WidgetBuilderSearchBarProps = {
  24. getFilterWarning: SearchBarProps['getFilterWarning'];
  25. onClose: SearchBarProps['onClose'];
  26. onSearch: SearchBarProps['onSearch'];
  27. organization: Organization;
  28. pageFilters: PageFilters;
  29. widgetQuery: WidgetQuery;
  30. };
  31. export interface DatasetConfig<SeriesResponse, TableResponse> {
  32. /**
  33. * Dataset specific search bar for the 'Filter' step in the
  34. * widget builder.
  35. */
  36. SearchBar: (props: WidgetBuilderSearchBarProps) => JSX.Element;
  37. /**
  38. * Default query to display when dataset is selected in the
  39. * Widget Builder.
  40. */
  41. defaultWidgetQuery: WidgetQuery;
  42. enableEquations: boolean;
  43. /**
  44. * Field options to display in the Column selectors for
  45. * Table display type.
  46. */
  47. getTableFieldOptions: (
  48. organization: Organization,
  49. tags?: TagCollection,
  50. customMeasurements?: CustomMeasurementCollection
  51. ) => Record<string, SelectValue<FieldValue>>;
  52. /**
  53. * List of supported display types for dataset.
  54. */
  55. supportedDisplayTypes: DisplayType[];
  56. /**
  57. * Transforms table API results into format that is used by
  58. * table and big number components.
  59. */
  60. transformTable: (
  61. data: TableResponse,
  62. widgetQuery: WidgetQuery,
  63. organization: Organization,
  64. pageFilters: PageFilters
  65. ) => TableData;
  66. /**
  67. * Configure enabling/disabling sort/direction options with an
  68. * optional message for why it is disabled.
  69. */
  70. disableSortOptions?: (widgetQuery: WidgetQuery) => {
  71. disableSort: boolean;
  72. disableSortDirection: boolean;
  73. disableSortReason?: string;
  74. };
  75. /**
  76. * Used for mapping column names to more desirable
  77. * values in tables.
  78. */
  79. fieldHeaderMap?: Record<string, string>;
  80. /**
  81. * Filter the options available to the parameters list
  82. * of an aggregate function in QueryField component on the
  83. * Widget Builder.
  84. */
  85. filterAggregateParams?: (
  86. option: FieldValueOption,
  87. fieldValue?: QueryFieldValue
  88. ) => boolean;
  89. /**
  90. * Refine the options available in the sort options for timeseries
  91. * displays on the 'Sort by' step of the Widget Builder.
  92. */
  93. filterSeriesSortOptions?: (
  94. columns: Set<string>
  95. ) => (option: FieldValueOption) => boolean;
  96. /**
  97. * Filter the primary options available in a table widget
  98. * columns on the Widget Builder.
  99. */
  100. filterTableOptions?: (option: FieldValueOption) => boolean;
  101. /**
  102. * Filter the options available to the parameters list
  103. * of an aggregate function in QueryField component on the
  104. * Widget Builder.
  105. */
  106. filterYAxisAggregateParams?: (
  107. fieldValue: QueryFieldValue,
  108. displayType: DisplayType
  109. ) => (option: FieldValueOption) => boolean;
  110. filterYAxisOptions?: (
  111. displayType: DisplayType
  112. ) => (option: FieldValueOption) => boolean;
  113. /**
  114. * Used to select custom renderers for field types.
  115. */
  116. getCustomFieldRenderer?: (
  117. field: string,
  118. meta: MetaType,
  119. organization?: Organization
  120. ) => ReturnType<typeof getFieldRenderer> | null;
  121. /**
  122. * Field options to display in the Group by selector.
  123. */
  124. getGroupByFieldOptions?: (
  125. organization: Organization,
  126. tags?: TagCollection
  127. ) => Record<string, SelectValue<FieldValue>>;
  128. /**
  129. * Generate the request promises for fetching
  130. * series data.
  131. */
  132. getSeriesRequest?: (
  133. api: Client,
  134. widget: Widget,
  135. queryIndex: number,
  136. organization: Organization,
  137. pageFilters: PageFilters,
  138. referrer?: string,
  139. mepSetting?: MEPState | null
  140. ) => Promise<[SeriesResponse, string | undefined, ResponseMeta | undefined]>;
  141. /**
  142. * Get the result type of the series. ie duration, size, percentage, etc
  143. */
  144. getSeriesResultType?: (
  145. data: SeriesResponse,
  146. widgetQuery: WidgetQuery
  147. ) => Record<string, AggregationOutputType>;
  148. /**
  149. * Generate the request promises for fetching
  150. * tabular data.
  151. */
  152. getTableRequest?: (
  153. api: Client,
  154. query: WidgetQuery,
  155. organization: Organization,
  156. pageFilters: PageFilters,
  157. limit?: number,
  158. cursor?: string,
  159. referrer?: string,
  160. mepSetting?: MEPState | null
  161. ) => Promise<[TableResponse, string | undefined, ResponseMeta | undefined]>;
  162. /**
  163. * Generate the list of sort options for table
  164. * displays on the 'Sort by' step of the Widget Builder.
  165. */
  166. getTableSortOptions?: (
  167. organization: Organization,
  168. widgetQuery: WidgetQuery
  169. ) => SelectValue<string>[];
  170. /**
  171. * Generate the list of sort options for timeseries
  172. * displays on the 'Sort by' step of the Widget Builder.
  173. */
  174. getTimeseriesSortOptions?: (
  175. organization: Organization,
  176. widgetQuery: WidgetQuery,
  177. tags?: TagCollection
  178. ) => Record<string, SelectValue<FieldValue>>;
  179. /**
  180. * Apply dataset specific overrides to the logic that handles
  181. * column updates for tables in the Widget Builder.
  182. */
  183. handleColumnFieldChangeOverride?: (widgetQuery: WidgetQuery) => WidgetQuery;
  184. /**
  185. * Called on column or y-axis change in the Widget Builder
  186. * to reset the orderby of the widget query.
  187. */
  188. handleOrderByReset?: (widgetQuery: WidgetQuery, newFields: string[]) => WidgetQuery;
  189. /**
  190. * Transforms timeseries API results into series data that is
  191. * ingestable by echarts for timeseries visualizations.
  192. */
  193. transformSeries?: (
  194. data: SeriesResponse,
  195. widgetQuery: WidgetQuery,
  196. organization: Organization
  197. ) => Series[];
  198. }
  199. export function getDatasetConfig<T extends WidgetType | undefined>(
  200. widgetType: T
  201. ): T extends WidgetType.ISSUE
  202. ? typeof IssuesConfig
  203. : T extends WidgetType.RELEASE
  204. ? typeof ReleasesConfig
  205. : typeof ErrorsAndTransactionsConfig;
  206. export function getDatasetConfig(
  207. widgetType?: WidgetType
  208. ): typeof IssuesConfig | typeof ReleasesConfig | typeof ErrorsAndTransactionsConfig {
  209. switch (widgetType) {
  210. case WidgetType.ISSUE:
  211. return IssuesConfig;
  212. case WidgetType.RELEASE:
  213. return ReleasesConfig;
  214. case WidgetType.DISCOVER:
  215. default:
  216. return ErrorsAndTransactionsConfig;
  217. }
  218. }
  219. /**
  220. * A generic orderby reset helper function that updates the query's
  221. * orderby based on new selected fields.
  222. */
  223. export function handleOrderByReset(
  224. widgetQuery: WidgetQuery,
  225. newFields: string[]
  226. ): WidgetQuery {
  227. const rawOrderby = trimStart(widgetQuery.orderby, '-');
  228. const isDescending = widgetQuery.orderby.startsWith('-');
  229. const orderbyPrefix = isDescending ? '-' : '';
  230. if (!newFields.includes(rawOrderby) && widgetQuery.orderby !== '') {
  231. const isFromAggregates = widgetQuery.aggregates.includes(rawOrderby);
  232. const isCustomEquation = isEquation(rawOrderby);
  233. const isUsedInGrouping = widgetQuery.columns.includes(rawOrderby);
  234. const keepCurrentOrderby = isFromAggregates || isCustomEquation || isUsedInGrouping;
  235. const firstAggregateAlias = isEquation(widgetQuery.aggregates[0] ?? '')
  236. ? `equation[${getNumEquations(widgetQuery.aggregates) - 1}]`
  237. : newFields[0];
  238. widgetQuery.orderby =
  239. (keepCurrentOrderby && widgetQuery.orderby) ||
  240. `${orderbyPrefix}${firstAggregateAlias}`;
  241. }
  242. return widgetQuery;
  243. }