base.tsx 8.4 KB

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