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.

Sidebar.tsx 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2021 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 * as React from 'react';
  21. import { connect } from 'react-redux';
  22. import { getGlobalSettingValue, Store } from '../../../store/rootReducer';
  23. import { BranchLike } from '../../../types/branch-like';
  24. import { ComponentQualifier, isApplication, isPortfolioLike } from '../../../types/component';
  25. import {
  26. Facet,
  27. ReferencedComponent,
  28. ReferencedLanguage,
  29. ReferencedRule
  30. } from '../../../types/issues';
  31. import { Query } from '../utils';
  32. import AssigneeFacet from './AssigneeFacet';
  33. import AuthorFacet from './AuthorFacet';
  34. import CreationDateFacet from './CreationDateFacet';
  35. import DirectoryFacet from './DirectoryFacet';
  36. import FileFacet from './FileFacet';
  37. import LanguageFacet from './LanguageFacet';
  38. import ProjectFacet from './ProjectFacet';
  39. import ResolutionFacet from './ResolutionFacet';
  40. import RuleFacet from './RuleFacet';
  41. import ScopeFacet from './ScopeFacet';
  42. import SeverityFacet from './SeverityFacet';
  43. import StandardFacet from './StandardFacet';
  44. import StatusFacet from './StatusFacet';
  45. import TagFacet from './TagFacet';
  46. import TypeFacet from './TypeFacet';
  47. export interface Props {
  48. branchLike?: BranchLike;
  49. component: T.Component | undefined;
  50. createdAfterIncludesTime: boolean;
  51. facets: T.Dict<Facet | undefined>;
  52. loadSearchResultCount: (property: string, changes: Partial<Query>) => Promise<Facet>;
  53. loadingFacets: T.Dict<boolean>;
  54. myIssues: boolean;
  55. onFacetToggle: (property: string) => void;
  56. onFilterChange: (changes: Partial<Query>) => void;
  57. openFacets: T.Dict<boolean>;
  58. query: Query;
  59. referencedComponentsById: T.Dict<ReferencedComponent>;
  60. referencedComponentsByKey: T.Dict<ReferencedComponent>;
  61. referencedLanguages: T.Dict<ReferencedLanguage>;
  62. referencedRules: T.Dict<ReferencedRule>;
  63. referencedUsers: T.Dict<T.UserBase>;
  64. disableDeveloperAggregatedInfo: boolean;
  65. }
  66. export class Sidebar extends React.PureComponent<Props> {
  67. renderComponentFacets() {
  68. const { component, facets, loadingFacets, openFacets, query, branchLike } = this.props;
  69. const hasFileOrDirectory =
  70. !isApplication(component?.qualifier) && !isPortfolioLike(component?.qualifier);
  71. if (!component || !hasFileOrDirectory) {
  72. return null;
  73. }
  74. const commonProps = {
  75. componentKey: component.key,
  76. loadSearchResultCount: this.props.loadSearchResultCount,
  77. onChange: this.props.onFilterChange,
  78. onToggle: this.props.onFacetToggle,
  79. query
  80. };
  81. return (
  82. <>
  83. {component.qualifier !== ComponentQualifier.Directory && (
  84. <DirectoryFacet
  85. branchLike={branchLike}
  86. directories={query.directories}
  87. fetching={loadingFacets.directories === true}
  88. open={!!openFacets.directories}
  89. stats={facets.directories}
  90. {...commonProps}
  91. />
  92. )}
  93. <FileFacet
  94. branchLike={branchLike}
  95. fetching={loadingFacets.files === true}
  96. files={query.files}
  97. open={!!openFacets.files}
  98. stats={facets.files}
  99. {...commonProps}
  100. />
  101. </>
  102. );
  103. }
  104. render() {
  105. const { component, createdAfterIncludesTime, facets, openFacets, query } = this.props;
  106. const displayProjectsFacet =
  107. !component || !['TRK', 'BRC', 'DIR', 'DEV_PRJ'].includes(component.qualifier);
  108. const displayAuthorFacet = !component || component.qualifier !== 'DEV';
  109. return (
  110. <>
  111. <TypeFacet
  112. fetching={this.props.loadingFacets.types === true}
  113. onChange={this.props.onFilterChange}
  114. onToggle={this.props.onFacetToggle}
  115. open={!!openFacets.types}
  116. stats={facets.types}
  117. types={query.types}
  118. />
  119. <SeverityFacet
  120. fetching={this.props.loadingFacets.severities === true}
  121. onChange={this.props.onFilterChange}
  122. onToggle={this.props.onFacetToggle}
  123. open={!!openFacets.severities}
  124. severities={query.severities}
  125. stats={facets.severities}
  126. />
  127. <ScopeFacet
  128. fetching={this.props.loadingFacets.scopes === true}
  129. onChange={this.props.onFilterChange}
  130. onToggle={this.props.onFacetToggle}
  131. open={!!openFacets.scopes}
  132. stats={facets.scopes}
  133. scopes={query.scopes}
  134. />
  135. <ResolutionFacet
  136. fetching={this.props.loadingFacets.resolutions === true}
  137. onChange={this.props.onFilterChange}
  138. onToggle={this.props.onFacetToggle}
  139. open={!!openFacets.resolutions}
  140. resolutions={query.resolutions}
  141. resolved={query.resolved}
  142. stats={facets.resolutions}
  143. />
  144. <StatusFacet
  145. fetching={this.props.loadingFacets.statuses === true}
  146. onChange={this.props.onFilterChange}
  147. onToggle={this.props.onFacetToggle}
  148. open={!!openFacets.statuses}
  149. stats={facets.statuses}
  150. statuses={query.statuses}
  151. />
  152. <StandardFacet
  153. cwe={query.cwe}
  154. cweOpen={!!openFacets.cwe}
  155. cweStats={facets.cwe}
  156. fetchingCwe={this.props.loadingFacets.cwe === true}
  157. fetchingOwaspTop10={this.props.loadingFacets.owaspTop10 === true}
  158. fetchingSansTop25={this.props.loadingFacets.sansTop25 === true}
  159. fetchingSonarSourceSecurity={this.props.loadingFacets.sonarsourceSecurity === true}
  160. loadSearchResultCount={this.props.loadSearchResultCount}
  161. onChange={this.props.onFilterChange}
  162. onToggle={this.props.onFacetToggle}
  163. open={!!openFacets.standards}
  164. owaspTop10={query.owaspTop10}
  165. owaspTop10Open={!!openFacets.owaspTop10}
  166. owaspTop10Stats={facets.owaspTop10}
  167. query={query}
  168. sansTop25={query.sansTop25}
  169. sansTop25Open={!!openFacets.sansTop25}
  170. sansTop25Stats={facets.sansTop25}
  171. sonarsourceSecurity={query.sonarsourceSecurity}
  172. sonarsourceSecurityOpen={!!openFacets.sonarsourceSecurity}
  173. sonarsourceSecurityStats={facets.sonarsourceSecurity}
  174. />
  175. <CreationDateFacet
  176. component={component}
  177. createdAfter={query.createdAfter}
  178. createdAfterIncludesTime={createdAfterIncludesTime}
  179. createdAt={query.createdAt}
  180. createdBefore={query.createdBefore}
  181. createdInLast={query.createdInLast}
  182. fetching={this.props.loadingFacets.createdAt === true}
  183. onChange={this.props.onFilterChange}
  184. onToggle={this.props.onFacetToggle}
  185. open={!!openFacets.createdAt}
  186. sinceLeakPeriod={query.sinceLeakPeriod}
  187. stats={facets.createdAt}
  188. />
  189. <LanguageFacet
  190. fetching={this.props.loadingFacets.languages === true}
  191. languages={query.languages}
  192. loadSearchResultCount={this.props.loadSearchResultCount}
  193. onChange={this.props.onFilterChange}
  194. onToggle={this.props.onFacetToggle}
  195. open={!!openFacets.languages}
  196. query={query}
  197. referencedLanguages={this.props.referencedLanguages}
  198. stats={facets.languages}
  199. />
  200. <RuleFacet
  201. fetching={this.props.loadingFacets.rules === true}
  202. languages={query.languages}
  203. loadSearchResultCount={this.props.loadSearchResultCount}
  204. onChange={this.props.onFilterChange}
  205. onToggle={this.props.onFacetToggle}
  206. open={!!openFacets.rules}
  207. query={query}
  208. referencedRules={this.props.referencedRules}
  209. rules={query.rules}
  210. stats={facets.rules}
  211. />
  212. <TagFacet
  213. component={component}
  214. fetching={this.props.loadingFacets.tags === true}
  215. loadSearchResultCount={this.props.loadSearchResultCount}
  216. onChange={this.props.onFilterChange}
  217. onToggle={this.props.onFacetToggle}
  218. open={!!openFacets.tags}
  219. query={query}
  220. stats={facets.tags}
  221. tags={query.tags}
  222. />
  223. {displayProjectsFacet && (
  224. <ProjectFacet
  225. component={component}
  226. fetching={this.props.loadingFacets.projects === true}
  227. loadSearchResultCount={this.props.loadSearchResultCount}
  228. onChange={this.props.onFilterChange}
  229. onToggle={this.props.onFacetToggle}
  230. open={!!openFacets.projects}
  231. projects={query.projects}
  232. query={query}
  233. referencedComponents={this.props.referencedComponentsByKey}
  234. stats={facets.projects}
  235. />
  236. )}
  237. {this.renderComponentFacets()}
  238. {!this.props.myIssues && !this.props.disableDeveloperAggregatedInfo && (
  239. <AssigneeFacet
  240. assigned={query.assigned}
  241. assignees={query.assignees}
  242. fetching={this.props.loadingFacets.assignees === true}
  243. loadSearchResultCount={this.props.loadSearchResultCount}
  244. onChange={this.props.onFilterChange}
  245. onToggle={this.props.onFacetToggle}
  246. open={!!openFacets.assignees}
  247. query={query}
  248. referencedUsers={this.props.referencedUsers}
  249. stats={facets.assignees}
  250. />
  251. )}
  252. {displayAuthorFacet && !this.props.disableDeveloperAggregatedInfo && (
  253. <AuthorFacet
  254. author={query.author}
  255. component={component}
  256. fetching={this.props.loadingFacets.author === true}
  257. loadSearchResultCount={this.props.loadSearchResultCount}
  258. onChange={this.props.onFilterChange}
  259. onToggle={this.props.onFacetToggle}
  260. open={!!openFacets.author}
  261. query={query}
  262. stats={facets.author}
  263. />
  264. )}
  265. </>
  266. );
  267. }
  268. }
  269. export const mapStateToProps = (state: Store) => {
  270. const disableDeveloperAggregatedInfo = getGlobalSettingValue(
  271. state,
  272. 'sonar.developerAggregatedInfo.disabled'
  273. );
  274. return {
  275. disableDeveloperAggregatedInfo: disableDeveloperAggregatedInfo?.value === true.toString()
  276. };
  277. };
  278. export default connect(mapStateToProps)(Sidebar);