useSpanMetrics.tsx 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import {Location} from 'history';
  2. import EventView from 'sentry/utils/discover/eventView';
  3. import {Sort} from 'sentry/utils/discover/fields';
  4. import {DiscoverDatasets} from 'sentry/utils/discover/types';
  5. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  6. import {useLocation} from 'sentry/utils/useLocation';
  7. import {
  8. MetricsProperty,
  9. MetricsResponse,
  10. SpanMetricsQueryFilters,
  11. } from 'sentry/views/starfish/types';
  12. import {useWrappedDiscoverQuery} from 'sentry/views/starfish/utils/useSpansQuery';
  13. import {EMPTY_OPTION_VALUE} from 'sentry/views/starfish/views/spans/selectors/emptyOption';
  14. interface UseSpanMetricsOptions<Fields> {
  15. cursor?: string;
  16. fields?: Fields;
  17. filters?: SpanMetricsQueryFilters;
  18. limit?: number;
  19. referrer?: string;
  20. sorts?: Sort[];
  21. }
  22. export const useSpanMetrics = <Fields extends MetricsProperty[]>(
  23. options: UseSpanMetricsOptions<Fields> = {}
  24. ) => {
  25. const {fields = [], filters = {}, sorts = [], limit, cursor, referrer} = options;
  26. const location = useLocation();
  27. const eventView = getEventView(filters, fields, sorts, location);
  28. const enabled = Object.values(filters).every(value => Boolean(value));
  29. const result = useWrappedDiscoverQuery({
  30. eventView,
  31. initialData: [],
  32. limit,
  33. enabled,
  34. referrer,
  35. cursor,
  36. });
  37. // This type is a little awkward but it explicitly states that the response could be empty. This doesn't enable unchecked access errors, but it at least indicates that it's possible that there's no data
  38. // eslint-disable-next-line @typescript-eslint/ban-types
  39. const data = (result?.data ?? []) as Pick<MetricsResponse, Fields[number]>[] | [];
  40. return {
  41. ...result,
  42. data,
  43. isEnabled: enabled,
  44. };
  45. };
  46. function getEventView(
  47. filters: SpanMetricsQueryFilters = {},
  48. fields: string[] = [],
  49. sorts: Sort[] = [],
  50. location: Location
  51. ) {
  52. const query = new MutableSearch('');
  53. Object.entries(filters).forEach(([key, value]) => {
  54. if (!value) {
  55. return;
  56. }
  57. if (value === EMPTY_OPTION_VALUE) {
  58. query.addFilterValue('!has', key);
  59. }
  60. query.addFilterValue(key, value, !ALLOWED_WILDCARD_FIELDS.includes(key));
  61. });
  62. // TODO: This condition should be enforced everywhere
  63. // query.addFilterValue('has', 'span.description');
  64. const eventView = EventView.fromNewQueryWithLocation(
  65. {
  66. name: '',
  67. query: query.formatString(),
  68. fields,
  69. dataset: DiscoverDatasets.SPANS_METRICS,
  70. version: 2,
  71. },
  72. location
  73. );
  74. if (sorts.length > 0) {
  75. eventView.sorts = sorts;
  76. }
  77. return eventView;
  78. }
  79. const ALLOWED_WILDCARD_FIELDS = ['span.description'];