You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

utils.ts 5.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. import { startOfDay } from 'date-fns';
  21. import { isEqual, uniq } from 'lodash';
  22. import { DEFAULT_GRAPH } from '../../components/activity-graph/utils';
  23. import { parseDate } from '../../helpers/dates';
  24. import { MEASURES_REDIRECTION } from '../../helpers/measures';
  25. import {
  26. cleanQuery,
  27. parseAsArray,
  28. parseAsDate,
  29. parseAsString,
  30. serializeDate,
  31. serializeString,
  32. serializeStringArray,
  33. } from '../../helpers/query';
  34. import { MetricKey } from '../../types/metrics';
  35. import { GraphType, ParsedAnalysis } from '../../types/project-activity';
  36. import { Dict, RawQuery } from '../../types/types';
  37. export interface Query {
  38. category: string;
  39. customMetrics: string[];
  40. from?: Date;
  41. graph: GraphType;
  42. project: string;
  43. selectedDate?: Date;
  44. to?: Date;
  45. }
  46. export function activityQueryChanged(prevQuery: Query, nextQuery: Query) {
  47. return prevQuery.category !== nextQuery.category || datesQueryChanged(prevQuery, nextQuery);
  48. }
  49. export function customMetricsChanged(prevQuery: Query, nextQuery: Query) {
  50. return !isEqual(prevQuery.customMetrics, nextQuery.customMetrics);
  51. }
  52. export function datesQueryChanged(prevQuery: Query, nextQuery: Query) {
  53. return !isEqual(prevQuery.from, nextQuery.from) || !isEqual(prevQuery.to, nextQuery.to);
  54. }
  55. export function historyQueryChanged(prevQuery: Query, nextQuery: Query) {
  56. return prevQuery.graph !== nextQuery.graph;
  57. }
  58. export interface AnalysesByDay {
  59. byDay: Dict<ParsedAnalysis[]>;
  60. version: string | null;
  61. key: string | null;
  62. }
  63. export function getAnalysesByVersionByDay(
  64. analyses: ParsedAnalysis[],
  65. query: Pick<Query, 'category' | 'from' | 'to'>,
  66. ) {
  67. return analyses.reduce<AnalysesByDay[]>((acc, analysis) => {
  68. let currentVersion = acc[acc.length - 1];
  69. const versionEvent = analysis.events.find((event) => event.category === 'VERSION');
  70. if (versionEvent) {
  71. const newVersion = { version: versionEvent.name, key: versionEvent.key, byDay: {} };
  72. if (!currentVersion || Object.keys(currentVersion.byDay).length > 0) {
  73. acc.push(newVersion);
  74. } else {
  75. acc[acc.length - 1] = newVersion;
  76. }
  77. currentVersion = newVersion;
  78. } else if (!currentVersion) {
  79. // APPs don't have version events, so let's create a fake one
  80. currentVersion = { version: null, key: null, byDay: {} };
  81. acc.push(currentVersion);
  82. }
  83. const day = startOfDay(parseDate(analysis.date)).getTime().toString();
  84. let matchFilters = true;
  85. if (query.category || query.from || query.to) {
  86. const isAfterFrom = !query.from || analysis.date >= query.from;
  87. const isBeforeTo = !query.to || analysis.date <= query.to;
  88. const hasSelectedCategoryEvents =
  89. !query.category ||
  90. analysis.events.find((event) => event.category === query.category) != null;
  91. matchFilters = isAfterFrom && isBeforeTo && hasSelectedCategoryEvents;
  92. }
  93. if (matchFilters) {
  94. if (!currentVersion.byDay[day]) {
  95. currentVersion.byDay[day] = [];
  96. }
  97. currentVersion.byDay[day].push(analysis);
  98. }
  99. return acc;
  100. }, []);
  101. }
  102. export function parseQuery(urlQuery: RawQuery): Query {
  103. const parsedMetrics = parseAsArray(urlQuery['custom_metrics'], parseAsString<MetricKey>);
  104. const customMetrics = uniq(parsedMetrics.map((metric) => MEASURES_REDIRECTION[metric] ?? metric));
  105. return {
  106. category: parseAsString(urlQuery['category']),
  107. customMetrics,
  108. from: parseAsDate(urlQuery['from']),
  109. graph: parseGraph(urlQuery['graph']),
  110. project: parseAsString(urlQuery['id']),
  111. to: parseAsDate(urlQuery['to']),
  112. selectedDate: parseAsDate(urlQuery['selected_date']),
  113. };
  114. }
  115. export function serializeQuery(query: Query): RawQuery {
  116. return cleanQuery({
  117. category: serializeString(query.category),
  118. project: serializeString(query.project),
  119. });
  120. }
  121. export function serializeUrlQuery(query: Query): RawQuery {
  122. return cleanQuery({
  123. category: serializeString(query.category),
  124. custom_metrics: serializeStringArray(query.customMetrics),
  125. from: serializeDate(query.from),
  126. graph: serializeGraph(query.graph),
  127. id: serializeString(query.project),
  128. to: serializeDate(query.to),
  129. selected_date: serializeDate(query.selectedDate),
  130. });
  131. }
  132. function parseGraph(value?: string) {
  133. const graph = parseAsString(value);
  134. return Object.keys(GraphType).includes(graph) ? (graph as GraphType) : DEFAULT_GRAPH;
  135. }
  136. function serializeGraph(value?: GraphType) {
  137. return value === DEFAULT_GRAPH ? undefined : value;
  138. }