]> source.dussan.org Git - sonarqube.git/blob
d17baac56792783f43325a0d62ed4e2cb8c1411e
[sonarqube.git] /
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 package org.sonar.server.issue.index;
21
22 import com.google.common.base.CharMatcher;
23 import com.google.common.base.Splitter;
24 import java.io.IOException;
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.Iterator;
28 import java.util.Optional;
29 import javax.annotation.CheckForNull;
30 import javax.annotation.Nullable;
31 import org.apache.ibatis.cursor.Cursor;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34 import org.sonar.api.resources.Qualifiers;
35 import org.sonar.api.resources.Scopes;
36 import org.sonar.api.rules.CleanCodeAttribute;
37 import org.sonar.api.rules.RuleType;
38 import org.sonar.db.DatabaseUtils;
39 import org.sonar.db.DbClient;
40 import org.sonar.db.DbSession;
41 import org.sonar.db.issue.IndexedIssueDto;
42 import org.sonar.server.security.SecurityStandards;
43
44 import static com.google.common.base.Preconditions.checkArgument;
45 import static org.sonar.api.utils.DateUtils.longToDate;
46 import static org.sonar.db.rule.RuleDto.deserializeSecurityStandardsString;
47 import static org.sonar.server.security.SecurityStandards.fromSecurityStandards;
48
49 /**
50  * Scrolls over table ISSUES and reads documents to populate
51  * the issues index
52  */
53 class IssueIteratorForSingleChunk implements IssueIterator {
54   private static final Logger LOG = LoggerFactory.getLogger(IssueIteratorForSingleChunk.class);
55
56   static final Splitter STRING_LIST_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
57
58   private final DbSession session;
59
60   private final Cursor<IndexedIssueDto> indexCursor;
61   private final Iterator<IndexedIssueDto> iterator;
62
63   IssueIteratorForSingleChunk(DbClient dbClient, @Nullable String branchUuid, @Nullable Collection<String> issueKeys) {
64     checkArgument(issueKeys == null || issueKeys.size() <= DatabaseUtils.PARTITION_SIZE_FOR_ORACLE,
65       "Cannot search for more than " + DatabaseUtils.PARTITION_SIZE_FOR_ORACLE + " issue keys at once. Please provide the keys in smaller chunks.");
66     this.session = dbClient.openSession(false);
67     try {
68       indexCursor = dbClient.issueDao().scrollIssuesForIndexation(session, branchUuid, issueKeys);
69       iterator = indexCursor.iterator();
70     } catch (Exception e) {
71       session.close();
72       throw new IllegalStateException("Fail to prepare SQL request to select all issues", e);
73     }
74   }
75
76   @Override
77   public boolean hasNext() {
78     return iterator.hasNext();
79   }
80
81   @Override
82   public IssueDoc next() {
83
84     return toIssueDoc(iterator.next());
85   }
86
87   private static IssueDoc toIssueDoc(IndexedIssueDto indexedIssueDto) {
88     IssueDoc doc = new IssueDoc(new HashMap<>(30));
89
90     String key = indexedIssueDto.getIssueKey();
91
92     // all the fields must be present, even if value is null
93     doc.setKey(key);
94     doc.setAssigneeUuid(indexedIssueDto.getAssignee());
95     doc.setLine(indexedIssueDto.getLine());
96     doc.setResolution(indexedIssueDto.getResolution());
97     doc.setSeverity(indexedIssueDto.getSeverity());
98     String cleanCodeAttributeCategory = Optional.ofNullable(indexedIssueDto.getCleanCodeAttribute())
99       .map(CleanCodeAttribute::valueOf)
100       .map(CleanCodeAttribute::getAttributeCategory)
101       .map(Enum::name)
102       .orElse(null);
103     doc.setCleanCodeAttributeCategory(cleanCodeAttributeCategory);
104     doc.setStatus(indexedIssueDto.getStatus());
105     doc.setIssueStatus(indexedIssueDto.getIssueStatus());
106     doc.setEffort(indexedIssueDto.getEffort());
107     doc.setAuthorLogin(indexedIssueDto.getAuthorLogin());
108
109     doc.setFuncCloseDate(longToDate(indexedIssueDto.getIssueCloseDate()));
110     doc.setFuncCreationDate(longToDate(indexedIssueDto.getIssueCreationDate()));
111     doc.setFuncUpdateDate(longToDate(indexedIssueDto.getIssueUpdateDate()));
112
113     doc.setRuleUuid(indexedIssueDto.getRuleUuid());
114     doc.setLanguage(indexedIssueDto.getLanguage());
115     doc.setComponentUuid(indexedIssueDto.getComponentUuid());
116     String scope = indexedIssueDto.getScope();
117     String filePath = extractFilePath(indexedIssueDto.getPath(), scope);
118     doc.setFilePath(filePath);
119     doc.setDirectoryPath(extractDirPath(doc.filePath(), scope));
120     String branchUuid = indexedIssueDto.getBranchUuid();
121     boolean isMainBranch = indexedIssueDto.isMain();
122     String projectUuid = indexedIssueDto.getProjectUuid();
123     doc.setBranchUuid(branchUuid);
124     doc.setIsMainBranch(isMainBranch);
125     doc.setProjectUuid(projectUuid);
126     String tags = indexedIssueDto.getTags();
127     doc.setTags(STRING_LIST_SPLITTER.splitToList(tags == null ? "" : tags));
128     doc.setType(RuleType.valueOf(indexedIssueDto.getIssueType()));
129     doc.setImpacts(indexedIssueDto.getEffectiveImpacts());
130     SecurityStandards securityStandards = fromSecurityStandards(deserializeSecurityStandardsString(indexedIssueDto.getSecurityStandards()));
131     SecurityStandards.SQCategory sqCategory = securityStandards.getSqCategory();
132     doc.setOwaspTop10(securityStandards.getOwaspTop10());
133     doc.setOwaspTop10For2021(securityStandards.getOwaspTop10For2021());
134     doc.setPciDss32(securityStandards.getPciDss32());
135     doc.setPciDss40(securityStandards.getPciDss40());
136     doc.setOwaspAsvs40(securityStandards.getOwaspAsvs40());
137     doc.setCwe(securityStandards.getCwe());
138     doc.setSansTop25(securityStandards.getSansTop25());
139     doc.setSonarSourceSecurityCategory(sqCategory);
140     doc.setVulnerabilityProbability(sqCategory.getVulnerability());
141
142     doc.setScope(Qualifiers.UNIT_TEST_FILE.equals(indexedIssueDto.getQualifier()) ? IssueScope.TEST : IssueScope.MAIN);
143     doc.setIsNewCodeReference(indexedIssueDto.isNewCodeReferenceIssue());
144     String codeVariants = indexedIssueDto.getCodeVariants();
145     doc.setCodeVariants(STRING_LIST_SPLITTER.splitToList(codeVariants == null ? "" : codeVariants));
146     doc.setPrioritizedRule(indexedIssueDto.isPrioritizedRule());
147     return doc;
148
149   }
150
151   @CheckForNull
152   private static String extractDirPath(@Nullable String filePath, String scope) {
153     if (filePath != null) {
154       if (Scopes.DIRECTORY.equals(scope)) {
155         return filePath;
156       }
157       int lastSlashIndex = CharMatcher.anyOf("/").lastIndexIn(filePath);
158       if (lastSlashIndex > 0) {
159         return filePath.substring(0, lastSlashIndex);
160       }
161       return "/";
162     }
163     return null;
164   }
165
166   @CheckForNull
167   private static String extractFilePath(@Nullable String filePath, String scope) {
168     // On modules, the path contains the relative path of the module starting from its parent, and in E/S we're only interested in the
169     // path
170     // of files and directories.
171     // That's why the file path should be null on modules and projects.
172     if (filePath != null && !Scopes.PROJECT.equals(scope)) {
173       return filePath;
174     }
175     return null;
176   }
177
178   @Override
179   public void close() {
180     try {
181       indexCursor.close();
182     } catch (IOException e) {
183       LOG.atWarn().setMessage("unable to close the cursor, this may lead to database connexion leak. error is : {}").addArgument(e).log();
184     }
185     session.close();
186   }
187 }