base.tsx 8.2 KB

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