From: Simon Brandhof Date: Wed, 24 Feb 2016 15:31:10 +0000 (+0100) Subject: Move IssueUpdater from sonar-core to sonar-server X-Git-Tag: 5.5-M5~6 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=1ef5de2df1f03e4d0a381e619353267c13448b21;p=sonarqube.git Move IssueUpdater from sonar-core to sonar-server --- diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueAssigner.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueAssigner.java index be57b30ca56..293c650dafe 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueAssigner.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueAssigner.java @@ -28,7 +28,7 @@ import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; +import org.sonar.server.issue.IssueUpdater; import org.sonar.server.computation.analysis.AnalysisMetadataHolder; import org.sonar.server.computation.component.Component; import org.sonar.server.computation.scm.ScmInfo; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueLifecycle.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueLifecycle.java index cc01fdf8611..33ab0b5f1d2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueLifecycle.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/IssueLifecycle.java @@ -25,7 +25,7 @@ import javax.annotation.Nullable; import org.sonar.api.issue.Issue; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; +import org.sonar.server.issue.IssueUpdater; import org.sonar.server.issue.workflow.IssueWorkflow; import org.sonar.core.util.Uuids; import org.sonar.server.computation.analysis.AnalysisMetadataHolder; diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtCalculator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtCalculator.java index adae0a498a9..3c9d7845be2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtCalculator.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/issue/NewDebtCalculator.java @@ -36,7 +36,7 @@ import javax.annotation.Nullable; import org.apache.commons.lang.time.DateUtils; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.FieldDiffs; -import org.sonar.core.issue.IssueUpdater; +import org.sonar.server.issue.IssueUpdater; import org.sonar.db.issue.IssueChangeDto; import org.sonar.server.computation.period.Period; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java index 28be9a3128d..a54f6ffc302 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/AbstractChangeTagsAction.java @@ -27,7 +27,6 @@ import org.sonar.api.issue.Issue; import org.sonar.api.issue.condition.IsUnResolved; import org.sonar.core.issue.DefaultIssue; import org.sonar.api.server.rule.RuleTagFormat; -import org.sonar.core.issue.IssueUpdater; import org.sonar.server.user.UserSession; import java.util.Collection; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java index 5f3d41c134c..b517e01fbe1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ActionService.java @@ -33,7 +33,6 @@ import org.sonar.api.issue.action.Function; import org.sonar.api.server.ServerSide; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.server.properties.ProjectSettingsFactory; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/AddTagsAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/AddTagsAction.java index ff93b503799..4e27a00de11 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/AddTagsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/AddTagsAction.java @@ -21,7 +21,6 @@ package org.sonar.server.issue; import com.google.common.collect.Sets; import org.sonar.api.server.ServerSide; -import org.sonar.core.issue.IssueUpdater; import java.util.Collection; import java.util.Set; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java index 38b9ab1eca5..19a44bb3d6f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/AssignAction.java @@ -28,7 +28,6 @@ import org.sonar.api.server.ServerSide; import org.sonar.api.user.User; import org.sonar.api.user.UserFinder; import org.sonar.core.issue.DefaultIssue; -import org.sonar.core.issue.IssueUpdater; import org.sonar.server.user.UserSession; @ServerSide diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java index d7075a443be..2c56f233220 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/CommentAction.java @@ -25,7 +25,6 @@ import java.util.Map; import org.sonar.api.issue.Issue; import org.sonar.api.server.ServerSide; import org.sonar.core.issue.DefaultIssue; -import org.sonar.core.issue.IssueUpdater; import org.sonar.server.user.UserSession; @ServerSide diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java index 8f868cac42a..e9e7e55faae 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueChangelogFormatter.java @@ -28,7 +28,6 @@ import org.sonar.api.i18n.I18n; import org.sonar.core.issue.FieldDiffs; import org.sonar.api.utils.Duration; import org.sonar.api.utils.Durations; -import org.sonar.core.issue.IssueUpdater; import org.sonar.server.user.UserSession; import static com.google.common.collect.Lists.newArrayList; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueCommentService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueCommentService.java index fc83784aaa7..6fb50b2a36f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueCommentService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueCommentService.java @@ -22,13 +22,11 @@ package org.sonar.server.issue; import com.google.common.base.Objects; import com.google.common.base.Strings; import org.apache.commons.lang.StringUtils; -import org.sonar.api.server.ServerSide; import org.sonar.api.issue.IssueComment; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.DefaultIssueComment; import org.sonar.core.issue.IssueChangeContext; import org.sonar.api.utils.System2; -import org.sonar.core.issue.IssueUpdater; import org.sonar.db.issue.IssueChangeDto; import org.sonar.db.DbSession; import org.sonar.db.DbClient; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java index c9bee6d5f74..09c2c041d2c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueService.java @@ -44,7 +44,6 @@ import org.sonar.api.web.UserRole; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.DefaultIssueBuilder; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; import org.sonar.server.issue.workflow.IssueWorkflow; import org.sonar.server.issue.workflow.Transition; import org.sonar.db.DbClient; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueUpdater.java new file mode 100644 index 00000000000..303a430134c --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueUpdater.java @@ -0,0 +1,357 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.server.issue; + +import com.google.common.base.Function; +import com.google.common.base.Joiner; +import com.google.common.base.Objects; +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.Sets; +import java.util.Calendar; +import java.util.Collection; +import java.util.Date; +import java.util.Set; +import javax.annotation.Nullable; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.time.DateUtils; +import org.sonar.api.issue.ActionPlan; +import org.sonar.api.server.ServerSide; +import org.sonar.api.server.rule.RuleTagFormat; +import org.sonar.api.user.User; +import org.sonar.api.utils.Duration; +import org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.DefaultIssueComment; +import org.sonar.core.issue.IssueChangeContext; + +import static com.google.common.base.Preconditions.checkState; +import static com.google.common.base.Strings.isNullOrEmpty; + +/** + * Updates issue fields and chooses if changes must be kept in history. + */ +@ServerSide +public class IssueUpdater { + + public static final String UNUSED = ""; + public static final String SEVERITY = "severity"; + public static final String ASSIGNEE = "assignee"; + public static final String RESOLUTION = "resolution"; + public static final String STATUS = "status"; + public static final String AUTHOR = "author"; + public static final String ACTION_PLAN = "actionPlan"; + public static final String TECHNICAL_DEBT = "technicalDebt"; + public static final String TAGS = "tags"; + + private static final Joiner CHANGELOG_TAG_JOINER = Joiner.on(" ").skipNulls(); + + public boolean setSeverity(DefaultIssue issue, String severity, IssueChangeContext context) { + if (issue.manualSeverity()) { + throw new IllegalStateException("Severity can't be changed"); + } + if (!Objects.equal(severity, issue.severity())) { + issue.setFieldChange(context, SEVERITY, issue.severity(), severity); + issue.setSeverity(severity); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + return true; + } + return false; + } + + public boolean setPastSeverity(DefaultIssue issue, @Nullable String previousSeverity, IssueChangeContext context) { + String currentSeverity = issue.severity(); + issue.setSeverity(previousSeverity); + return setSeverity(issue, currentSeverity, context); + } + + public boolean setManualSeverity(DefaultIssue issue, String severity, IssueChangeContext context) { + if (!issue.manualSeverity() || !Objects.equal(severity, issue.severity())) { + issue.setFieldChange(context, SEVERITY, issue.severity(), severity); + issue.setSeverity(severity); + issue.setManualSeverity(true); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + issue.setSendNotifications(true); + return true; + } + return false; + } + + public boolean assign(DefaultIssue issue, @Nullable User user, IssueChangeContext context) { + String sanitizedAssignee = null; + if (user != null) { + sanitizedAssignee = StringUtils.defaultIfBlank(user.login(), null); + } + if (!Objects.equal(sanitizedAssignee, issue.assignee())) { + String newAssigneeName = user != null ? user.name() : null; + issue.setFieldChange(context, ASSIGNEE, UNUSED, newAssigneeName); + issue.setAssignee(sanitizedAssignee); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + issue.setSendNotifications(true); + return true; + } + return false; + } + + /** + * Used to set the assignee when it was null + */ + public boolean setNewAssignee(DefaultIssue issue, @Nullable String newAssignee, IssueChangeContext context) { + if (newAssignee == null) { + return false; + } + checkState(issue.assignee() == null, "It's not possible to update the assignee with this method, please use assign()"); + issue.setFieldChange(context, ASSIGNEE, UNUSED, newAssignee); + issue.setAssignee(newAssignee); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + issue.setSendNotifications(true); + return true; + } + + public boolean setLine(DefaultIssue issue, @Nullable Integer line) { + if (!Objects.equal(line, issue.line())) { + issue.setLine(line); + issue.setChanged(true); + return true; + } + return false; + } + + public boolean setPastLine(DefaultIssue issue, @Nullable Integer previousLine) { + Integer currentLine = issue.line(); + issue.setLine(previousLine); + return setLine(issue, currentLine); + } + + public boolean setLocations(DefaultIssue issue, @Nullable Object locations) { + if (!Objects.equal(locations, issue.getLocations())) { + issue.setLocations(locations); + issue.setChanged(true); + return true; + } + return false; + } + + public boolean setPastLocations(DefaultIssue issue, @Nullable Object previousLocations) { + Object currentLocations = issue.getLocations(); + issue.setLocations(previousLocations); + return setLocations(issue, currentLocations); + + } + + public boolean setResolution(DefaultIssue issue, @Nullable String resolution, IssueChangeContext context) { + if (!Objects.equal(resolution, issue.resolution())) { + issue.setFieldChange(context, RESOLUTION, issue.resolution(), resolution); + issue.setResolution(resolution); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + issue.setSendNotifications(true); + return true; + } + return false; + } + + public boolean setStatus(DefaultIssue issue, String status, IssueChangeContext context) { + if (!Objects.equal(status, issue.status())) { + issue.setFieldChange(context, STATUS, issue.status(), status); + issue.setStatus(status); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + issue.setSendNotifications(true); + return true; + } + return false; + } + + public boolean setAuthorLogin(DefaultIssue issue, @Nullable String authorLogin, IssueChangeContext context) { + if (!Objects.equal(authorLogin, issue.authorLogin())) { + issue.setFieldChange(context, AUTHOR, issue.authorLogin(), authorLogin); + issue.setAuthorLogin(authorLogin); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + // do not send notifications to prevent spam when installing the developer cockpit plugin + return true; + } + return false; + } + + /** + * Used to set the author when it was null + */ + public boolean setNewAuthor(DefaultIssue issue, @Nullable String newAuthorLogin, IssueChangeContext context) { + if (isNullOrEmpty(newAuthorLogin)) { + return false; + } + checkState(issue.authorLogin() == null, "It's not possible to update the author with this method, please use setAuthorLogin()"); + issue.setFieldChange(context, AUTHOR, null, newAuthorLogin); + issue.setAuthorLogin(newAuthorLogin); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + // do not send notifications to prevent spam when installing the developer cockpit plugin + return true; + } + + public boolean setMessage(DefaultIssue issue, @Nullable String s, IssueChangeContext context) { + if (!Objects.equal(s, issue.message())) { + issue.setMessage(s); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + return true; + } + return false; + } + + public boolean setPastMessage(DefaultIssue issue, @Nullable String previousMessage, IssueChangeContext context) { + String currentMessage = issue.message(); + issue.setMessage(previousMessage); + return setMessage(issue, currentMessage, context); + } + + public void addComment(DefaultIssue issue, String text, IssueChangeContext context) { + issue.addComment(DefaultIssueComment.create(issue.key(), context.login(), text)); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + } + + public void setCloseDate(DefaultIssue issue, @Nullable Date d, IssueChangeContext context) { + Date dateWithoutMilliseconds = d == null ? null : DateUtils.truncate(d, Calendar.SECOND); + if (!Objects.equal(dateWithoutMilliseconds, issue.closeDate())) { + issue.setCloseDate(d); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + } + } + + public boolean setEffortToFix(DefaultIssue issue, @Nullable Double d, IssueChangeContext context) { + if (!Objects.equal(d, issue.effortToFix())) { + issue.setEffortToFix(d); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + // Do not send notifications to prevent spam when installing the SQALE plugin, + // and do not complete the changelog (for the moment) + return true; + } + return false; + } + + public boolean setPastEffortToFix(DefaultIssue issue, @Nullable Double previousEffort, IssueChangeContext context) { + Double currentEffort = issue.effortToFix(); + issue.setEffortToFix(previousEffort); + return setEffortToFix(issue, currentEffort, context); + } + + public boolean setTechnicalDebt(DefaultIssue issue, @Nullable Duration value, IssueChangeContext context) { + Duration oldValue = issue.debt(); + if (!Objects.equal(value, oldValue)) { + issue.setDebt(value != null ? value : null); + issue.setFieldChange(context, TECHNICAL_DEBT, oldValue != null ? oldValue.toMinutes() : null, value != null ? value.toMinutes() : null); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + return true; + } + return false; + } + + public boolean setPastTechnicalDebt(DefaultIssue issue, @Nullable Duration previousTechnicalDebt, IssueChangeContext context) { + Duration currentTechnicalDebt = issue.debt(); + issue.setDebt(previousTechnicalDebt); + return setTechnicalDebt(issue, currentTechnicalDebt, context); + } + + public boolean setAttribute(DefaultIssue issue, String key, @Nullable String value, IssueChangeContext context) { + String oldValue = issue.attribute(key); + if (!Objects.equal(oldValue, value)) { + issue.setFieldChange(context, key, oldValue, value); + issue.setAttribute(key, value); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + return true; + } + return false; + } + + public boolean plan(DefaultIssue issue, @Nullable ActionPlan actionPlan, IssueChangeContext context) { + String sanitizedActionPlanKey = null; + if (actionPlan != null) { + sanitizedActionPlanKey = StringUtils.defaultIfBlank(actionPlan.key(), null); + } + if (!Objects.equal(sanitizedActionPlanKey, issue.actionPlanKey())) { + String newActionPlanName = actionPlan != null ? actionPlan.name() : null; + issue.setFieldChange(context, ACTION_PLAN, UNUSED, newActionPlanName); + issue.setActionPlanKey(sanitizedActionPlanKey); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + issue.setSendNotifications(true); + return true; + } + return false; + } + + public boolean setProject(DefaultIssue issue, String projectKey, IssueChangeContext context) { + if (!Objects.equal(projectKey, issue.projectKey())) { + issue.setProjectKey(projectKey); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + return true; + } + return false; + } + + public boolean setPastProject(DefaultIssue issue, String previousKey, IssueChangeContext context) { + String currentProjectKey = issue.projectKey(); + issue.setProjectKey(previousKey); + return setProject(issue, currentProjectKey, context); + } + + public boolean setTags(DefaultIssue issue, Collection tags, IssueChangeContext context) { + Set newTags = Sets.newHashSet(Collections2.transform( + Collections2.filter(tags, new Predicate() { + @Override + public boolean apply(@Nullable String tag) { + return tag != null && !tag.isEmpty(); + } + }), new Function() { + @Override + public String apply(String tag) { + String lowerCaseTag = tag.toLowerCase(); + RuleTagFormat.validate(lowerCaseTag); + return lowerCaseTag; + } + })); + + Set oldTags = Sets.newHashSet(issue.tags()); + + if (!oldTags.equals(newTags)) { + issue.setFieldChange(context, TAGS, + oldTags.isEmpty() ? null : CHANGELOG_TAG_JOINER.join(oldTags), + newTags.isEmpty() ? null : CHANGELOG_TAG_JOINER.join(newTags)); + issue.setTags(newTags); + issue.setUpdateDate(context.date()); + issue.setChanged(true); + issue.setSendNotifications(true); + return true; + } + return false; + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java index e86e77d1fd2..7d82e9be151 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/PlanAction.java @@ -27,7 +27,6 @@ import org.sonar.api.issue.Issue; import org.sonar.api.issue.condition.IsUnResolved; import org.sonar.api.server.ServerSide; import org.sonar.core.issue.DefaultIssue; -import org.sonar.core.issue.IssueUpdater; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.user.UserSession; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/RemoveTagsAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/RemoveTagsAction.java index 5798428b90f..c9cb09c465b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/RemoveTagsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/RemoveTagsAction.java @@ -21,7 +21,6 @@ package org.sonar.server.issue; import com.google.common.collect.Sets; import org.sonar.api.server.ServerSide; -import org.sonar.core.issue.IssueUpdater; import java.util.Collection; import java.util.Set; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java index 9bc86b568eb..75656f961ad 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/SetSeverityAction.java @@ -28,7 +28,6 @@ import org.sonar.api.issue.condition.IsUnResolved; import org.sonar.api.server.ServerSide; import org.sonar.api.web.UserRole; import org.sonar.core.issue.DefaultIssue; -import org.sonar.core.issue.IssueUpdater; import org.sonar.server.user.UserSession; @ServerSide diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java index 4ee93f6ded3..f425dd87295 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/actionplan/ActionPlanService.java @@ -33,7 +33,7 @@ import org.sonar.core.issue.ActionPlanStats; import org.sonar.core.issue.DefaultActionPlan; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; +import org.sonar.server.issue.IssueUpdater; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ResourceDao; diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/workflow/FunctionExecutor.java b/server/sonar-server/src/main/java/org/sonar/server/issue/workflow/FunctionExecutor.java index 9b9c88b303a..392bb204fd7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/workflow/FunctionExecutor.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/workflow/FunctionExecutor.java @@ -25,7 +25,7 @@ import org.sonar.api.server.ServerSide; import org.sonar.api.user.User; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; +import org.sonar.server.issue.IssueUpdater; @ServerSide public class FunctionExecutor { diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/workflow/IssueWorkflow.java b/server/sonar-server/src/main/java/org/sonar/server/issue/workflow/IssueWorkflow.java index bb65c579d52..b46048b6d66 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/workflow/IssueWorkflow.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/workflow/IssueWorkflow.java @@ -29,7 +29,7 @@ import org.sonar.api.server.ServerSide; import org.sonar.api.web.UserRole; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; +import org.sonar.server.issue.IssueUpdater; @ServerSide public class IssueWorkflow implements Startable { diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 9236429f5ba..a6f46242d5f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -31,7 +31,7 @@ import org.sonar.api.rules.AnnotationRuleParser; import org.sonar.api.rules.XMLRuleParser; import org.sonar.api.server.rule.RulesDefinitionXmlLoader; import org.sonar.core.component.DefaultResourceTypes; -import org.sonar.core.issue.IssueUpdater; +import org.sonar.server.issue.IssueUpdater; import org.sonar.server.issue.workflow.FunctionExecutor; import org.sonar.server.issue.workflow.IssueWorkflow; import org.sonar.core.timemachine.Periods; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java index 1abcbc0d81b..5a929c0d149 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueAssignerTest.java @@ -23,7 +23,7 @@ import org.junit.Test; import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; import org.sonar.core.issue.DefaultIssue; -import org.sonar.core.issue.IssueUpdater; +import org.sonar.server.issue.IssueUpdater; import org.sonar.server.computation.analysis.AnalysisMetadataHolderRule; import org.sonar.server.computation.component.Component; import org.sonar.server.computation.scm.Changeset; diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueLifecycleTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueLifecycleTest.java index 844d0ff8bbf..f9957a41dce 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueLifecycleTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/issue/IssueLifecycleTest.java @@ -25,7 +25,7 @@ import org.junit.Test; import org.sonar.api.utils.Duration; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; +import org.sonar.server.issue.IssueUpdater; import org.sonar.server.issue.workflow.IssueWorkflow; import org.sonar.db.protobuf.DbCommons; import org.sonar.db.protobuf.DbIssues; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java index dac1cf20a82..ed6f5695b32 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ActionServiceTest.java @@ -30,7 +30,6 @@ import org.sonar.api.issue.action.Function; import org.sonar.api.issue.condition.Condition; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/AddTagsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/AddTagsActionTest.java index 764afc96b86..ec27342bf6c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/AddTagsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/AddTagsActionTest.java @@ -27,7 +27,6 @@ import org.junit.rules.ExpectedException; import org.mockito.Matchers; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; import java.util.Collection; import java.util.Map; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java index b5a3c05a4c2..e9462422ecd 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/AssignActionTest.java @@ -28,7 +28,6 @@ import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; import org.sonar.api.user.User; import org.sonar.api.user.UserFinder; -import org.sonar.core.issue.IssueUpdater; import org.sonar.core.user.DefaultUser; import org.sonar.server.user.ThreadLocalUserSession; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/CommentActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/CommentActionTest.java index 95a93d5d25f..dea9d87c82c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/CommentActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/CommentActionTest.java @@ -26,7 +26,6 @@ import org.junit.Test; import org.sonar.api.issue.Issue; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; import org.sonar.server.tester.AnonymousMockUserSession; import static com.google.common.collect.Maps.newHashMap; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceTest.java index c754980f12a..c02a79c08ea 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueCommentServiceTest.java @@ -30,7 +30,6 @@ import org.mockito.runners.MockitoJUnitRunner; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.DefaultIssueComment; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; import org.sonar.core.permission.GlobalPermissions; import org.sonar.db.DbClient; import org.sonar.db.DbSession; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueUpdaterTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueUpdaterTest.java new file mode 100644 index 00000000000..b536a6d1c7c --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueUpdaterTest.java @@ -0,0 +1,578 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.server.issue; + +import java.util.Date; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.issue.ActionPlan; +import org.sonar.api.user.User; +import org.sonar.api.utils.Duration; +import org.sonar.core.issue.DefaultActionPlan; +import org.sonar.core.issue.DefaultIssue; +import org.sonar.core.issue.FieldDiffs; +import org.sonar.core.issue.IssueChangeContext; +import org.sonar.core.user.DefaultUser; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.server.issue.IssueUpdater.ACTION_PLAN; +import static org.sonar.server.issue.IssueUpdater.ASSIGNEE; +import static org.sonar.server.issue.IssueUpdater.RESOLUTION; +import static org.sonar.server.issue.IssueUpdater.SEVERITY; +import static org.sonar.server.issue.IssueUpdater.STATUS; +import static org.sonar.server.issue.IssueUpdater.TECHNICAL_DEBT; +import static org.sonar.server.issue.IssueUpdater.UNUSED; + +public class IssueUpdaterTest { + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + DefaultIssue issue = new DefaultIssue(); + IssueChangeContext context = IssueChangeContext.createUser(new Date(), "emmerik"); + + IssueUpdater updater; + + @Before + public void setUp() { + updater = new IssueUpdater(); + } + + @Test + public void assign() { + User user = new DefaultUser().setLogin("emmerik").setName("Emmerik"); + + boolean updated = updater.assign(issue, user, context); + assertThat(updated).isTrue(); + assertThat(issue.assignee()).isEqualTo("emmerik"); + assertThat(issue.mustSendNotifications()).isTrue(); + FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE); + assertThat(diff.oldValue()).isEqualTo(UNUSED); + assertThat(diff.newValue()).isEqualTo("Emmerik"); + } + + @Test + public void unassign() { + issue.setAssignee("morgan"); + boolean updated = updater.assign(issue, null, context); + assertThat(updated).isTrue(); + assertThat(issue.assignee()).isNull(); + assertThat(issue.mustSendNotifications()).isTrue(); + FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE); + assertThat(diff.oldValue()).isEqualTo(UNUSED); + assertThat(diff.newValue()).isNull(); + } + + @Test + public void change_assignee() { + User user = new DefaultUser().setLogin("emmerik").setName("Emmerik"); + + issue.setAssignee("morgan"); + boolean updated = updater.assign(issue, user, context); + assertThat(updated).isTrue(); + assertThat(issue.assignee()).isEqualTo("emmerik"); + assertThat(issue.mustSendNotifications()).isTrue(); + FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE); + assertThat(diff.oldValue()).isEqualTo(UNUSED); + assertThat(diff.newValue()).isEqualTo("Emmerik"); + } + + @Test + public void not_change_assignee() { + User user = new DefaultUser().setLogin("morgan").setName("Morgan"); + + issue.setAssignee("morgan"); + boolean updated = updater.assign(issue, user, context); + assertThat(updated).isFalse(); + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void set_new_assignee() throws Exception { + boolean updated = updater.setNewAssignee(issue, "simon", context); + assertThat(updated).isTrue(); + assertThat(issue.assignee()).isEqualTo("simon"); + assertThat(issue.mustSendNotifications()).isTrue(); + FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE); + assertThat(diff.oldValue()).isEqualTo(UNUSED); + assertThat(diff.newValue()).isEqualTo("simon"); + } + + @Test + public void not_set_new_assignee_if_new_assignee_is_null() throws Exception { + boolean updated = updater.setNewAssignee(issue, null, context); + assertThat(updated).isFalse(); + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void fail_with_ISE_when_setting_new_assignee_on_already_assigned_issue() throws Exception { + issue.setAssignee("simon"); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("It's not possible to update the assignee with this method, please use assign()"); + updater.setNewAssignee(issue, "julien", context); + } + + @Test + public void set_severity() { + boolean updated = updater.setSeverity(issue, "BLOCKER", context); + assertThat(updated).isTrue(); + assertThat(issue.severity()).isEqualTo("BLOCKER"); + assertThat(issue.manualSeverity()).isFalse(); + assertThat(issue.mustSendNotifications()).isFalse(); + + FieldDiffs.Diff diff = issue.currentChange().get(SEVERITY); + assertThat(diff.oldValue()).isNull(); + assertThat(diff.newValue()).isEqualTo("BLOCKER"); + } + + @Test + public void set_past_severity() { + issue.setSeverity("BLOCKER"); + boolean updated = updater.setPastSeverity(issue, "INFO", context); + assertThat(updated).isTrue(); + assertThat(issue.severity()).isEqualTo("BLOCKER"); + assertThat(issue.mustSendNotifications()).isFalse(); + + FieldDiffs.Diff diff = issue.currentChange().get(SEVERITY); + assertThat(diff.oldValue()).isEqualTo("INFO"); + assertThat(diff.newValue()).isEqualTo("BLOCKER"); + } + + @Test + public void update_severity() { + issue.setSeverity("BLOCKER"); + boolean updated = updater.setSeverity(issue, "MINOR", context); + + assertThat(updated).isTrue(); + assertThat(issue.severity()).isEqualTo("MINOR"); + assertThat(issue.mustSendNotifications()).isFalse(); + FieldDiffs.Diff diff = issue.currentChange().get(SEVERITY); + assertThat(diff.oldValue()).isEqualTo("BLOCKER"); + assertThat(diff.newValue()).isEqualTo("MINOR"); + } + + @Test + public void not_change_severity() { + issue.setSeverity("MINOR"); + boolean updated = updater.setSeverity(issue, "MINOR", context); + assertThat(updated).isFalse(); + assertThat(issue.mustSendNotifications()).isFalse(); + assertThat(issue.currentChange()).isNull(); + } + + @Test + public void not_revert_manual_severity() { + issue.setSeverity("MINOR").setManualSeverity(true); + try { + updater.setSeverity(issue, "MAJOR", context); + } catch (IllegalStateException e) { + assertThat(e).hasMessage("Severity can't be changed"); + } + } + + @Test + public void set_manual_severity() { + issue.setSeverity("BLOCKER"); + boolean updated = updater.setManualSeverity(issue, "MINOR", context); + + assertThat(updated).isTrue(); + assertThat(issue.severity()).isEqualTo("MINOR"); + assertThat(issue.manualSeverity()).isTrue(); + assertThat(issue.mustSendNotifications()).isTrue(); + FieldDiffs.Diff diff = issue.currentChange().get(SEVERITY); + assertThat(diff.oldValue()).isEqualTo("BLOCKER"); + assertThat(diff.newValue()).isEqualTo("MINOR"); + } + + @Test + public void not_change_manual_severity() { + issue.setSeverity("MINOR").setManualSeverity(true); + boolean updated = updater.setManualSeverity(issue, "MINOR", context); + assertThat(updated).isFalse(); + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void set_line() { + boolean updated = updater.setLine(issue, 123); + assertThat(updated).isTrue(); + assertThat(issue.line()).isEqualTo(123); + assertThat(issue.mustSendNotifications()).isFalse(); + // do not save change + assertThat(issue.currentChange()).isNull(); + } + + @Test + public void set_past_line() { + issue.setLine(42); + boolean updated = updater.setPastLine(issue, 123); + assertThat(updated).isTrue(); + assertThat(issue.line()).isEqualTo(42); + assertThat(issue.mustSendNotifications()).isFalse(); + + // do not save change + assertThat(issue.currentChange()).isNull(); + } + + @Test + public void line_is_not_changed() { + issue.setLine(123); + boolean updated = updater.setLine(issue, 123); + assertThat(updated).isFalse(); + assertThat(issue.line()).isEqualTo(123); + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void change_locations() { + issue.setLocations("[1-3]"); + boolean updated = updater.setLocations(issue, "[1-4]"); + assertThat(updated).isTrue(); + assertThat(issue.getLocations()).isEqualTo("[1-4]"); + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void do_not_change_locations() { + issue.setLocations("[1-3]"); + boolean updated = updater.setLocations(issue, "[1-3]"); + assertThat(updated).isFalse(); + assertThat(issue.getLocations()).isEqualTo("[1-3]"); + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void set_locations_for_the_first_time() { + issue.setLocations(null); + boolean updated = updater.setLocations(issue, "[1-4]"); + assertThat(updated).isTrue(); + assertThat(issue.getLocations()).isEqualTo("[1-4]"); + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void set_resolution() { + boolean updated = updater.setResolution(issue, "OPEN", context); + assertThat(updated).isTrue(); + assertThat(issue.resolution()).isEqualTo("OPEN"); + + FieldDiffs.Diff diff = issue.currentChange().get(RESOLUTION); + assertThat(diff.oldValue()).isNull(); + assertThat(diff.newValue()).isEqualTo("OPEN"); + assertThat(issue.mustSendNotifications()).isTrue(); + } + + @Test + public void not_change_resolution() { + issue.setResolution("FIXED"); + boolean updated = updater.setResolution(issue, "FIXED", context); + assertThat(updated).isFalse(); + assertThat(issue.resolution()).isEqualTo("FIXED"); + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void set_status() { + boolean updated = updater.setStatus(issue, "OPEN", context); + assertThat(updated).isTrue(); + assertThat(issue.status()).isEqualTo("OPEN"); + + FieldDiffs.Diff diff = issue.currentChange().get(STATUS); + assertThat(diff.oldValue()).isNull(); + assertThat(diff.newValue()).isEqualTo("OPEN"); + assertThat(issue.mustSendNotifications()).isTrue(); + } + + @Test + public void not_change_status() { + issue.setStatus("CLOSED"); + boolean updated = updater.setStatus(issue, "CLOSED", context); + assertThat(updated).isFalse(); + assertThat(issue.status()).isEqualTo("CLOSED"); + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void set_new_attribute_value() { + boolean updated = updater.setAttribute(issue, "JIRA", "FOO-123", context); + assertThat(updated).isTrue(); + assertThat(issue.attribute("JIRA")).isEqualTo("FOO-123"); + assertThat(issue.currentChange().diffs()).hasSize(1); + assertThat(issue.currentChange().get("JIRA").oldValue()).isNull(); + assertThat(issue.currentChange().get("JIRA").newValue()).isEqualTo("FOO-123"); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void unset_attribute() { + issue.setAttribute("JIRA", "FOO-123"); + boolean updated = updater.setAttribute(issue, "JIRA", null, context); + assertThat(updated).isTrue(); + assertThat(issue.attribute("JIRA")).isNull(); + assertThat(issue.currentChange().diffs()).hasSize(1); + assertThat(issue.currentChange().get("JIRA").oldValue()).isEqualTo("FOO-123"); + assertThat(issue.currentChange().get("JIRA").newValue()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void not_update_attribute() { + issue.setAttribute("JIRA", "FOO-123"); + boolean updated = updater.setAttribute(issue, "JIRA", "FOO-123", context); + assertThat(updated).isFalse(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void plan_with_no_existing_plan() { + ActionPlan newActionPlan = DefaultActionPlan.create("newName"); + + boolean updated = updater.plan(issue, newActionPlan, context); + assertThat(updated).isTrue(); + assertThat(issue.actionPlanKey()).isEqualTo(newActionPlan.key()); + + FieldDiffs.Diff diff = issue.currentChange().get(ACTION_PLAN); + assertThat(diff.oldValue()).isEqualTo(UNUSED); + assertThat(diff.newValue()).isEqualTo("newName"); + assertThat(issue.mustSendNotifications()).isTrue(); + } + + @Test + public void plan_with_existing_plan() { + issue.setActionPlanKey("formerActionPlan"); + + ActionPlan newActionPlan = DefaultActionPlan.create("newName").setKey("newKey"); + + boolean updated = updater.plan(issue, newActionPlan, context); + assertThat(updated).isTrue(); + assertThat(issue.actionPlanKey()).isEqualTo(newActionPlan.key()); + + FieldDiffs.Diff diff = issue.currentChange().get(ACTION_PLAN); + assertThat(diff.oldValue()).isEqualTo(UNUSED); + assertThat(diff.newValue()).isEqualTo("newName"); + assertThat(issue.mustSendNotifications()).isTrue(); + } + + @Test + public void unplan() { + issue.setActionPlanKey("formerActionPlan"); + + boolean updated = updater.plan(issue, null, context); + assertThat(updated).isTrue(); + assertThat(issue.actionPlanKey()).isNull(); + + FieldDiffs.Diff diff = issue.currentChange().get(ACTION_PLAN); + assertThat(diff.oldValue()).isEqualTo(UNUSED); + assertThat(diff.newValue()).isNull(); + assertThat(issue.mustSendNotifications()).isTrue(); + } + + @Test + public void not_plan_again() { + issue.setActionPlanKey("existingActionPlan"); + + ActionPlan newActionPlan = DefaultActionPlan.create("existingActionPlan").setKey("existingActionPlan"); + + boolean updated = updater.plan(issue, newActionPlan, context); + assertThat(updated).isFalse(); + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void set_effort_to_fix() { + boolean updated = updater.setEffortToFix(issue, 3.14, context); + assertThat(updated).isTrue(); + assertThat(issue.isChanged()).isTrue(); + assertThat(issue.effortToFix()).isEqualTo(3.14); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void not_set_effort_to_fix_if_unchanged() { + issue.setEffortToFix(3.14); + boolean updated = updater.setEffortToFix(issue, 3.14, context); + assertThat(updated).isFalse(); + assertThat(issue.isChanged()).isFalse(); + assertThat(issue.effortToFix()).isEqualTo(3.14); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void set_past_effort() { + issue.setEffortToFix(3.14); + boolean updated = updater.setPastEffortToFix(issue, 1.0, context); + assertThat(updated).isTrue(); + assertThat(issue.effortToFix()).isEqualTo(3.14); + + // do not save change + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void set_past_technical_debt() { + Duration newDebt = Duration.create(15 * 8 * 60); + Duration previousDebt = Duration.create(10 * 8 * 60); + issue.setDebt(newDebt); + boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context); + assertThat(updated).isTrue(); + assertThat(issue.debt()).isEqualTo(newDebt); + assertThat(issue.mustSendNotifications()).isFalse(); + + FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT); + assertThat(diff.oldValue()).isEqualTo(10L * 8 * 60); + assertThat(diff.newValue()).isEqualTo(15L * 8 * 60); + } + + @Test + public void set_past_technical_debt_without_previous_value() { + Duration newDebt = Duration.create(15 * 8 * 60); + issue.setDebt(newDebt); + boolean updated = updater.setPastTechnicalDebt(issue, null, context); + assertThat(updated).isTrue(); + assertThat(issue.debt()).isEqualTo(newDebt); + assertThat(issue.mustSendNotifications()).isFalse(); + + FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT); + assertThat(diff.oldValue()).isNull(); + assertThat(diff.newValue()).isEqualTo(15L * 8 * 60); + } + + @Test + public void set_past_technical_debt_with_null_new_value() { + issue.setDebt(null); + Duration previousDebt = Duration.create(10 * 8 * 60); + boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context); + assertThat(updated).isTrue(); + assertThat(issue.debt()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + + FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT); + assertThat(diff.oldValue()).isEqualTo(10L * 8 * 60); + assertThat(diff.newValue()).isNull(); + } + + @Test + public void set_message() { + boolean updated = updater.setMessage(issue, "the message", context); + assertThat(updated).isTrue(); + assertThat(issue.isChanged()).isTrue(); + assertThat(issue.message()).isEqualTo("the message"); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void set_past_message() { + issue.setMessage("new message"); + boolean updated = updater.setPastMessage(issue, "past message", context); + assertThat(updated).isTrue(); + assertThat(issue.message()).isEqualTo("new message"); + + // do not save change + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void set_author() { + boolean updated = updater.setAuthorLogin(issue, "eric", context); + assertThat(updated).isTrue(); + assertThat(issue.authorLogin()).isEqualTo("eric"); + + FieldDiffs.Diff diff = issue.currentChange().get("author"); + assertThat(diff.oldValue()).isNull(); + assertThat(diff.newValue()).isEqualTo("eric"); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void set_new_author() throws Exception { + boolean updated = updater.setNewAuthor(issue, "simon", context); + assertThat(updated).isTrue(); + + FieldDiffs.Diff diff = issue.currentChange().get("author"); + assertThat(diff.oldValue()).isNull(); + assertThat(diff.newValue()).isEqualTo("simon"); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void not_set_new_author_if_new_author_is_null() throws Exception { + boolean updated = updater.setNewAuthor(issue, null, context); + assertThat(updated).isFalse(); + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void fail_with_ISE_when_setting_new_author_on_issue() throws Exception { + issue.setAuthorLogin("simon"); + + thrown.expect(IllegalStateException.class); + thrown.expectMessage("It's not possible to update the author with this method, please use setAuthorLogin()"); + updater.setNewAuthor(issue, "julien", context); + } + + @Test + public void set_project() { + boolean updated = updater.setProject(issue, "sample", context); + assertThat(updated).isTrue(); + assertThat(issue.projectKey()).isEqualTo("sample"); + + // do not save change + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void set_past_project() { + issue.setProjectKey("new project key"); + boolean updated = updater.setPastProject(issue, "past project key", context); + assertThat(updated).isTrue(); + assertThat(issue.projectKey()).isEqualTo("new project key"); + + // do not save change + assertThat(issue.currentChange()).isNull(); + assertThat(issue.mustSendNotifications()).isFalse(); + } + + @Test + public void not_set_past_project_if_no_change() { + issue.setProjectKey("key"); + boolean updated = updater.setPastProject(issue, "key", context); + assertThat(updated).isFalse(); + assertThat(issue.projectKey()).isEqualTo("key"); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java index df4974f8567..05e28447b30 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/PlanActionTest.java @@ -28,7 +28,6 @@ import org.sonar.api.issue.Issue; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; import org.sonar.core.issue.DefaultActionPlan; -import org.sonar.core.issue.IssueUpdater; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.user.ThreadLocalUserSession; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/RemoveTagsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/RemoveTagsActionTest.java index 60c8ca20742..efe5914e89c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/RemoveTagsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/RemoveTagsActionTest.java @@ -27,7 +27,6 @@ import org.junit.rules.ExpectedException; import org.mockito.Matchers; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; import java.util.Collection; import java.util.Map; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java index 6e5154626de..2a6d4613c2e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/SetSeverityActionTest.java @@ -28,7 +28,6 @@ import org.sonar.api.issue.Issue; import org.sonar.api.web.UserRole; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; import org.sonar.server.tester.AnonymousMockUserSession; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.user.UserSession; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanServiceTest.java index 7ecda866583..3076cf36686 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/actionplan/ActionPlanServiceTest.java @@ -34,7 +34,7 @@ import org.sonar.core.issue.ActionPlanStats; import org.sonar.core.issue.DefaultActionPlan; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; +import org.sonar.server.issue.IssueUpdater; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ResourceDao; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java index 1900c2c0ce1..abc253ed079 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/workflow/IssueWorkflowTest.java @@ -32,7 +32,7 @@ import org.sonar.api.issue.DefaultTransitions; import org.sonar.api.rule.RuleKey; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.issue.IssueUpdater; +import org.sonar.server.issue.IssueUpdater; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java deleted file mode 100644 index c6919762f0e..00000000000 --- a/sonar-core/src/main/java/org/sonar/core/issue/IssueUpdater.java +++ /dev/null @@ -1,354 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact 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.core.issue; - -import com.google.common.base.Function; -import com.google.common.base.Joiner; -import com.google.common.base.Objects; -import com.google.common.base.Predicate; -import com.google.common.collect.Collections2; -import com.google.common.collect.Sets; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; -import java.util.Set; -import javax.annotation.Nullable; -import org.apache.commons.lang.StringUtils; -import org.apache.commons.lang.time.DateUtils; -import org.sonar.api.issue.ActionPlan; -import org.sonar.api.server.ServerSide; -import org.sonar.api.server.rule.RuleTagFormat; -import org.sonar.api.user.User; -import org.sonar.api.utils.Duration; - -import static com.google.common.base.Preconditions.checkState; -import static com.google.common.base.Strings.isNullOrEmpty; - -/** - * Updates issue fields and chooses if changes must be kept in history. - */ -@ServerSide -public class IssueUpdater { - - public static final String UNUSED = ""; - public static final String SEVERITY = "severity"; - public static final String ASSIGNEE = "assignee"; - public static final String RESOLUTION = "resolution"; - public static final String STATUS = "status"; - public static final String AUTHOR = "author"; - public static final String ACTION_PLAN = "actionPlan"; - public static final String TECHNICAL_DEBT = "technicalDebt"; - public static final String TAGS = "tags"; - - private static final Joiner CHANGELOG_TAG_JOINER = Joiner.on(" ").skipNulls(); - - public boolean setSeverity(DefaultIssue issue, String severity, IssueChangeContext context) { - if (issue.manualSeverity()) { - throw new IllegalStateException("Severity can't be changed"); - } - if (!Objects.equal(severity, issue.severity())) { - issue.setFieldChange(context, SEVERITY, issue.severity(), severity); - issue.setSeverity(severity); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - return true; - } - return false; - } - - public boolean setPastSeverity(DefaultIssue issue, @Nullable String previousSeverity, IssueChangeContext context) { - String currentSeverity = issue.severity(); - issue.setSeverity(previousSeverity); - return setSeverity(issue, currentSeverity, context); - } - - public boolean setManualSeverity(DefaultIssue issue, String severity, IssueChangeContext context) { - if (!issue.manualSeverity() || !Objects.equal(severity, issue.severity())) { - issue.setFieldChange(context, SEVERITY, issue.severity(), severity); - issue.setSeverity(severity); - issue.setManualSeverity(true); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - issue.setSendNotifications(true); - return true; - } - return false; - } - - public boolean assign(DefaultIssue issue, @Nullable User user, IssueChangeContext context) { - String sanitizedAssignee = null; - if (user != null) { - sanitizedAssignee = StringUtils.defaultIfBlank(user.login(), null); - } - if (!Objects.equal(sanitizedAssignee, issue.assignee())) { - String newAssigneeName = user != null ? user.name() : null; - issue.setFieldChange(context, ASSIGNEE, UNUSED, newAssigneeName); - issue.setAssignee(sanitizedAssignee); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - issue.setSendNotifications(true); - return true; - } - return false; - } - - /** - * Used to set the assignee when it was null - */ - public boolean setNewAssignee(DefaultIssue issue, @Nullable String newAssignee, IssueChangeContext context) { - if (newAssignee == null) { - return false; - } - checkState(issue.assignee() == null, "It's not possible to update the assignee with this method, please use assign()"); - issue.setFieldChange(context, ASSIGNEE, UNUSED, newAssignee); - issue.setAssignee(newAssignee); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - issue.setSendNotifications(true); - return true; - } - - public boolean setLine(DefaultIssue issue, @Nullable Integer line) { - if (!Objects.equal(line, issue.line())) { - issue.setLine(line); - issue.setChanged(true); - return true; - } - return false; - } - - public boolean setPastLine(DefaultIssue issue, @Nullable Integer previousLine) { - Integer currentLine = issue.line(); - issue.setLine(previousLine); - return setLine(issue, currentLine); - } - - public boolean setLocations(DefaultIssue issue, @Nullable Object locations) { - if (!Objects.equal(locations, issue.getLocations())) { - issue.setLocations(locations); - issue.setChanged(true); - return true; - } - return false; - } - - public boolean setPastLocations(DefaultIssue issue, @Nullable Object previousLocations) { - Object currentLocations = issue.getLocations(); - issue.setLocations(previousLocations); - return setLocations(issue, currentLocations); - - } - - public boolean setResolution(DefaultIssue issue, @Nullable String resolution, IssueChangeContext context) { - if (!Objects.equal(resolution, issue.resolution())) { - issue.setFieldChange(context, RESOLUTION, issue.resolution(), resolution); - issue.setResolution(resolution); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - issue.setSendNotifications(true); - return true; - } - return false; - } - - public boolean setStatus(DefaultIssue issue, String status, IssueChangeContext context) { - if (!Objects.equal(status, issue.status())) { - issue.setFieldChange(context, STATUS, issue.status(), status); - issue.setStatus(status); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - issue.setSendNotifications(true); - return true; - } - return false; - } - - public boolean setAuthorLogin(DefaultIssue issue, @Nullable String authorLogin, IssueChangeContext context) { - if (!Objects.equal(authorLogin, issue.authorLogin())) { - issue.setFieldChange(context, AUTHOR, issue.authorLogin(), authorLogin); - issue.setAuthorLogin(authorLogin); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - // do not send notifications to prevent spam when installing the developer cockpit plugin - return true; - } - return false; - } - - /** - * Used to set the author when it was null - */ - public boolean setNewAuthor(DefaultIssue issue, @Nullable String newAuthorLogin, IssueChangeContext context) { - if (isNullOrEmpty(newAuthorLogin)) { - return false; - } - checkState(issue.authorLogin() == null, "It's not possible to update the author with this method, please use setAuthorLogin()"); - issue.setFieldChange(context, AUTHOR, null, newAuthorLogin); - issue.setAuthorLogin(newAuthorLogin); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - // do not send notifications to prevent spam when installing the developer cockpit plugin - return true; - } - - public boolean setMessage(DefaultIssue issue, @Nullable String s, IssueChangeContext context) { - if (!Objects.equal(s, issue.message())) { - issue.setMessage(s); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - return true; - } - return false; - } - - public boolean setPastMessage(DefaultIssue issue, @Nullable String previousMessage, IssueChangeContext context) { - String currentMessage = issue.message(); - issue.setMessage(previousMessage); - return setMessage(issue, currentMessage, context); - } - - public void addComment(DefaultIssue issue, String text, IssueChangeContext context) { - issue.addComment(DefaultIssueComment.create(issue.key(), context.login(), text)); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - } - - public void setCloseDate(DefaultIssue issue, @Nullable Date d, IssueChangeContext context) { - Date dateWithoutMilliseconds = d == null ? null : DateUtils.truncate(d, Calendar.SECOND); - if (!Objects.equal(dateWithoutMilliseconds, issue.closeDate())) { - issue.setCloseDate(d); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - } - } - - public boolean setEffortToFix(DefaultIssue issue, @Nullable Double d, IssueChangeContext context) { - if (!Objects.equal(d, issue.effortToFix())) { - issue.setEffortToFix(d); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - // Do not send notifications to prevent spam when installing the SQALE plugin, - // and do not complete the changelog (for the moment) - return true; - } - return false; - } - - public boolean setPastEffortToFix(DefaultIssue issue, @Nullable Double previousEffort, IssueChangeContext context) { - Double currentEffort = issue.effortToFix(); - issue.setEffortToFix(previousEffort); - return setEffortToFix(issue, currentEffort, context); - } - - public boolean setTechnicalDebt(DefaultIssue issue, @Nullable Duration value, IssueChangeContext context) { - Duration oldValue = issue.debt(); - if (!Objects.equal(value, oldValue)) { - issue.setDebt(value != null ? value : null); - issue.setFieldChange(context, TECHNICAL_DEBT, oldValue != null ? oldValue.toMinutes() : null, value != null ? value.toMinutes() : null); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - return true; - } - return false; - } - - public boolean setPastTechnicalDebt(DefaultIssue issue, @Nullable Duration previousTechnicalDebt, IssueChangeContext context) { - Duration currentTechnicalDebt = issue.debt(); - issue.setDebt(previousTechnicalDebt); - return setTechnicalDebt(issue, currentTechnicalDebt, context); - } - - public boolean setAttribute(DefaultIssue issue, String key, @Nullable String value, IssueChangeContext context) { - String oldValue = issue.attribute(key); - if (!Objects.equal(oldValue, value)) { - issue.setFieldChange(context, key, oldValue, value); - issue.setAttribute(key, value); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - return true; - } - return false; - } - - public boolean plan(DefaultIssue issue, @Nullable ActionPlan actionPlan, IssueChangeContext context) { - String sanitizedActionPlanKey = null; - if (actionPlan != null) { - sanitizedActionPlanKey = StringUtils.defaultIfBlank(actionPlan.key(), null); - } - if (!Objects.equal(sanitizedActionPlanKey, issue.actionPlanKey())) { - String newActionPlanName = actionPlan != null ? actionPlan.name() : null; - issue.setFieldChange(context, ACTION_PLAN, UNUSED, newActionPlanName); - issue.setActionPlanKey(sanitizedActionPlanKey); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - issue.setSendNotifications(true); - return true; - } - return false; - } - - public boolean setProject(DefaultIssue issue, String projectKey, IssueChangeContext context) { - if (!Objects.equal(projectKey, issue.projectKey())) { - issue.setProjectKey(projectKey); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - return true; - } - return false; - } - - public boolean setPastProject(DefaultIssue issue, String previousKey, IssueChangeContext context) { - String currentProjectKey = issue.projectKey(); - issue.setProjectKey(previousKey); - return setProject(issue, currentProjectKey, context); - } - - public boolean setTags(DefaultIssue issue, Collection tags, IssueChangeContext context) { - Set newTags = Sets.newHashSet(Collections2.transform( - Collections2.filter(tags, new Predicate() { - @Override - public boolean apply(@Nullable String tag) { - return tag != null && !tag.isEmpty(); - } - }), new Function() { - @Override - public String apply(String tag) { - String lowerCaseTag = tag.toLowerCase(); - RuleTagFormat.validate(lowerCaseTag); - return lowerCaseTag; - } - })); - - Set oldTags = Sets.newHashSet(issue.tags()); - - if (!oldTags.equals(newTags)) { - issue.setFieldChange(context, TAGS, - oldTags.isEmpty() ? null : CHANGELOG_TAG_JOINER.join(oldTags), - newTags.isEmpty() ? null : CHANGELOG_TAG_JOINER.join(newTags)); - issue.setTags(newTags); - issue.setUpdateDate(context.date()); - issue.setChanged(true); - issue.setSendNotifications(true); - return true; - } - return false; - } - -} diff --git a/sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java b/sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java deleted file mode 100644 index 247e551213b..00000000000 --- a/sonar-core/src/test/java/org/sonar/core/issue/IssueUpdaterTest.java +++ /dev/null @@ -1,574 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact 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.core.issue; - -import java.util.Date; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; -import org.sonar.api.issue.ActionPlan; -import org.sonar.api.user.User; -import org.sonar.api.utils.Duration; -import org.sonar.core.user.DefaultUser; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.core.issue.IssueUpdater.ACTION_PLAN; -import static org.sonar.core.issue.IssueUpdater.ASSIGNEE; -import static org.sonar.core.issue.IssueUpdater.RESOLUTION; -import static org.sonar.core.issue.IssueUpdater.SEVERITY; -import static org.sonar.core.issue.IssueUpdater.STATUS; -import static org.sonar.core.issue.IssueUpdater.TECHNICAL_DEBT; -import static org.sonar.core.issue.IssueUpdater.UNUSED; - -public class IssueUpdaterTest { - - @Rule - public ExpectedException thrown = ExpectedException.none(); - - DefaultIssue issue = new DefaultIssue(); - IssueChangeContext context = IssueChangeContext.createUser(new Date(), "emmerik"); - - IssueUpdater updater; - - @Before - public void setUp() { - updater = new IssueUpdater(); - } - - @Test - public void assign() { - User user = new DefaultUser().setLogin("emmerik").setName("Emmerik"); - - boolean updated = updater.assign(issue, user, context); - assertThat(updated).isTrue(); - assertThat(issue.assignee()).isEqualTo("emmerik"); - assertThat(issue.mustSendNotifications()).isTrue(); - FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE); - assertThat(diff.oldValue()).isEqualTo(UNUSED); - assertThat(diff.newValue()).isEqualTo("Emmerik"); - } - - @Test - public void unassign() { - issue.setAssignee("morgan"); - boolean updated = updater.assign(issue, null, context); - assertThat(updated).isTrue(); - assertThat(issue.assignee()).isNull(); - assertThat(issue.mustSendNotifications()).isTrue(); - FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE); - assertThat(diff.oldValue()).isEqualTo(UNUSED); - assertThat(diff.newValue()).isNull(); - } - - @Test - public void change_assignee() { - User user = new DefaultUser().setLogin("emmerik").setName("Emmerik"); - - issue.setAssignee("morgan"); - boolean updated = updater.assign(issue, user, context); - assertThat(updated).isTrue(); - assertThat(issue.assignee()).isEqualTo("emmerik"); - assertThat(issue.mustSendNotifications()).isTrue(); - FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE); - assertThat(diff.oldValue()).isEqualTo(UNUSED); - assertThat(diff.newValue()).isEqualTo("Emmerik"); - } - - @Test - public void not_change_assignee() { - User user = new DefaultUser().setLogin("morgan").setName("Morgan"); - - issue.setAssignee("morgan"); - boolean updated = updater.assign(issue, user, context); - assertThat(updated).isFalse(); - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void set_new_assignee() throws Exception { - boolean updated = updater.setNewAssignee(issue, "simon", context); - assertThat(updated).isTrue(); - assertThat(issue.assignee()).isEqualTo("simon"); - assertThat(issue.mustSendNotifications()).isTrue(); - FieldDiffs.Diff diff = issue.currentChange().get(ASSIGNEE); - assertThat(diff.oldValue()).isEqualTo(UNUSED); - assertThat(diff.newValue()).isEqualTo("simon"); - } - - @Test - public void not_set_new_assignee_if_new_assignee_is_null() throws Exception { - boolean updated = updater.setNewAssignee(issue, null, context); - assertThat(updated).isFalse(); - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void fail_with_ISE_when_setting_new_assignee_on_already_assigned_issue() throws Exception { - issue.setAssignee("simon"); - - thrown.expect(IllegalStateException.class); - thrown.expectMessage("It's not possible to update the assignee with this method, please use assign()"); - updater.setNewAssignee(issue, "julien", context); - } - - @Test - public void set_severity() { - boolean updated = updater.setSeverity(issue, "BLOCKER", context); - assertThat(updated).isTrue(); - assertThat(issue.severity()).isEqualTo("BLOCKER"); - assertThat(issue.manualSeverity()).isFalse(); - assertThat(issue.mustSendNotifications()).isFalse(); - - FieldDiffs.Diff diff = issue.currentChange().get(SEVERITY); - assertThat(diff.oldValue()).isNull(); - assertThat(diff.newValue()).isEqualTo("BLOCKER"); - } - - @Test - public void set_past_severity() { - issue.setSeverity("BLOCKER"); - boolean updated = updater.setPastSeverity(issue, "INFO", context); - assertThat(updated).isTrue(); - assertThat(issue.severity()).isEqualTo("BLOCKER"); - assertThat(issue.mustSendNotifications()).isFalse(); - - FieldDiffs.Diff diff = issue.currentChange().get(SEVERITY); - assertThat(diff.oldValue()).isEqualTo("INFO"); - assertThat(diff.newValue()).isEqualTo("BLOCKER"); - } - - @Test - public void update_severity() { - issue.setSeverity("BLOCKER"); - boolean updated = updater.setSeverity(issue, "MINOR", context); - - assertThat(updated).isTrue(); - assertThat(issue.severity()).isEqualTo("MINOR"); - assertThat(issue.mustSendNotifications()).isFalse(); - FieldDiffs.Diff diff = issue.currentChange().get(SEVERITY); - assertThat(diff.oldValue()).isEqualTo("BLOCKER"); - assertThat(diff.newValue()).isEqualTo("MINOR"); - } - - @Test - public void not_change_severity() { - issue.setSeverity("MINOR"); - boolean updated = updater.setSeverity(issue, "MINOR", context); - assertThat(updated).isFalse(); - assertThat(issue.mustSendNotifications()).isFalse(); - assertThat(issue.currentChange()).isNull(); - } - - @Test - public void not_revert_manual_severity() { - issue.setSeverity("MINOR").setManualSeverity(true); - try { - updater.setSeverity(issue, "MAJOR", context); - } catch (IllegalStateException e) { - assertThat(e).hasMessage("Severity can't be changed"); - } - } - - @Test - public void set_manual_severity() { - issue.setSeverity("BLOCKER"); - boolean updated = updater.setManualSeverity(issue, "MINOR", context); - - assertThat(updated).isTrue(); - assertThat(issue.severity()).isEqualTo("MINOR"); - assertThat(issue.manualSeverity()).isTrue(); - assertThat(issue.mustSendNotifications()).isTrue(); - FieldDiffs.Diff diff = issue.currentChange().get(SEVERITY); - assertThat(diff.oldValue()).isEqualTo("BLOCKER"); - assertThat(diff.newValue()).isEqualTo("MINOR"); - } - - @Test - public void not_change_manual_severity() { - issue.setSeverity("MINOR").setManualSeverity(true); - boolean updated = updater.setManualSeverity(issue, "MINOR", context); - assertThat(updated).isFalse(); - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void set_line() { - boolean updated = updater.setLine(issue, 123); - assertThat(updated).isTrue(); - assertThat(issue.line()).isEqualTo(123); - assertThat(issue.mustSendNotifications()).isFalse(); - // do not save change - assertThat(issue.currentChange()).isNull(); - } - - @Test - public void set_past_line() { - issue.setLine(42); - boolean updated = updater.setPastLine(issue, 123); - assertThat(updated).isTrue(); - assertThat(issue.line()).isEqualTo(42); - assertThat(issue.mustSendNotifications()).isFalse(); - - // do not save change - assertThat(issue.currentChange()).isNull(); - } - - @Test - public void line_is_not_changed() { - issue.setLine(123); - boolean updated = updater.setLine(issue, 123); - assertThat(updated).isFalse(); - assertThat(issue.line()).isEqualTo(123); - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void change_locations() { - issue.setLocations("[1-3]"); - boolean updated = updater.setLocations(issue, "[1-4]"); - assertThat(updated).isTrue(); - assertThat(issue.getLocations()).isEqualTo("[1-4]"); - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void do_not_change_locations() { - issue.setLocations("[1-3]"); - boolean updated = updater.setLocations(issue, "[1-3]"); - assertThat(updated).isFalse(); - assertThat(issue.getLocations()).isEqualTo("[1-3]"); - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void set_locations_for_the_first_time() { - issue.setLocations(null); - boolean updated = updater.setLocations(issue, "[1-4]"); - assertThat(updated).isTrue(); - assertThat(issue.getLocations()).isEqualTo("[1-4]"); - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void set_resolution() { - boolean updated = updater.setResolution(issue, "OPEN", context); - assertThat(updated).isTrue(); - assertThat(issue.resolution()).isEqualTo("OPEN"); - - FieldDiffs.Diff diff = issue.currentChange().get(RESOLUTION); - assertThat(diff.oldValue()).isNull(); - assertThat(diff.newValue()).isEqualTo("OPEN"); - assertThat(issue.mustSendNotifications()).isTrue(); - } - - @Test - public void not_change_resolution() { - issue.setResolution("FIXED"); - boolean updated = updater.setResolution(issue, "FIXED", context); - assertThat(updated).isFalse(); - assertThat(issue.resolution()).isEqualTo("FIXED"); - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void set_status() { - boolean updated = updater.setStatus(issue, "OPEN", context); - assertThat(updated).isTrue(); - assertThat(issue.status()).isEqualTo("OPEN"); - - FieldDiffs.Diff diff = issue.currentChange().get(STATUS); - assertThat(diff.oldValue()).isNull(); - assertThat(diff.newValue()).isEqualTo("OPEN"); - assertThat(issue.mustSendNotifications()).isTrue(); - } - - @Test - public void not_change_status() { - issue.setStatus("CLOSED"); - boolean updated = updater.setStatus(issue, "CLOSED", context); - assertThat(updated).isFalse(); - assertThat(issue.status()).isEqualTo("CLOSED"); - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void set_new_attribute_value() { - boolean updated = updater.setAttribute(issue, "JIRA", "FOO-123", context); - assertThat(updated).isTrue(); - assertThat(issue.attribute("JIRA")).isEqualTo("FOO-123"); - assertThat(issue.currentChange().diffs()).hasSize(1); - assertThat(issue.currentChange().get("JIRA").oldValue()).isNull(); - assertThat(issue.currentChange().get("JIRA").newValue()).isEqualTo("FOO-123"); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void unset_attribute() { - issue.setAttribute("JIRA", "FOO-123"); - boolean updated = updater.setAttribute(issue, "JIRA", null, context); - assertThat(updated).isTrue(); - assertThat(issue.attribute("JIRA")).isNull(); - assertThat(issue.currentChange().diffs()).hasSize(1); - assertThat(issue.currentChange().get("JIRA").oldValue()).isEqualTo("FOO-123"); - assertThat(issue.currentChange().get("JIRA").newValue()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void not_update_attribute() { - issue.setAttribute("JIRA", "FOO-123"); - boolean updated = updater.setAttribute(issue, "JIRA", "FOO-123", context); - assertThat(updated).isFalse(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void plan_with_no_existing_plan() { - ActionPlan newActionPlan = DefaultActionPlan.create("newName"); - - boolean updated = updater.plan(issue, newActionPlan, context); - assertThat(updated).isTrue(); - assertThat(issue.actionPlanKey()).isEqualTo(newActionPlan.key()); - - FieldDiffs.Diff diff = issue.currentChange().get(ACTION_PLAN); - assertThat(diff.oldValue()).isEqualTo(UNUSED); - assertThat(diff.newValue()).isEqualTo("newName"); - assertThat(issue.mustSendNotifications()).isTrue(); - } - - @Test - public void plan_with_existing_plan() { - issue.setActionPlanKey("formerActionPlan"); - - ActionPlan newActionPlan = DefaultActionPlan.create("newName").setKey("newKey"); - - boolean updated = updater.plan(issue, newActionPlan, context); - assertThat(updated).isTrue(); - assertThat(issue.actionPlanKey()).isEqualTo(newActionPlan.key()); - - FieldDiffs.Diff diff = issue.currentChange().get(ACTION_PLAN); - assertThat(diff.oldValue()).isEqualTo(UNUSED); - assertThat(diff.newValue()).isEqualTo("newName"); - assertThat(issue.mustSendNotifications()).isTrue(); - } - - @Test - public void unplan() { - issue.setActionPlanKey("formerActionPlan"); - - boolean updated = updater.plan(issue, null, context); - assertThat(updated).isTrue(); - assertThat(issue.actionPlanKey()).isNull(); - - FieldDiffs.Diff diff = issue.currentChange().get(ACTION_PLAN); - assertThat(diff.oldValue()).isEqualTo(UNUSED); - assertThat(diff.newValue()).isNull(); - assertThat(issue.mustSendNotifications()).isTrue(); - } - - @Test - public void not_plan_again() { - issue.setActionPlanKey("existingActionPlan"); - - ActionPlan newActionPlan = DefaultActionPlan.create("existingActionPlan").setKey("existingActionPlan"); - - boolean updated = updater.plan(issue, newActionPlan, context); - assertThat(updated).isFalse(); - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void set_effort_to_fix() { - boolean updated = updater.setEffortToFix(issue, 3.14, context); - assertThat(updated).isTrue(); - assertThat(issue.isChanged()).isTrue(); - assertThat(issue.effortToFix()).isEqualTo(3.14); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void not_set_effort_to_fix_if_unchanged() { - issue.setEffortToFix(3.14); - boolean updated = updater.setEffortToFix(issue, 3.14, context); - assertThat(updated).isFalse(); - assertThat(issue.isChanged()).isFalse(); - assertThat(issue.effortToFix()).isEqualTo(3.14); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void set_past_effort() { - issue.setEffortToFix(3.14); - boolean updated = updater.setPastEffortToFix(issue, 1.0, context); - assertThat(updated).isTrue(); - assertThat(issue.effortToFix()).isEqualTo(3.14); - - // do not save change - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void set_past_technical_debt() { - Duration newDebt = Duration.create(15 * 8 * 60); - Duration previousDebt = Duration.create(10 * 8 * 60); - issue.setDebt(newDebt); - boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context); - assertThat(updated).isTrue(); - assertThat(issue.debt()).isEqualTo(newDebt); - assertThat(issue.mustSendNotifications()).isFalse(); - - FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT); - assertThat(diff.oldValue()).isEqualTo(10L * 8 * 60); - assertThat(diff.newValue()).isEqualTo(15L * 8 * 60); - } - - @Test - public void set_past_technical_debt_without_previous_value() { - Duration newDebt = Duration.create(15 * 8 * 60); - issue.setDebt(newDebt); - boolean updated = updater.setPastTechnicalDebt(issue, null, context); - assertThat(updated).isTrue(); - assertThat(issue.debt()).isEqualTo(newDebt); - assertThat(issue.mustSendNotifications()).isFalse(); - - FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT); - assertThat(diff.oldValue()).isNull(); - assertThat(diff.newValue()).isEqualTo(15L * 8 * 60); - } - - @Test - public void set_past_technical_debt_with_null_new_value() { - issue.setDebt(null); - Duration previousDebt = Duration.create(10 * 8 * 60); - boolean updated = updater.setPastTechnicalDebt(issue, previousDebt, context); - assertThat(updated).isTrue(); - assertThat(issue.debt()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - - FieldDiffs.Diff diff = issue.currentChange().get(TECHNICAL_DEBT); - assertThat(diff.oldValue()).isEqualTo(10L * 8 * 60); - assertThat(diff.newValue()).isNull(); - } - - @Test - public void set_message() { - boolean updated = updater.setMessage(issue, "the message", context); - assertThat(updated).isTrue(); - assertThat(issue.isChanged()).isTrue(); - assertThat(issue.message()).isEqualTo("the message"); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void set_past_message() { - issue.setMessage("new message"); - boolean updated = updater.setPastMessage(issue, "past message", context); - assertThat(updated).isTrue(); - assertThat(issue.message()).isEqualTo("new message"); - - // do not save change - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void set_author() { - boolean updated = updater.setAuthorLogin(issue, "eric", context); - assertThat(updated).isTrue(); - assertThat(issue.authorLogin()).isEqualTo("eric"); - - FieldDiffs.Diff diff = issue.currentChange().get("author"); - assertThat(diff.oldValue()).isNull(); - assertThat(diff.newValue()).isEqualTo("eric"); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void set_new_author() throws Exception { - boolean updated = updater.setNewAuthor(issue, "simon", context); - assertThat(updated).isTrue(); - - FieldDiffs.Diff diff = issue.currentChange().get("author"); - assertThat(diff.oldValue()).isNull(); - assertThat(diff.newValue()).isEqualTo("simon"); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void not_set_new_author_if_new_author_is_null() throws Exception { - boolean updated = updater.setNewAuthor(issue, null, context); - assertThat(updated).isFalse(); - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void fail_with_ISE_when_setting_new_author_on_issue() throws Exception { - issue.setAuthorLogin("simon"); - - thrown.expect(IllegalStateException.class); - thrown.expectMessage("It's not possible to update the author with this method, please use setAuthorLogin()"); - updater.setNewAuthor(issue, "julien", context); - } - - @Test - public void set_project() { - boolean updated = updater.setProject(issue, "sample", context); - assertThat(updated).isTrue(); - assertThat(issue.projectKey()).isEqualTo("sample"); - - // do not save change - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void set_past_project() { - issue.setProjectKey("new project key"); - boolean updated = updater.setPastProject(issue, "past project key", context); - assertThat(updated).isTrue(); - assertThat(issue.projectKey()).isEqualTo("new project key"); - - // do not save change - assertThat(issue.currentChange()).isNull(); - assertThat(issue.mustSendNotifications()).isFalse(); - } - - @Test - public void not_set_past_project_if_no_change() { - issue.setProjectKey("key"); - boolean updated = updater.setPastProject(issue, "key", context); - assertThat(updated).isFalse(); - assertThat(issue.projectKey()).isEqualTo("key"); - } - -}