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

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