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 8.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2018 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 { searchMembers } from '../../api/organizations';
  21. import { searchUsers } from '../../api/users';
  22. import { formatMeasure } from '../../helpers/measures';
  23. import { get, save } from '../../helpers/storage';
  24. import {
  25. queriesEqual,
  26. cleanQuery,
  27. parseAsBoolean,
  28. parseAsArray,
  29. parseAsString,
  30. serializeString,
  31. serializeStringArray,
  32. parseAsDate,
  33. serializeDateShort,
  34. RawQuery
  35. } from '../../helpers/query';
  36. export interface Query {
  37. assigned: boolean;
  38. assignees: string[];
  39. authors: string[];
  40. createdAfter: Date | undefined;
  41. createdAt: string;
  42. createdBefore: Date | undefined;
  43. createdInLast: string;
  44. cwe: string[];
  45. directories: string[];
  46. files: string[];
  47. issues: string[];
  48. languages: string[];
  49. modules: string[];
  50. owaspTop10: string[];
  51. projects: string[];
  52. resolutions: string[];
  53. resolved: boolean;
  54. rules: string[];
  55. sansTop25: string[];
  56. severities: string[];
  57. sinceLeakPeriod: boolean;
  58. sort: string;
  59. statuses: string[];
  60. tags: string[];
  61. types: string[];
  62. }
  63. export const STANDARDS = 'standards';
  64. // allow sorting by CREATION_DATE only
  65. const parseAsSort = (sort: string) => (sort === 'CREATION_DATE' ? 'CREATION_DATE' : '');
  66. const ISSUES_DEFAULT = 'sonarqube.issues.default';
  67. export function parseQuery(query: RawQuery): Query {
  68. return {
  69. assigned: parseAsBoolean(query.assigned),
  70. assignees: parseAsArray(query.assignees, parseAsString),
  71. authors: parseAsArray(query.authors, parseAsString),
  72. createdAfter: parseAsDate(query.createdAfter),
  73. createdAt: parseAsString(query.createdAt),
  74. createdBefore: parseAsDate(query.createdBefore),
  75. createdInLast: parseAsString(query.createdInLast),
  76. cwe: parseAsArray(query.cwe, parseAsString),
  77. directories: parseAsArray(query.directories, parseAsString),
  78. files: parseAsArray(query.fileUuids, parseAsString),
  79. issues: parseAsArray(query.issues, parseAsString),
  80. languages: parseAsArray(query.languages, parseAsString),
  81. modules: parseAsArray(query.moduleUuids, parseAsString),
  82. owaspTop10: parseAsArray(query.owaspTop10, parseAsString),
  83. projects: parseAsArray(query.projects, parseAsString),
  84. resolutions: parseAsArray(query.resolutions, parseAsString),
  85. resolved: parseAsBoolean(query.resolved),
  86. rules: parseAsArray(query.rules, parseAsString),
  87. sansTop25: parseAsArray(query.sansTop25, parseAsString),
  88. severities: parseAsArray(query.severities, parseAsString),
  89. sinceLeakPeriod: parseAsBoolean(query.sinceLeakPeriod, false),
  90. sort: parseAsSort(query.s),
  91. statuses: parseAsArray(query.statuses, parseAsString),
  92. tags: parseAsArray(query.tags, parseAsString),
  93. types: parseAsArray(query.types, parseAsString)
  94. };
  95. }
  96. export function getOpen(query: RawQuery): string {
  97. return query.open;
  98. }
  99. export const areMyIssuesSelected = (query: RawQuery) => query.myIssues === 'true';
  100. export function serializeQuery(query: Query): RawQuery {
  101. const filter = {
  102. assigned: query.assigned ? undefined : 'false',
  103. assignees: serializeStringArray(query.assignees),
  104. authors: serializeStringArray(query.authors),
  105. createdAfter: serializeDateShort(query.createdAfter),
  106. createdAt: serializeString(query.createdAt),
  107. createdBefore: serializeDateShort(query.createdBefore),
  108. createdInLast: serializeString(query.createdInLast),
  109. cwe: serializeStringArray(query.cwe),
  110. directories: serializeStringArray(query.directories),
  111. fileUuids: serializeStringArray(query.files),
  112. issues: serializeStringArray(query.issues),
  113. languages: serializeStringArray(query.languages),
  114. moduleUuids: serializeStringArray(query.modules),
  115. owaspTop10: serializeStringArray(query.owaspTop10),
  116. projects: serializeStringArray(query.projects),
  117. resolutions: serializeStringArray(query.resolutions),
  118. resolved: query.resolved ? undefined : 'false',
  119. rules: serializeStringArray(query.rules),
  120. s: serializeString(query.sort),
  121. sansTop25: serializeStringArray(query.sansTop25),
  122. severities: serializeStringArray(query.severities),
  123. sinceLeakPeriod: query.sinceLeakPeriod ? 'true' : undefined,
  124. statuses: serializeStringArray(query.statuses),
  125. tags: serializeStringArray(query.tags),
  126. types: serializeStringArray(query.types)
  127. };
  128. return cleanQuery(filter);
  129. }
  130. export const areQueriesEqual = (a: RawQuery, b: RawQuery) =>
  131. queriesEqual(parseQuery(a), parseQuery(b));
  132. export interface RawFacet {
  133. property: string;
  134. values: Array<{ val: string; count: number }>;
  135. }
  136. export interface Facet {
  137. [value: string]: number;
  138. }
  139. export function mapFacet(facet: string) {
  140. const propertyMapping: { [x: string]: string } = {
  141. files: 'fileUuids',
  142. modules: 'moduleUuids'
  143. };
  144. return propertyMapping[facet] || facet;
  145. }
  146. export function parseFacets(facets: RawFacet[]): { [x: string]: Facet } {
  147. if (!facets) {
  148. return {};
  149. }
  150. // for readability purpose
  151. const propertyMapping: { [x: string]: string } = {
  152. fileUuids: 'files',
  153. moduleUuids: 'modules'
  154. };
  155. const result: { [x: string]: Facet } = {};
  156. facets.forEach(facet => {
  157. const values: Facet = {};
  158. facet.values.forEach(value => {
  159. values[value.val] = value.count;
  160. });
  161. const finalProperty = propertyMapping[facet.property] || facet.property;
  162. result[finalProperty] = values;
  163. });
  164. return result;
  165. }
  166. export function formatFacetStat(stat: number | undefined) {
  167. return stat && formatMeasure(stat, 'SHORT_INT');
  168. }
  169. export interface ReferencedComponent {
  170. key: string;
  171. name: string;
  172. organization: string;
  173. path?: string;
  174. uuid: string;
  175. }
  176. export interface ReferencedUser {
  177. avatar: string;
  178. name: string;
  179. }
  180. export interface ReferencedLanguage {
  181. name: string;
  182. }
  183. export interface ReferencedRule {
  184. langName?: string;
  185. name: string;
  186. }
  187. export interface SearchedAssignee {
  188. avatar?: string;
  189. login: string;
  190. name: string;
  191. }
  192. export const searchAssignees = (
  193. query: string,
  194. organization: string | undefined,
  195. page = 1
  196. ): Promise<{ paging: T.Paging; results: SearchedAssignee[] }> => {
  197. return organization
  198. ? searchMembers({ organization, p: page, ps: 50, q: query }).then(({ paging, users }) => ({
  199. paging,
  200. results: users
  201. }))
  202. : searchUsers({ p: page, q: query }).then(({ paging, users }) => ({ paging, results: users }));
  203. };
  204. const LOCALSTORAGE_MY = 'my';
  205. const LOCALSTORAGE_ALL = 'all';
  206. export const isMySet = () => {
  207. return get(ISSUES_DEFAULT) === LOCALSTORAGE_MY;
  208. };
  209. export const saveMyIssues = (myIssues: boolean) =>
  210. save(ISSUES_DEFAULT, myIssues ? LOCALSTORAGE_MY : LOCALSTORAGE_ALL);
  211. export function getLocations(
  212. { flows, secondaryLocations }: Pick<T.Issue, 'flows' | 'secondaryLocations'>,
  213. selectedFlowIndex: number | undefined
  214. ) {
  215. if (selectedFlowIndex !== undefined) {
  216. return flows[selectedFlowIndex] || [];
  217. } else {
  218. return flows.length > 0 ? flows[0] : secondaryLocations;
  219. }
  220. }
  221. export function getSelectedLocation(
  222. issue: Pick<T.Issue, 'flows' | 'secondaryLocations'>,
  223. selectedFlowIndex: number | undefined,
  224. selectedLocationIndex: number | undefined
  225. ) {
  226. const locations = getLocations(issue, selectedFlowIndex);
  227. if (
  228. selectedLocationIndex !== undefined &&
  229. selectedLocationIndex >= 0 &&
  230. locations.length >= selectedLocationIndex
  231. ) {
  232. return locations[selectedLocationIndex];
  233. } else {
  234. return undefined;
  235. }
  236. }
  237. export function allLocationsEmpty(
  238. issue: Pick<T.Issue, 'flows' | 'secondaryLocations'>,
  239. selectedFlowIndex: number | undefined
  240. ) {
  241. return getLocations(issue, selectedFlowIndex).every(location => !location.msg);
  242. }