transformSessionsResponseToSeries.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  1. import {t} from 'sentry/locale';
  2. import type {Series} from 'sentry/types/echarts';
  3. import type {MetricsApiResponse} from 'sentry/types/metrics';
  4. import type {SessionApiResponse} from 'sentry/types/organization';
  5. import {defined} from 'sentry/utils';
  6. import {DERIVED_STATUS_METRICS_PATTERN} from '../widgetBuilder/releaseWidget/fields';
  7. import {derivedMetricsToField} from './releaseWidgetQueries';
  8. export function getSeriesName(
  9. field: string,
  10. group: SessionApiResponse['groups'][number],
  11. queryAlias?: string
  12. ) {
  13. const groupName = Object.entries(group.by)
  14. .map(([_, value]) => `${value}`)
  15. .join(', ');
  16. const seriesName = groupName
  17. ? `${groupName} : ${derivedMetricsToField(field)}`
  18. : derivedMetricsToField(field);
  19. return `${queryAlias ? `${queryAlias} > ` : ''}${seriesName}`;
  20. }
  21. export function transformSessionsResponseToSeries(
  22. response: SessionApiResponse | MetricsApiResponse | null,
  23. requestedStatusMetrics: string[],
  24. injectedFields: string[],
  25. queryAlias?: string
  26. ): Series[] {
  27. if (response === null) {
  28. return [];
  29. }
  30. const results: Series[] = [];
  31. if (!response.groups.length) {
  32. return [
  33. {
  34. seriesName: `(${t('no results')})`,
  35. data: response.intervals.map(interval => ({
  36. name: interval,
  37. value: 0,
  38. })),
  39. },
  40. ];
  41. }
  42. response.groups.forEach(group => {
  43. Object.keys(group.series).forEach(field => {
  44. // if `sum(session)` or `count_unique(user)` are not
  45. // requested as a part of the payload for
  46. // derived status metrics through the Sessions API,
  47. // they are injected into the payload and need to be
  48. // stripped.
  49. if (!injectedFields.includes(derivedMetricsToField(field))) {
  50. results.push({
  51. // @ts-expect-error TS(2345): Argument of type '{ by: Record<string, string | nu... Remove this comment to see the full error message
  52. seriesName: getSeriesName(field, group, queryAlias),
  53. data: response.intervals.map((interval, index) => ({
  54. name: interval,
  55. value: group.series[field]?.[index] ?? 0,
  56. })),
  57. });
  58. }
  59. });
  60. // if session.status is a groupby, some post processing
  61. // is needed to calculate the status derived metrics
  62. // from grouped results of `sum(session)` or `count_unique(user)`
  63. if (requestedStatusMetrics.length && defined(group.by['session.status'])) {
  64. requestedStatusMetrics.forEach(status => {
  65. const result = status.match(DERIVED_STATUS_METRICS_PATTERN);
  66. if (result) {
  67. let metricField: string | undefined = undefined;
  68. if (group.by['session.status'] === result[1]) {
  69. if (result[2] === 'session') {
  70. metricField = 'sum(session)';
  71. } else if (result[2] === 'user') {
  72. metricField = 'count_unique(user)';
  73. }
  74. }
  75. results.push({
  76. // @ts-expect-error TS(2345): Argument of type '{ by: Record<string, string | nu... Remove this comment to see the full error message
  77. seriesName: getSeriesName(status, group, queryAlias),
  78. data: response.intervals.map((interval, index) => ({
  79. name: interval,
  80. value: metricField ? group.series[metricField]?.[index] ?? 0 : 0,
  81. })),
  82. });
  83. }
  84. });
  85. }
  86. });
  87. return results;
  88. }