base.tsx 7.7 KB

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