base.tsx 8.3 KB

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