parseMetricWidgetsQueryParam.tsx 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. import {getDefaultMetricDisplayType, getDefaultMetricOp} from 'sentry/utils/metrics';
  2. import {DEFAULT_SORT_STATE} from 'sentry/utils/metrics/constants';
  3. import {isMRI} from 'sentry/utils/metrics/mri';
  4. import {
  5. type FocusedMetricsSeries,
  6. MetricDisplayType,
  7. type MetricWidgetQueryParams,
  8. type SortState,
  9. } from 'sentry/utils/metrics/types';
  10. function isRecord(value: unknown): value is Record<string, unknown> {
  11. return typeof value === 'object' && value !== null && !Array.isArray(value);
  12. }
  13. function isMetricDisplayType(value: unknown): value is MetricDisplayType {
  14. return Object.values(MetricDisplayType).includes(value as MetricDisplayType);
  15. }
  16. function getMRIParam(widget: Record<string, unknown>) {
  17. return 'mri' in widget && isMRI(widget.mri) ? widget.mri : undefined;
  18. }
  19. function parseStringParam(
  20. widget: Record<string, unknown>,
  21. key: string
  22. ): string | undefined {
  23. const value = widget[key];
  24. return typeof value === 'string' ? value : undefined;
  25. }
  26. function parseBooleanParam(
  27. widget: Record<string, unknown>,
  28. key: string
  29. ): boolean | undefined {
  30. const value = widget[key];
  31. return typeof value === 'boolean' ? value : undefined;
  32. }
  33. function parseArrayParam<T extends Exclude<any, undefined>>(
  34. widget: object,
  35. key: string,
  36. entryParser: (entry: unknown) => T | undefined
  37. ): T[] {
  38. if (!(key in widget)) {
  39. return [];
  40. }
  41. // allow single values instead of arrays
  42. if (!Array.isArray(widget[key])) {
  43. const entry = entryParser(widget[key]);
  44. return entry === undefined ? [] : [entry];
  45. }
  46. return widget[key].map(entryParser).filter((entry): entry is T => entry !== undefined);
  47. }
  48. function parseFocusedSeries(series: any): FocusedMetricsSeries | undefined {
  49. if (!isRecord(series)) {
  50. return undefined;
  51. }
  52. const seriesName = parseStringParam(series, 'seriesName');
  53. const groupBy =
  54. 'groupBy' in series && isRecord(series.groupBy)
  55. ? (series.groupBy as Record<string, string>)
  56. : undefined;
  57. if (!seriesName) {
  58. return undefined;
  59. }
  60. return {seriesName, groupBy};
  61. }
  62. function parseSortParam(widget: Record<string, unknown>, key: string): SortState {
  63. const sort = widget[key];
  64. if (!isRecord(sort)) {
  65. return DEFAULT_SORT_STATE;
  66. }
  67. const name = parseStringParam(sort, 'name');
  68. const order =
  69. 'order' in sort && (sort.order === 'desc' || sort.order === 'asc')
  70. ? sort.order
  71. : DEFAULT_SORT_STATE.order;
  72. if (
  73. name === 'name' ||
  74. name === 'avg' ||
  75. name === 'min' ||
  76. name === 'max' ||
  77. name === 'sum'
  78. ) {
  79. return {name, order};
  80. }
  81. return {name: undefined, order};
  82. }
  83. export function parseMetricWidgetsQueryParam(
  84. queryParam?: string
  85. ): MetricWidgetQueryParams[] | undefined {
  86. let currentWidgets: unknown = undefined;
  87. try {
  88. currentWidgets = JSON.parse(queryParam || '');
  89. } catch (_) {
  90. return undefined;
  91. }
  92. // It has to be an array and non-empty
  93. if (!Array.isArray(currentWidgets) || currentWidgets.length === 0) {
  94. return undefined;
  95. }
  96. const parsedWidgets = currentWidgets
  97. .map((widget: unknown): MetricWidgetQueryParams | null => {
  98. if (!isRecord(widget)) {
  99. return null;
  100. }
  101. const mri = getMRIParam(widget);
  102. // If we cannot retrieve an MRI the resulting widget will be useless anyway
  103. if (!mri) {
  104. return null;
  105. }
  106. const op = parseStringParam(widget, 'op');
  107. const displayType = parseStringParam(widget, 'displayType');
  108. return {
  109. mri,
  110. op: parseStringParam(widget, 'op') ?? getDefaultMetricOp(mri),
  111. query: parseStringParam(widget, 'query') ?? '',
  112. groupBy: parseArrayParam(widget, 'groupBy', entry =>
  113. typeof entry === 'string' ? entry : undefined
  114. ),
  115. displayType: isMetricDisplayType(displayType)
  116. ? displayType
  117. : getDefaultMetricDisplayType(mri, op),
  118. focusedSeries: parseArrayParam(widget, 'focusedSeries', parseFocusedSeries),
  119. powerUserMode: parseBooleanParam(widget, 'powerUserMode') ?? false,
  120. sort: parseSortParam(widget, 'sort'),
  121. };
  122. })
  123. .filter((widget): widget is MetricWidgetQueryParams => !!widget);
  124. return parsedWidgets.length > 0 ? parsedWidgets : undefined;
  125. }