base.tsx 8.1 KB

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