base.tsx 8.1 KB

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