metrics.tsx 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. import {useMemo} from 'react';
  2. import moment from 'moment';
  3. import {getInterval} from 'sentry/components/charts/utils';
  4. import {parseStatsPeriod} from 'sentry/components/organizations/timeRangeSelector/utils';
  5. import {ApiQueryKey, useApiQuery} from 'sentry/utils/queryClient';
  6. import useOrganization from 'sentry/utils/useOrganization';
  7. type MetricMeta = {
  8. mri: string;
  9. operations: string[];
  10. };
  11. export function useMetricsMeta(): Record<string, MetricMeta> {
  12. const {slug} = useOrganization();
  13. const getKey = (useCase: UseCase): ApiQueryKey => {
  14. return [`/organizations/${slug}/metrics/meta/`, {query: {useCase}}];
  15. };
  16. const opts = {
  17. staleTime: Infinity,
  18. };
  19. const {data: sessionsMeta = []} = useApiQuery<MetricMeta[]>(getKey('sessions'), opts);
  20. const {data: txnsMeta = []} = useApiQuery<MetricMeta[]>(getKey('transactions'), opts);
  21. const {data: customMeta = []} = useApiQuery<MetricMeta[]>(getKey('custom'), opts);
  22. return useMemo(
  23. () =>
  24. [...sessionsMeta, ...txnsMeta, ...customMeta].reduce((acc, metricMeta) => {
  25. return {...acc, [metricMeta.mri]: metricMeta};
  26. }, {}),
  27. [sessionsMeta, txnsMeta, customMeta]
  28. );
  29. }
  30. type MetricTag = {
  31. key: string;
  32. };
  33. export function useMetricsTags(mri: string) {
  34. const {slug} = useOrganization();
  35. const useCase = getUseCaseFromMri(mri);
  36. return useApiQuery<MetricTag[]>(
  37. [`/organizations/${slug}/metrics/tags/`, {query: {metric: mri, useCase}}],
  38. {
  39. staleTime: Infinity,
  40. }
  41. );
  42. }
  43. export function useMetricsTagValues(mri: string, tag: string) {
  44. const {slug} = useOrganization();
  45. const useCase = getUseCaseFromMri(mri);
  46. return useApiQuery<MetricTag[]>(
  47. [`/organizations/${slug}/metrics/tags/${tag}`, {query: {useCase}}],
  48. {
  49. staleTime: Infinity,
  50. enabled: !!tag,
  51. }
  52. );
  53. }
  54. export type MetricsDataProps = {
  55. mri: string;
  56. timeRange: any;
  57. groupBy?: string[];
  58. op?: string;
  59. projects?: string[];
  60. queryString?: string;
  61. };
  62. type Group = {
  63. by: Record<string, unknown>;
  64. series: Record<string, number[]>;
  65. totals: Record<string, number>;
  66. };
  67. export type MetricsData = {
  68. end: string;
  69. groups: Group[];
  70. intervals: string[];
  71. meta: MetricMeta[];
  72. query: string;
  73. start: string;
  74. };
  75. export function useMetricsData({
  76. mri,
  77. op,
  78. timeRange,
  79. projects,
  80. queryString,
  81. groupBy,
  82. }: MetricsDataProps) {
  83. const {slug} = useOrganization();
  84. const useCase = getUseCaseFromMri(mri);
  85. const field = op ? `${op}(${mri})` : mri;
  86. const {start, end} = getUTCTimeRange(timeRange);
  87. const interval = getMetricsInterval({start, end});
  88. const query = getQueryString({projects, queryString});
  89. const queryToSend = {
  90. field,
  91. useCase,
  92. interval,
  93. query,
  94. groupBy,
  95. };
  96. return useApiQuery<MetricsData>(
  97. [`/organizations/${slug}/metrics/data/`, {query: queryToSend}],
  98. {
  99. staleTime: 60,
  100. retry: 0,
  101. }
  102. );
  103. }
  104. function getQueryString({
  105. projects = [],
  106. queryString = '',
  107. }: Pick<MetricsDataProps, 'projects' | 'queryString'>): string {
  108. const projectQuery = projects.map(p => `project:${p}`).join(' OR ');
  109. return [projectQuery, queryString].join(' ');
  110. }
  111. const getUTCTimeRange = (timeRange: Record<string, any>) => {
  112. const absoluteTimeRange = timeRange.relative
  113. ? parseStatsPeriod(timeRange.relative)
  114. : timeRange;
  115. return {
  116. start: moment(absoluteTimeRange.start).utc().toISOString(),
  117. end: moment(absoluteTimeRange.end).utc().toISOString(),
  118. };
  119. };
  120. const getMetricsInterval = (timeRange: Record<string, any>) => {
  121. const diff = moment(timeRange.end).diff(moment(timeRange.start), 'days');
  122. if (diff >= 7 && diff <= 16) {
  123. return '2h';
  124. }
  125. if (diff > 16 && diff <= 30) {
  126. return '4h';
  127. }
  128. if (diff > 32 && diff <= 90) {
  129. return '12h';
  130. }
  131. return getInterval(timeRange, 'medium');
  132. };
  133. type UseCase = 'sessions' | 'transactions' | 'custom';
  134. export function getUseCaseFromMri(mri?: string): UseCase {
  135. if (mri?.includes('custom')) {
  136. return 'custom';
  137. }
  138. if (mri?.includes('transactions')) {
  139. return 'transactions';
  140. }
  141. return 'sessions';
  142. }