123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750 |
- /*
- * SonarQube
- * Copyright (C) 2009-2021 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
- package org.sonar.db.issue;
-
- import com.google.common.base.Joiner;
- import com.google.common.base.MoreObjects;
- import com.google.common.base.Preconditions;
- import com.google.common.base.Splitter;
- import com.google.common.collect.ImmutableSet;
- import com.google.protobuf.InvalidProtocolBufferException;
- import java.io.Serializable;
- import java.util.Collection;
- import java.util.Date;
- import java.util.Optional;
- import java.util.Set;
- import javax.annotation.CheckForNull;
- import javax.annotation.Nullable;
- import org.apache.commons.lang.builder.ToStringBuilder;
- import org.apache.commons.lang.builder.ToStringStyle;
- import org.sonar.api.rule.RuleKey;
- import org.sonar.api.rules.RuleType;
- import org.sonar.api.utils.Duration;
- import org.sonar.api.utils.KeyValueFormat;
- import org.sonar.core.issue.DefaultIssue;
- import org.sonar.db.component.ComponentDto;
- import org.sonar.db.protobuf.DbIssues;
- import org.sonar.db.rule.RuleDefinitionDto;
-
- import static com.google.common.base.Preconditions.checkArgument;
- import static org.sonar.api.utils.DateUtils.dateToLong;
- import static org.sonar.api.utils.DateUtils.longToDate;
-
- public final class IssueDto implements Serializable {
-
- public static final int AUTHOR_MAX_SIZE = 255;
- private static final char TAGS_SEPARATOR = ',';
- private static final Joiner TAGS_JOINER = Joiner.on(TAGS_SEPARATOR).skipNulls();
- private static final Splitter TAGS_SPLITTER = Splitter.on(',').trimResults().omitEmptyStrings();
-
- private int type;
- private String kee;
- private String componentUuid;
- private String projectUuid;
- private String ruleUuid;
- private String severity;
- private boolean manualSeverity;
- private String message;
- private Integer line;
- private Double gap;
- private Long effort;
- private String status;
- private String resolution;
- private String checksum;
- private String assigneeUuid;
- private String assigneeLogin;
- private String authorLogin;
- private String issueAttributes;
- private String securityStandards;
- private byte[] locations;
- private long createdAt;
- private long updatedAt;
-
- // functional dates stored as Long
- private Long issueCreationDate;
- private Long issueUpdateDate;
- private Long issueCloseDate;
-
- /**
- * Temporary date used only during scan
- */
- private Long selectedAt;
-
- // joins
- private String ruleKey;
- private String ruleRepo;
- private boolean isExternal;
- private String language;
- private String componentKey;
- private String moduleUuid;
- private String moduleUuidPath;
- private String projectKey;
- private String filePath;
- private String tags;
- // populate only when retrieving closed issue for issue tracking
- private String closedChangeData;
-
- public IssueDto() {
- // nothing to do
- }
-
- /**
- * On batch side, component keys and uuid are useless
- */
- public static IssueDto toDtoForComputationInsert(DefaultIssue issue, String ruleUuid, long now) {
- return new IssueDto()
- .setKee(issue.key())
- .setType(issue.type())
- .setLine(issue.line())
- .setLocations((DbIssues.Locations) issue.getLocations())
- .setMessage(issue.message())
- .setGap(issue.gap())
- .setEffort(issue.effortInMinutes())
- .setResolution(issue.resolution())
- .setStatus(issue.status())
- .setSeverity(issue.severity())
- .setManualSeverity(issue.manualSeverity())
- .setChecksum(issue.checksum())
- .setAssigneeUuid(issue.assignee())
- .setRuleUuid(ruleUuid)
- .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule())
- .setExternal(issue.isFromExternalRuleEngine())
- .setTags(issue.tags())
- .setComponentUuid(issue.componentUuid())
- .setComponentKey(issue.componentKey())
- .setModuleUuid(issue.moduleUuid())
- .setModuleUuidPath(issue.moduleUuidPath())
- .setProjectUuid(issue.projectUuid())
- .setProjectKey(issue.projectKey())
- .setIssueAttributes(KeyValueFormat.format(issue.attributes()))
- .setAuthorLogin(issue.authorLogin())
- .setIssueCreationDate(issue.creationDate())
- .setIssueCloseDate(issue.closeDate())
- .setIssueUpdateDate(issue.updateDate())
- .setSelectedAt(issue.selectedAt())
-
- // technical dates
- .setCreatedAt(now)
- .setUpdatedAt(now);
- }
-
- /**
- * On server side, we need component keys and uuid
- */
- public static IssueDto toDtoForServerInsert(DefaultIssue issue, ComponentDto component, ComponentDto project, String ruleUuid, long now) {
- return toDtoForComputationInsert(issue, ruleUuid, now)
- .setComponent(component)
- .setProject(project);
- }
-
- public static IssueDto toDtoForUpdate(DefaultIssue issue, long now) {
- // Invariant fields, like key and rule, can't be updated
- return new IssueDto()
- .setKee(issue.key())
- .setType(issue.type())
- .setLine(issue.line())
- .setLocations((DbIssues.Locations) issue.getLocations())
- .setMessage(issue.message())
- .setGap(issue.gap())
- .setEffort(issue.effortInMinutes())
- .setResolution(issue.resolution())
- .setStatus(issue.status())
- .setSeverity(issue.severity())
- .setChecksum(issue.checksum())
- .setManualSeverity(issue.manualSeverity())
- .setAssigneeUuid(issue.assignee())
- .setIssueAttributes(KeyValueFormat.format(issue.attributes()))
- .setAuthorLogin(issue.authorLogin())
- .setRuleKey(issue.ruleKey().repository(), issue.ruleKey().rule())
- .setExternal(issue.isFromExternalRuleEngine())
- .setTags(issue.tags())
- .setComponentUuid(issue.componentUuid())
- .setComponentKey(issue.componentKey())
- .setModuleUuid(issue.moduleUuid())
- .setModuleUuidPath(issue.moduleUuidPath())
- .setProjectUuid(issue.projectUuid())
- .setProjectKey(issue.projectKey())
- .setIssueCreationDate(issue.creationDate())
- .setIssueCloseDate(issue.closeDate())
- .setIssueUpdateDate(issue.updateDate())
- .setSelectedAt(issue.selectedAt())
-
- // technical date
- .setUpdatedAt(now);
- }
-
- public String getKey() {
- return getKee();
- }
-
- public String getKee() {
- return kee;
- }
-
- public IssueDto setKee(String s) {
- this.kee = s;
- return this;
- }
-
- public IssueDto setComponent(ComponentDto component) {
- this.componentKey = component.getDbKey();
- this.componentUuid = component.uuid();
- this.moduleUuid = component.moduleUuid();
- this.moduleUuidPath = component.moduleUuidPath();
- this.filePath = component.path();
- return this;
- }
-
- public IssueDto setProject(ComponentDto project) {
- this.projectKey = project.getDbKey();
- this.projectUuid = project.uuid();
- return this;
- }
-
- public String getRuleUuid() {
- return ruleUuid;
- }
-
- /**
- * please use setRule(RuleDto rule)
- */
- public IssueDto setRuleUuid(String ruleUuid) {
- this.ruleUuid = ruleUuid;
- return this;
- }
-
- @CheckForNull
- public String getSeverity() {
- return severity;
- }
-
- public IssueDto setSeverity(@Nullable String s) {
- checkArgument(s == null || s.length() <= 10, "Value is too long for issue severity: %s", s);
- this.severity = s;
- return this;
- }
-
- public boolean isManualSeverity() {
- return manualSeverity;
- }
-
- public IssueDto setManualSeverity(boolean manualSeverity) {
- this.manualSeverity = manualSeverity;
- return this;
- }
-
- @CheckForNull
- public String getMessage() {
- return message;
- }
-
- public IssueDto setMessage(@Nullable String s) {
- checkArgument(s == null || s.length() <= 4000, "Value is too long for issue message: %s", s);
- this.message = s;
- return this;
- }
-
- @CheckForNull
- public Integer getLine() {
- return line;
- }
-
- public IssueDto setLine(@Nullable Integer i) {
- checkArgument(i == null || i >= 0, "Value of issue line must be positive: %d", i);
- this.line = i;
- return this;
- }
-
- @CheckForNull
- public Double getGap() {
- return gap;
- }
-
- public IssueDto setGap(@Nullable Double d) {
- checkArgument(d == null || d >= 0, "Value of issue gap must be positive: %d", d);
- this.gap = d;
- return this;
- }
-
- @CheckForNull
- public Long getEffort() {
- return effort;
- }
-
- public IssueDto setEffort(@Nullable Long l) {
- checkArgument(l == null || l >= 0, "Value of issue effort must be positive: %d", l);
- this.effort = l;
- return this;
- }
-
- public String getStatus() {
- return status;
- }
-
- public IssueDto setStatus(@Nullable String s) {
- checkArgument(s == null || s.length() <= 20, "Value is too long for issue status: %s", s);
- this.status = s;
- return this;
- }
-
- @CheckForNull
- public String getResolution() {
- return resolution;
- }
-
- public IssueDto setResolution(@Nullable String s) {
- checkArgument(s == null || s.length() <= 20, "Value is too long for issue resolution: %s", s);
- this.resolution = s;
- return this;
- }
-
- @CheckForNull
- public String getChecksum() {
- return checksum;
- }
-
- public IssueDto setChecksum(@Nullable String s) {
- checkArgument(s == null || s.length() <= 1000, "Value is too long for issue checksum: %s", s);
- this.checksum = s;
- return this;
- }
-
- @CheckForNull
- public String getAssigneeUuid() {
- return assigneeUuid;
- }
-
- public IssueDto setAssigneeUuid(@Nullable String s) {
- checkArgument(s == null || s.length() <= 255, "Value is too long for issue assigneeUuid: %s", s);
- this.assigneeUuid = s;
- return this;
- }
-
- @CheckForNull
- public String getAssigneeLogin() {
- return assigneeLogin;
- }
-
- public IssueDto setAssigneeLogin(@Nullable String s) {
- this.assigneeLogin = s;
- return this;
- }
-
- @CheckForNull
- public String getAuthorLogin() {
- return authorLogin;
- }
-
- public IssueDto setAuthorLogin(@Nullable String s) {
- checkArgument(s == null || s.length() <= AUTHOR_MAX_SIZE, "Value is too long for issue author login: %s", s);
- this.authorLogin = s;
- return this;
- }
-
- @CheckForNull
- public String getIssueAttributes() {
- return issueAttributes;
- }
-
- public IssueDto setIssueAttributes(@Nullable String s) {
- checkArgument(s == null || s.length() <= 4000, "Value is too long for issue attributes: %s", s);
- this.issueAttributes = s;
- return this;
- }
-
- public IssueDto setSecurityStandards(@Nullable String s) {
- this.securityStandards = s;
- return this;
- }
-
- public Set<String> getSecurityStandards() {
- return RuleDefinitionDto.deserializeSecurityStandardsString(securityStandards);
- }
-
- /**
- * Technical date
- */
- public long getCreatedAt() {
- return createdAt;
- }
-
- public IssueDto setCreatedAt(long createdAt) {
- this.createdAt = createdAt;
- return this;
- }
-
- /**
- * Technical date
- */
- public long getUpdatedAt() {
- return updatedAt;
- }
-
- public IssueDto setUpdatedAt(long updatedAt) {
- this.updatedAt = updatedAt;
- return this;
- }
-
- public Long getIssueCreationTime() {
- return issueCreationDate;
- }
-
- public IssueDto setIssueCreationTime(Long time) {
- this.issueCreationDate = time;
- return this;
- }
-
- public Date getIssueCreationDate() {
- return longToDate(issueCreationDate);
- }
-
- public IssueDto setIssueCreationDate(@Nullable Date d) {
- this.issueCreationDate = dateToLong(d);
- return this;
- }
-
- public Long getIssueUpdateTime() {
- return issueUpdateDate;
- }
-
- public IssueDto setIssueUpdateTime(Long time) {
- this.issueUpdateDate = time;
- return this;
- }
-
- public Date getIssueUpdateDate() {
- return longToDate(issueUpdateDate);
- }
-
- public IssueDto setIssueUpdateDate(@Nullable Date d) {
- this.issueUpdateDate = dateToLong(d);
- return this;
- }
-
- public Long getIssueCloseTime() {
- return issueCloseDate;
- }
-
- public IssueDto setIssueCloseTime(Long time) {
- this.issueCloseDate = time;
- return this;
- }
-
- public Date getIssueCloseDate() {
- return longToDate(issueCloseDate);
- }
-
- public IssueDto setIssueCloseDate(@Nullable Date d) {
- this.issueCloseDate = dateToLong(d);
- return this;
- }
-
- public String getRule() {
- return ruleKey;
- }
-
- public IssueDto setRule(RuleDefinitionDto rule) {
- Preconditions.checkNotNull(rule.getUuid(), "Rule must be persisted.");
- this.ruleUuid = rule.getUuid();
- this.ruleKey = rule.getRuleKey();
- this.ruleRepo = rule.getRepositoryKey();
- this.language = rule.getLanguage();
- this.isExternal = rule.isExternal();
- return this;
- }
-
- public String getRuleRepo() {
- return ruleRepo;
- }
-
- public RuleKey getRuleKey() {
- return RuleKey.of(ruleRepo, ruleKey);
- }
-
- public String getLanguage() {
- return language;
- }
-
- /**
- * Should only be used to persist in E/S
- * <p/>
- * Please use {@link #setRule(RuleDefinitionDto)} instead
- */
- public IssueDto setLanguage(String language) {
- this.language = language;
- return this;
- }
-
- public boolean isExternal() {
- return isExternal;
- }
-
- public IssueDto setExternal(boolean external) {
- isExternal = external;
- return this;
- }
-
- public String getComponentKey() {
- return componentKey;
- }
-
- /**
- * Should only be used to persist in E/S
- * <p/>
- * Please use {@link #setComponent(ComponentDto)} instead
- */
- public IssueDto setComponentKey(String componentKey) {
- this.componentKey = componentKey;
- return this;
- }
-
- /**
- * Can be null on Views or Devs
- */
- @CheckForNull
- public String getComponentUuid() {
- return componentUuid;
- }
-
- /**
- * Should only be used to persist in E/S
- * <p/>
- * Please use {@link #setComponent(ComponentDto)} instead
- */
- public IssueDto setComponentUuid(@Nullable String s) {
- checkArgument(s == null || s.length() <= 50, "Value is too long for column ISSUES.COMPONENT_UUID: %s", s);
- this.componentUuid = s;
- return this;
- }
-
- @CheckForNull
- public String getModuleUuid() {
- return moduleUuid;
- }
-
- /**
- * Should only be used to persist in E/S
- * <p/>
- * Please use {@link #setComponent(ComponentDto)} instead
- */
- public IssueDto setModuleUuid(@Nullable String moduleUuid) {
- this.moduleUuid = moduleUuid;
- return this;
- }
-
- @CheckForNull
- public String getModuleUuidPath() {
- return moduleUuidPath;
- }
-
- /**
- * Should only be used to persist in E/S
- * <p/>
- * Please use {@link #setComponent(ComponentDto)} instead
- */
- public IssueDto setModuleUuidPath(@Nullable String moduleUuidPath) {
- this.moduleUuidPath = moduleUuidPath;
- return this;
- }
-
- /**
- * Used by the issue tracking mechanism, but it should used the component uuid instead
- */
- public String getProjectKey() {
- return projectKey;
- }
-
- /**
- * Should only be used to persist in E/S
- * <p/>
- * Please use {@link #setProject(ComponentDto)} instead
- */
- public IssueDto setProjectKey(String projectKey) {
- this.projectKey = projectKey;
- return this;
- }
-
- public String getProjectUuid() {
- return projectUuid;
- }
-
- /**
- * Should only be used to persist in E/S
- * <p/>
- * Please use {@link #setProject(ComponentDto)} instead
- */
- public IssueDto setProjectUuid(String s) {
- checkArgument(s.length() <= 50, "Value is too long for column ISSUES.PROJECT_UUID: %s", s);
- this.projectUuid = s;
- return this;
- }
-
- @CheckForNull
- public Long getSelectedAt() {
- return selectedAt;
- }
-
- public IssueDto setSelectedAt(@Nullable Long d) {
- this.selectedAt = d;
- return this;
- }
-
- /**
- * Should only be used to persist in E/S
- * <p/>
- * Please use {@link #setRule(RuleDefinitionDto)} instead
- */
- public IssueDto setRuleKey(String repo, String rule) {
- this.ruleRepo = repo;
- this.ruleKey = rule;
- return this;
- }
-
- /**
- * Should only be used to persist in E/S
- * <p/>
- * Please use {@link #setProject(ComponentDto)} instead
- */
- public String getFilePath() {
- return filePath;
- }
-
- /**
- * Should only be used to persist in E/S
- * <p/>
- * Please use {@link #setProject(ComponentDto)} instead
- */
- public IssueDto setFilePath(String filePath) {
- this.filePath = filePath;
- return this;
- }
-
- public Set<String> getTags() {
- return ImmutableSet.copyOf(TAGS_SPLITTER.split(tags == null ? "" : tags));
- }
-
- public IssueDto setTags(@Nullable Collection<String> tags) {
- if (tags == null || tags.isEmpty()) {
- setTagsString(null);
- } else {
- setTagsString(TAGS_JOINER.join(tags));
- }
- return this;
- }
-
- public IssueDto setTagsString(@Nullable String s) {
- checkArgument(s == null || s.length() <= 4000, "Value is too long for column ISSUES.TAGS: %s", s);
- this.tags = s;
- return this;
- }
-
- public String getTagsString() {
- return tags;
- }
-
- @CheckForNull
- public byte[] getLocations() {
- return locations;
- }
-
- @CheckForNull
- public DbIssues.Locations parseLocations() {
- if (locations != null) {
- try {
- return DbIssues.Locations.parseFrom(locations);
- } catch (InvalidProtocolBufferException e) {
- throw new IllegalStateException(String.format("Fail to read ISSUES.LOCATIONS [KEE=%s]", kee), e);
- }
- }
- return null;
- }
-
- public IssueDto setLocations(@Nullable byte[] locations) {
- this.locations = locations;
- return this;
- }
-
- public IssueDto setLocations(@Nullable DbIssues.Locations locations) {
- if (locations == null) {
- this.locations = null;
- } else {
- this.locations = locations.toByteArray();
- }
- return this;
- }
-
- public int getType() {
- return type;
- }
-
- public IssueDto setType(int type) {
- this.type = type;
- return this;
- }
-
- public IssueDto setType(RuleType type) {
- this.type = type.getDbConstant();
- return this;
- }
-
- public Optional<String> getClosedChangeData() {
- return Optional.ofNullable(closedChangeData);
- }
-
- @Override
- public String toString() {
- return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
- }
-
- public DefaultIssue toDefaultIssue() {
- DefaultIssue issue = new DefaultIssue();
- issue.setKey(kee);
- issue.setType(RuleType.valueOf(type));
- issue.setStatus(status);
- issue.setResolution(resolution);
- issue.setMessage(message);
- issue.setGap(gap);
- issue.setEffort(effort != null ? Duration.create(effort) : null);
- issue.setLine(line);
- issue.setChecksum(checksum);
- issue.setSeverity(severity);
- issue.setAssigneeUuid(assigneeUuid);
- issue.setAttributes(KeyValueFormat.parse(MoreObjects.firstNonNull(issueAttributes, "")));
- issue.setComponentKey(componentKey);
- issue.setComponentUuid(componentUuid);
- issue.setModuleUuid(moduleUuid);
- issue.setModuleUuidPath(moduleUuidPath);
- issue.setProjectUuid(projectUuid);
- issue.setProjectKey(projectKey);
- issue.setManualSeverity(manualSeverity);
- issue.setRuleKey(getRuleKey());
- issue.setTags(getTags());
- issue.setLanguage(language);
- issue.setAuthorLogin(authorLogin);
- issue.setNew(false);
- issue.setCreationDate(longToDate(issueCreationDate));
- issue.setCloseDate(longToDate(issueCloseDate));
- issue.setUpdateDate(longToDate(issueUpdateDate));
- issue.setSelectedAt(selectedAt);
- issue.setLocations(parseLocations());
- issue.setIsFromExternalRuleEngine(isExternal);
- return issue;
- }
- }
|