From afaa0ecdd013e75d9ffb67bfceba8335f16ba6c4 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Wed, 17 Sep 2014 11:24:27 +0200 Subject: [PATCH] SONAR-5531 Provide an implementation based on ES of all "issues relating" web services --- .../server/issue/DefaultIssueService.java | 339 +++++++++++++++ .../issue/InternalRubyIssueService.java | 14 +- .../org/sonar/server/issue/IssueService.java | 312 +------------- .../sonar/server/issue/OldIssueService.java | 301 ++++++++++++++ .../server/issue/ws/IssueActionsWriter.java | 2 +- .../sonar/server/issue/ws/SearchAction.java | 17 +- .../server/platform/ServerComponents.java | 162 +------- .../server/search/IndexSynchronizer.java | 14 +- .../component/ws/ComponentAppActionTest.java | 13 +- .../issue/DefaultIssueServiceMediumTest.java | 391 ++++++++++++++++++ ... InternalRubyDefaultIssueServiceTest.java} | 24 +- .../server/issue/IssueServiceMediumTest.java | 165 -------- ...t.java => OldDefaultIssueServiceTest.java} | 62 +-- ...=> PublicRubyDefaultIssueServiceTest.java} | 2 +- .../issue/db/IssueBackendMediumTest.java | 3 +- .../sonar/server/issue/db/IssueDaoTest.java | 112 ++++- .../IssueAuthorizationIndexMediumTest.java | 3 +- .../issue/index/IssueIndexMediumTest.java | 3 +- .../issue/ws/IssueActionsWriterTest.java | 25 +- .../issue/ws/IssueSearchActionTest.java | 3 +- .../server/issue/ws/IssueShowActionTest.java | 3 +- .../org/sonar/server/rule/RuleTesting.java | 1 + .../search/IndexSynchronizerMediumTest.java | 6 +- .../issue/db/IssueDaoTest/get_by_key.xml | 28 ++ .../issue/db/IssueDaoTest/insert-result.xml | 28 ++ ...{should_select_all.xml => some_issues.xml} | 0 .../issue/db/IssueDaoTest/update-result.xml | 28 ++ .../server/issue/db/IssueDaoTest/update.xml | 28 ++ .../sonar/core/issue/IssueNotifications.java | 11 +- .../org/sonar/core/issue/db/IssueDto.java | 14 +- .../core/permission/PermissionFacade.java | 2 + .../org/sonar/api/issue/IssueQueryResult.java | 2 + 32 files changed, 1409 insertions(+), 709 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueService.java create mode 100644 server/sonar-server/src/main/java/org/sonar/server/issue/OldIssueService.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueServiceMediumTest.java rename server/sonar-server/src/test/java/org/sonar/server/issue/{InternalRubyIssueServiceTest.java => InternalRubyDefaultIssueServiceTest.java} (96%) delete mode 100644 server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java rename server/sonar-server/src/test/java/org/sonar/server/issue/{IssueServiceTest.java => OldDefaultIssueServiceTest.java} (92%) rename server/sonar-server/src/test/java/org/sonar/server/issue/{PublicRubyIssueServiceTest.java => PublicRubyDefaultIssueServiceTest.java} (99%) create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/get_by_key.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/insert-result.xml rename server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/{should_select_all.xml => some_issues.xml} (100%) create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update-result.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update.xml diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueService.java new file mode 100644 index 00000000000..c19387a2a42 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueService.java @@ -0,0 +1,339 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.Objects; +import com.google.common.base.Strings; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.issue.ActionPlan; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.IssueQuery; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.issue.internal.IssueChangeContext; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RuleFinder; +import org.sonar.api.user.User; +import org.sonar.api.user.UserFinder; +import org.sonar.api.web.UserRole; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.issue.DefaultIssueBuilder; +import org.sonar.core.issue.IssueNotifications; +import org.sonar.core.issue.IssueUpdater; +import org.sonar.core.issue.db.IssueDao; +import org.sonar.core.issue.db.IssueDto; +import org.sonar.core.issue.db.IssueStorage; +import org.sonar.core.issue.workflow.IssueWorkflow; +import org.sonar.core.issue.workflow.Transition; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.preview.PreviewCache; +import org.sonar.core.rule.RuleDto; +import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.issue.actionplan.ActionPlanService; +import org.sonar.server.issue.index.IssueIndex; +import org.sonar.server.search.IndexClient; +import org.sonar.server.search.QueryContext; +import org.sonar.server.user.UserSession; + +import javax.annotation.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + +public class DefaultIssueService implements IssueService { + + private final DbClient dbClient; + private final IndexClient indexClient; + + private final IssueWorkflow workflow; + private final IssueUpdater issueUpdater; + private final IssueStorage issueStorage; + private final IssueNotifications issueNotifications; + private final ActionPlanService actionPlanService; + private final RuleFinder ruleFinder; + private final IssueDao issueDao; + private final UserFinder userFinder; + private final PreviewCache dryRunCache; + + public DefaultIssueService(DbClient dbClient, IndexClient indexClient, + IssueWorkflow workflow, + IssueStorage issueStorage, + IssueUpdater issueUpdater, + IssueNotifications issueNotifications, + ActionPlanService actionPlanService, + RuleFinder ruleFinder, + IssueDao issueDao, + UserFinder userFinder, + PreviewCache dryRunCache) { + this.dbClient = dbClient; + this.indexClient = indexClient; + this.workflow = workflow; + this.issueStorage = issueStorage; + this.issueUpdater = issueUpdater; + this.actionPlanService = actionPlanService; + this.ruleFinder = ruleFinder; + this.issueNotifications = issueNotifications; + this.issueDao = issueDao; + this.userFinder = userFinder; + this.dryRunCache = dryRunCache; + } + + @Override + public List listStatus() { + return workflow.statusKeys(); + } + + /** + * List of available transitions. + *

+ * Never return null, but return an empty list if the issue does not exist. + */ + @Override + public List listTransitions(String issueKey) { + DbSession session = dbClient.openSession(false); + try { + IssueDto issueDto = getByKey(session, issueKey); + return listTransitions(issueDto.toDefaultIssue()); + } finally { + session.close(); + } + } + + /** + * Never return null, but an empty list if the issue does not exist. + * No security check is done since it should already have been done to get the issue + */ + @Override + public List listTransitions(@Nullable Issue issue) { + if (issue == null) { + return Collections.emptyList(); + } + List outTransitions = workflow.outTransitions(issue); + List allowedTransitions = new ArrayList(); + for (Transition transition : outTransitions) { + DefaultIssue defaultIssue = (DefaultIssue) issue; + String projectKey = defaultIssue.projectKey(); + if (StringUtils.isBlank(transition.requiredProjectPermission()) || + (projectKey != null && UserSession.get().hasProjectPermission(transition.requiredProjectPermission(), projectKey))) { + allowedTransitions.add(transition); + } + } + return allowedTransitions; + } + + @Override + public Issue doTransition(String issueKey, String transitionKey) { + verifyLoggedIn(); + + DbSession session = dbClient.openSession(false); + try { + IssueDto issueDto = getByKey(session, issueKey); + DefaultIssue defaultIssue = issueDto.toDefaultIssue(); + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + checkTransitionPermission(transitionKey, UserSession.get(), defaultIssue); + if (workflow.doTransition(defaultIssue, transitionKey, context)) { + saveIssue(session, defaultIssue, context); + } + return defaultIssue; + + } finally { + session.close(); + } + } + + private void checkTransitionPermission(String transitionKey, UserSession userSession, DefaultIssue defaultIssue) { + List outTransitions = workflow.outTransitions(defaultIssue); + for (Transition transition : outTransitions) { + String projectKey = defaultIssue.projectKey(); + if (transition.key().equals(transitionKey) && StringUtils.isNotBlank(transition.requiredProjectPermission()) && projectKey != null) { + userSession.checkProjectPermission(transition.requiredProjectPermission(), projectKey); + } + } + } + + @Override + public Issue assign(String issueKey, @Nullable String assignee) { + verifyLoggedIn(); + + DbSession session = dbClient.openSession(false); + try { + IssueDto issueDto = getByKey(session, issueKey); + DefaultIssue issue = issueDto.toDefaultIssue(); + User user = null; + if (!Strings.isNullOrEmpty(assignee)) { + user = userFinder.findByLogin(assignee); + if (user == null) { + throw new NotFoundException("Unknown user: " + assignee); + } + } + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + if (issueUpdater.assign(issue, user, context)) { + saveIssue(session, issue, context); + } + return issue; + + } finally { + session.close(); + } + } + + @Override + public Issue plan(String issueKey, @Nullable String actionPlanKey) { + verifyLoggedIn(); + + DbSession session = dbClient.openSession(false); + try { + ActionPlan actionPlan = null; + if (!Strings.isNullOrEmpty(actionPlanKey)) { + actionPlan = actionPlanService.findByKey(actionPlanKey, UserSession.get()); + if (actionPlan == null) { + throw new NotFoundException("Unknown action plan: " + actionPlanKey); + } + } + IssueDto issueDto = getByKey(session, issueKey); + DefaultIssue issue = issueDto.toDefaultIssue(); + + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + if (issueUpdater.plan(issue, actionPlan, context)) { + saveIssue(session, issue, context); + } + return issue; + + } finally { + session.close(); + } + } + + @Override + public Issue setSeverity(String issueKey, String severity) { + verifyLoggedIn(); + + DbSession session = dbClient.openSession(false); + try { + IssueDto issueDto = getByKey(session, issueKey); + DefaultIssue issue = issueDto.toDefaultIssue(); + UserSession.get().checkProjectPermission(UserRole.ISSUE_ADMIN, issue.projectKey()); + + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + if (issueUpdater.setManualSeverity(issue, severity, context)) { + saveIssue(session, issue, context); + } + return issue; + } finally { + session.close(); + } + } + + @Override + public DefaultIssue createManualIssue(String componentKey, RuleKey ruleKey, @Nullable Integer line, @Nullable String message, @Nullable String severity, + @Nullable Double effortToFix) { + verifyLoggedIn(); + + DbSession session = dbClient.openSession(false); + try { + ComponentDto component = dbClient.componentDao().getByKey(session, componentKey); + ComponentDto project = dbClient.componentDao().getRootProjectByKey(componentKey, session); + + UserSession.get().checkProjectPermission(UserRole.USER, project.getKey()); + if (!ruleKey.isManual()) { + throw new IllegalArgumentException("Issues can be created only on rules marked as 'manual': " + ruleKey); + } + Rule rule = getRuleByKey(ruleKey); + + DefaultIssue issue = new DefaultIssueBuilder() + .componentKey(component.getKey()) + .projectKey(project.getKey()) + .line(line) + .message(!Strings.isNullOrEmpty(message) ? message : rule.getName()) + .severity(Objects.firstNonNull(severity, Severity.MAJOR)) + .effortToFix(effortToFix) + .ruleKey(ruleKey) + .reporter(UserSession.get().login()) + .build(); + + Date now = new Date(); + issue.setCreationDate(now); + issue.setUpdateDate(now); + issueStorage.save(issue); + dryRunCache.reportResourceModification(component.getKey()); + return issue; + } finally { + session.close(); + } + } + + // TODO result should be replaced by an aggregation object in IssueIndex + @Override + public RulesAggregation findRulesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { + RulesAggregation rulesAggregation = new RulesAggregation(); + for (RuleDto ruleDto : issueDao.findRulesByComponent(componentKey, periodDate, session)) { + rulesAggregation.add(ruleDto); + } + return rulesAggregation; + } + + // TODO result should be replaced by an aggregation object in IssueIndex + @Override + public Multiset findSeveritiesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { + Multiset aggregation = HashMultiset.create(); + for (String severity : issueDao.findSeveritiesByComponent(componentKey, periodDate, session)) { + aggregation.add(severity); + } + return aggregation; + } + + public IssueDto getByKey(DbSession session, String key) { + // Load with index to check permission + indexClient.get(IssueIndex.class).getByKey(key); + return dbClient.issueDao().getByKey(session, key); + } + + private void saveIssue(DbSession session, DefaultIssue issue, IssueChangeContext context) { + issueStorage.save(issue); + issueNotifications.sendChanges(issue, context, + getRuleByKey(issue.ruleKey()), + dbClient.componentDao().getByKey(session, issue.projectKey()), + dbClient.componentDao().getNullableByKey(session, issue.componentKey())); + dryRunCache.reportResourceModification(issue.componentKey()); + } + + private Rule getRuleByKey(RuleKey ruleKey) { + Rule rule = ruleFinder.findByKey(ruleKey); + if (rule == null) { + throw new IllegalArgumentException("Unknown rule: " + ruleKey); + } + return rule; + } + + public org.sonar.server.search.Result search(IssueQuery query, QueryContext options) { + IssueIndex issueIndex = indexClient.get(IssueIndex.class); + return issueIndex.search(query, options); + } + + private void verifyLoggedIn() { + UserSession.get().checkLoggedIn(); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java index 1fa162a5dd7..e919ba8f6dc 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/InternalRubyIssueService.java @@ -110,11 +110,11 @@ public class InternalRubyIssueService implements ServerComponent { } public List listTransitions(String issueKey) { - return issueService.listTransitions(issueKey, UserSession.get()); + return issueService.listTransitions(issueKey); } public List listTransitions(Issue issue) { - return issueService.listTransitions(issue, UserSession.get()); + return issueService.listTransitions(issue); } public List listStatus() { @@ -149,7 +149,7 @@ public class InternalRubyIssueService implements ServerComponent { public Result doTransition(String issueKey, String transitionKey) { Result result = Result.of(); try { - result.set(issueService.doTransition(issueKey, transitionKey, UserSession.get())); + result.set(issueService.doTransition(issueKey, transitionKey)); } catch (Exception e) { result.addError(e.getMessage()); } @@ -159,7 +159,7 @@ public class InternalRubyIssueService implements ServerComponent { public Result assign(String issueKey, @Nullable String assignee) { Result result = Result.of(); try { - result.set(issueService.assign(issueKey, StringUtils.defaultIfBlank(assignee, null), UserSession.get())); + result.set(issueService.assign(issueKey, StringUtils.defaultIfBlank(assignee, null))); } catch (Exception e) { result.addError(e.getMessage()); } @@ -169,7 +169,7 @@ public class InternalRubyIssueService implements ServerComponent { public Result setSeverity(String issueKey, String severity) { Result result = Result.of(); try { - result.set(issueService.setSeverity(issueKey, severity, UserSession.get())); + result.set(issueService.setSeverity(issueKey, severity)); } catch (Exception e) { result.addError(e.getMessage()); } @@ -179,7 +179,7 @@ public class InternalRubyIssueService implements ServerComponent { public Result plan(String issueKey, @Nullable String actionPlanKey) { Result result = Result.of(); try { - result.set(issueService.plan(issueKey, actionPlanKey, UserSession.get())); + result.set(issueService.plan(issueKey, actionPlanKey)); } catch (Exception e) { result.addError(e.getMessage()); } @@ -235,7 +235,7 @@ public class InternalRubyIssueService implements ServerComponent { if (result.ok()) { DefaultIssue issue = issueService.createManualIssue(componentKey, ruleKey, RubyUtils.toInteger(params.get("line")), params.get("message"), params.get("severity"), - RubyUtils.toDouble(params.get("effortToFix")), UserSession.get()); + RubyUtils.toDouble(params.get("effortToFix"))); result.set(issue); } 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 6ef2e3b252f..c60d205fe8a 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 @@ -17,326 +17,44 @@ * 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.annotations.VisibleForTesting; -import com.google.common.base.Objects; -import com.google.common.base.Strings; -import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.sonar.api.ServerComponent; -import org.sonar.api.issue.ActionPlan; import org.sonar.api.issue.Issue; -import org.sonar.api.issue.IssueQuery; -import org.sonar.api.issue.IssueQueryResult; import org.sonar.api.issue.internal.DefaultIssue; -import org.sonar.api.issue.internal.IssueChangeContext; import org.sonar.api.rule.RuleKey; -import org.sonar.api.rule.Severity; -import org.sonar.api.rules.Rule; -import org.sonar.api.rules.RuleFinder; -import org.sonar.api.user.User; -import org.sonar.api.user.UserFinder; -import org.sonar.api.web.UserRole; -import org.sonar.core.issue.DefaultIssueBuilder; -import org.sonar.core.issue.IssueNotifications; -import org.sonar.core.issue.IssueUpdater; -import org.sonar.core.issue.db.IssueDao; -import org.sonar.core.issue.db.IssueStorage; -import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.issue.workflow.Transition; import org.sonar.core.persistence.DbSession; -import org.sonar.core.preview.PreviewCache; -import org.sonar.core.resource.ResourceDao; -import org.sonar.core.resource.ResourceDto; -import org.sonar.core.resource.ResourceQuery; -import org.sonar.core.rule.RuleDto; -import org.sonar.core.user.AuthorizationDao; -import org.sonar.server.db.DbClient; -import org.sonar.server.issue.actionplan.ActionPlanService; -import org.sonar.server.issue.index.IssueIndex; -import org.sonar.server.search.IndexClient; -import org.sonar.server.search.QueryContext; -import org.sonar.server.user.UserSession; import javax.annotation.Nullable; -import java.util.*; - -/** - * @since 3.6 - */ -public class IssueService implements ServerComponent { - - private static final Logger LOGGER = LoggerFactory.getLogger(IssueService.class); - - private final DbClient dbClient; - private final IndexClient indexClient; - - private final DefaultIssueFinder finder; - private final IssueWorkflow workflow; - private final IssueUpdater issueUpdater; - private final IssueStorage issueStorage; - private final IssueNotifications issueNotifications; - private final ActionPlanService actionPlanService; - private final RuleFinder ruleFinder; - private final ResourceDao resourceDao; - private final IssueDao issueDao; - private final AuthorizationDao authorizationDao; - private final UserFinder userFinder; - private final PreviewCache dryRunCache; - - @Deprecated - @VisibleForTesting - public IssueService(DefaultIssueFinder finder, - IssueWorkflow workflow, - IssueStorage issueStorage, - IssueUpdater issueUpdater, - IssueNotifications issueNotifications, - ActionPlanService actionPlanService, - RuleFinder ruleFinder, - ResourceDao resourceDao, - IssueDao issueDao, - AuthorizationDao authorizationDao, - UserFinder userFinder, - PreviewCache dryRunCache) { - this(null, null, finder, workflow, issueStorage, issueUpdater, issueNotifications, actionPlanService, - ruleFinder, resourceDao, issueDao, authorizationDao, userFinder, dryRunCache); - } - - public IssueService(DbClient dbClient, IndexClient indexClient, - DefaultIssueFinder finder, - IssueWorkflow workflow, - IssueStorage issueStorage, - IssueUpdater issueUpdater, - IssueNotifications issueNotifications, - ActionPlanService actionPlanService, - RuleFinder ruleFinder, - ResourceDao resourceDao, - IssueDao issueDao, - AuthorizationDao authorizationDao, - UserFinder userFinder, - PreviewCache dryRunCache) { - this.dbClient = dbClient; - this.indexClient = indexClient; - this.finder = finder; - this.workflow = workflow; - this.issueStorage = issueStorage; - this.issueUpdater = issueUpdater; - this.actionPlanService = actionPlanService; - this.ruleFinder = ruleFinder; - this.issueNotifications = issueNotifications; - this.resourceDao = resourceDao; - this.issueDao = issueDao; - this.authorizationDao = authorizationDao; - this.userFinder = userFinder; - this.dryRunCache = dryRunCache; - } - - /** - * List of available transitions. - *

- * Never return null, but return an empty list if the issue does not exist. - */ - public List listTransitions(String issueKey, UserSession userSession) { - Issue issue = loadIssue(issueKey).first(); - return listTransitions(issue, userSession); - } +import java.util.Date; +import java.util.List; - /** - * Never return null, but an empty list if the issue does not exist. - * No security check is done since it should already have been done to get the issue - */ - public List listTransitions(@Nullable Issue issue, UserSession userSession) { - if (issue == null) { - return Collections.emptyList(); - } - List outTransitions = workflow.outTransitions(issue); - List allowedTransitions = new ArrayList(); - for (Transition transition : outTransitions) { - DefaultIssue defaultIssue = (DefaultIssue) issue; - String projectKey = defaultIssue.projectKey(); - if (StringUtils.isBlank(transition.requiredProjectPermission()) || - (projectKey != null && userSession.hasProjectPermission(transition.requiredProjectPermission(), projectKey))) { - allowedTransitions.add(transition); - } - } - return allowedTransitions; - } +public interface IssueService extends ServerComponent { + List listStatus(); - public Issue doTransition(String issueKey, String transitionKey, UserSession userSession) { - verifyLoggedIn(userSession); - IssueQueryResult queryResult = loadIssue(issueKey); - DefaultIssue defaultIssue = (DefaultIssue) queryResult.first(); - IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login()); - checkTransitionPermission(transitionKey, userSession, defaultIssue); - if (workflow.doTransition(defaultIssue, transitionKey, context)) { - issueStorage.save(defaultIssue); - issueNotifications.sendChanges(defaultIssue, context, queryResult); - dryRunCache.reportResourceModification(defaultIssue.componentKey()); - } - return defaultIssue; - } + List listTransitions(String issueKey); - private void checkTransitionPermission(String transitionKey, UserSession userSession, DefaultIssue defaultIssue) { - List outTransitions = workflow.outTransitions(defaultIssue); - for (Transition transition : outTransitions) { - String projectKey = defaultIssue.projectKey(); - if (transition.key().equals(transitionKey) && StringUtils.isNotBlank(transition.requiredProjectPermission()) && projectKey != null) { - userSession.checkProjectPermission(transition.requiredProjectPermission(), projectKey); - } - } - } + List listTransitions(@Nullable Issue issue); - public Issue assign(String issueKey, @Nullable String assignee, UserSession userSession) { - verifyLoggedIn(userSession); - IssueQueryResult queryResult = loadIssue(issueKey); - DefaultIssue issue = (DefaultIssue) queryResult.first(); - User user = null; - if (!Strings.isNullOrEmpty(assignee)) { - user = userFinder.findByLogin(assignee); - if (user == null) { - throw new IllegalArgumentException("Unknown user: " + assignee); - } - } - IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login()); - if (issueUpdater.assign(issue, user, context)) { - issueStorage.save(issue); - issueNotifications.sendChanges(issue, context, queryResult); - dryRunCache.reportResourceModification(issue.componentKey()); - } - return issue; - } + Issue doTransition(String issueKey, String transitionKey); - public Issue plan(String issueKey, @Nullable String actionPlanKey, UserSession userSession) { - verifyLoggedIn(userSession); - ActionPlan actionPlan = null; - if (!Strings.isNullOrEmpty(actionPlanKey)) { - actionPlan = actionPlanService.findByKey(actionPlanKey, userSession); - if (actionPlan == null) { - throw new IllegalArgumentException("Unknown action plan: " + actionPlanKey); - } - } - IssueQueryResult queryResult = loadIssue(issueKey); - DefaultIssue issue = (DefaultIssue) queryResult.first(); + Issue assign(String issueKey, @Nullable String assignee); - IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login()); - if (issueUpdater.plan(issue, actionPlan, context)) { - issueStorage.save(issue); - issueNotifications.sendChanges(issue, context, queryResult); - dryRunCache.reportResourceModification(issue.componentKey()); - } - return issue; - } + Issue plan(String issueKey, @Nullable String actionPlanKey); - public Issue setSeverity(String issueKey, String severity, UserSession userSession) { - verifyLoggedIn(userSession); - IssueQueryResult queryResult = loadIssue(issueKey); - DefaultIssue issue = (DefaultIssue) queryResult.first(); - userSession.checkProjectPermission(UserRole.ISSUE_ADMIN, issue.projectKey()); + Issue setSeverity(String issueKey, String severity); - IssueChangeContext context = IssueChangeContext.createUser(new Date(), userSession.login()); - if (issueUpdater.setManualSeverity(issue, severity, context)) { - issueStorage.save(issue); - issueNotifications.sendChanges(issue, context, queryResult); - dryRunCache.reportResourceModification(issue.componentKey()); - } - return issue; - } - - public DefaultIssue createManualIssue(String componentKey, RuleKey ruleKey, @Nullable Integer line, @Nullable String message, @Nullable String severity, - @Nullable Double effortToFix, UserSession userSession) { - verifyLoggedIn(userSession); - ResourceDto component = resourceDao.getResource(ResourceQuery.create().setKey(componentKey)); - ResourceDto project = resourceDao.getRootProjectByComponentKey(componentKey); - if (component == null || project == null) { - throw new IllegalArgumentException("Unknown component: " + componentKey); - } - if (!authorizationDao.isAuthorizedComponentKey(component.getKey(), userSession.userId(), UserRole.USER)) { - // TODO throw unauthorized - throw new IllegalStateException("User does not have the required role"); - } - if (!ruleKey.isManual()) { - throw new IllegalArgumentException("Issues can be created only on rules marked as 'manual': " + ruleKey); - } - Rule rule = findRule(ruleKey); - - DefaultIssue issue = (DefaultIssue) new DefaultIssueBuilder() - .componentKey(component.getKey()) - .projectKey(project.getKey()) - .line(line) - .message(!Strings.isNullOrEmpty(message) ? message : rule.getName()) - .severity(Objects.firstNonNull(severity, Severity.MAJOR)) - .effortToFix(effortToFix) - .ruleKey(ruleKey) - .reporter(UserSession.get().login()) - .build(); - - Date now = new Date(); - issue.setCreationDate(now); - issue.setUpdateDate(now); - issueStorage.save(issue); - dryRunCache.reportResourceModification(component.getKey()); - return issue; - } - - private Rule findRule(RuleKey ruleKey) { - Rule rule = ruleFinder.findByKey(ruleKey); - if (rule == null) { - throw new IllegalArgumentException("Unknown rule: " + ruleKey); - } - return rule; - } - - public IssueQueryResult loadIssue(String issueKey) { - // TODO use IssueIndex for ACL - // TODO load DTO - IssueQueryResult result = finder.find(IssueQuery.builder().issueKeys(Arrays.asList(issueKey)).requiredRole(UserRole.USER).build()); - if (result.issues().size() != 1) { - // TODO throw 404 - throw new IllegalArgumentException("Issue not found: " + issueKey); - } - return result; - } - - public List listStatus() { - return workflow.statusKeys(); - } - - private void verifyLoggedIn(UserSession userSession) { - if (!userSession.isLoggedIn()) { - // must be logged - throw new IllegalStateException("User is not logged in"); - } - } + DefaultIssue createManualIssue(String componentKey, RuleKey ruleKey, @Nullable Integer line, @Nullable String message, @Nullable String severity, + @Nullable Double effortToFix); // TODO result should be replaced by an aggregation object in IssueIndex - public RulesAggregation findRulesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { - RulesAggregation rulesAggregation = new RulesAggregation(); - for (RuleDto ruleDto : issueDao.findRulesByComponent(componentKey, periodDate, session)) { - rulesAggregation.add(ruleDto); - } - return rulesAggregation; - } + RulesAggregation findRulesByComponent(String componentKey, @Nullable Date periodDate, DbSession session); // TODO result should be replaced by an aggregation object in IssueIndex - public Multiset findSeveritiesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { - Multiset aggregation = HashMultiset.create(); - for (String severity : issueDao.findSeveritiesByComponent(componentKey, periodDate, session)) { - aggregation.add(severity); - } - return aggregation; - } - - public Issue getByKey(String key) { - return indexClient.get(IssueIndex.class).getByKey(key); - } + Multiset findSeveritiesByComponent(String componentKey, @Nullable Date periodDate, DbSession session); - public org.sonar.server.search.Result search(IssueQuery query, QueryContext options) { - IssueIndex issueIndex = indexClient.get(IssueIndex.class); - return issueIndex.search(query, options); - } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/OldIssueService.java b/server/sonar-server/src/main/java/org/sonar/server/issue/OldIssueService.java new file mode 100644 index 00000000000..107ac341c95 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/OldIssueService.java @@ -0,0 +1,301 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.Objects; +import com.google.common.base.Strings; +import com.google.common.collect.HashMultiset; +import com.google.common.collect.Multiset; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.issue.ActionPlan; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.IssueQuery; +import org.sonar.api.issue.IssueQueryResult; +import org.sonar.api.issue.internal.DefaultIssue; +import org.sonar.api.issue.internal.IssueChangeContext; +import org.sonar.api.rule.RuleKey; +import org.sonar.api.rule.Severity; +import org.sonar.api.rules.Rule; +import org.sonar.api.rules.RuleFinder; +import org.sonar.api.user.User; +import org.sonar.api.user.UserFinder; +import org.sonar.api.web.UserRole; +import org.sonar.core.issue.DefaultIssueBuilder; +import org.sonar.core.issue.IssueNotifications; +import org.sonar.core.issue.IssueUpdater; +import org.sonar.core.issue.db.IssueDao; +import org.sonar.core.issue.db.IssueStorage; +import org.sonar.core.issue.workflow.IssueWorkflow; +import org.sonar.core.issue.workflow.Transition; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.preview.PreviewCache; +import org.sonar.core.resource.ResourceDao; +import org.sonar.core.resource.ResourceDto; +import org.sonar.core.resource.ResourceQuery; +import org.sonar.core.rule.RuleDto; +import org.sonar.core.user.AuthorizationDao; +import org.sonar.server.issue.actionplan.ActionPlanService; +import org.sonar.server.user.UserSession; + +import javax.annotation.Nullable; + +import java.util.*; + +/** + * @since 3.6 + */ +public class OldIssueService implements IssueService { + + private final DefaultIssueFinder finder; + private final IssueWorkflow workflow; + private final IssueUpdater issueUpdater; + private final IssueStorage issueStorage; + private final IssueNotifications issueNotifications; + private final ActionPlanService actionPlanService; + private final RuleFinder ruleFinder; + private final ResourceDao resourceDao; + private final IssueDao issueDao; + private final AuthorizationDao authorizationDao; + private final UserFinder userFinder; + private final PreviewCache dryRunCache; + + public OldIssueService( + DefaultIssueFinder finder, + IssueWorkflow workflow, + IssueStorage issueStorage, + IssueUpdater issueUpdater, + IssueNotifications issueNotifications, + ActionPlanService actionPlanService, + RuleFinder ruleFinder, + ResourceDao resourceDao, + IssueDao issueDao, + AuthorizationDao authorizationDao, + UserFinder userFinder, + PreviewCache dryRunCache) { + this.finder = finder; + this.workflow = workflow; + this.issueStorage = issueStorage; + this.issueUpdater = issueUpdater; + this.actionPlanService = actionPlanService; + this.ruleFinder = ruleFinder; + this.issueNotifications = issueNotifications; + this.resourceDao = resourceDao; + this.issueDao = issueDao; + this.authorizationDao = authorizationDao; + this.userFinder = userFinder; + this.dryRunCache = dryRunCache; + } + + /** + * List of available transitions. + *

+ * Never return null, but return an empty list if the issue does not exist. + */ + public List listTransitions(String issueKey) { + Issue issue = loadIssue(issueKey).first(); + return listTransitions(issue); + } + + /** + * Never return null, but an empty list if the issue does not exist. + * No security check is done since it should already have been done to get the issue + */ + public List listTransitions(@Nullable Issue issue) { + if (issue == null) { + return Collections.emptyList(); + } + List outTransitions = workflow.outTransitions(issue); + List allowedTransitions = new ArrayList(); + for (Transition transition : outTransitions) { + DefaultIssue defaultIssue = (DefaultIssue) issue; + String projectKey = defaultIssue.projectKey(); + if (StringUtils.isBlank(transition.requiredProjectPermission()) || + (projectKey != null && UserSession.get().hasProjectPermission(transition.requiredProjectPermission(), projectKey))) { + allowedTransitions.add(transition); + } + } + return allowedTransitions; + } + + public Issue doTransition(String issueKey, String transitionKey) { + verifyLoggedIn(); + IssueQueryResult queryResult = loadIssue(issueKey); + DefaultIssue defaultIssue = (DefaultIssue) queryResult.first(); + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + checkTransitionPermission(transitionKey, UserSession.get(), defaultIssue); + if (workflow.doTransition(defaultIssue, transitionKey, context)) { + issueStorage.save(defaultIssue); + issueNotifications.sendChanges(defaultIssue, context, queryResult); + dryRunCache.reportResourceModification(defaultIssue.componentKey()); + } + return defaultIssue; + } + + private void checkTransitionPermission(String transitionKey, UserSession userSession, DefaultIssue defaultIssue) { + List outTransitions = workflow.outTransitions(defaultIssue); + for (Transition transition : outTransitions) { + String projectKey = defaultIssue.projectKey(); + if (transition.key().equals(transitionKey) && StringUtils.isNotBlank(transition.requiredProjectPermission()) && projectKey != null) { + userSession.checkProjectPermission(transition.requiredProjectPermission(), projectKey); + } + } + } + + public Issue assign(String issueKey, @Nullable String assignee) { + verifyLoggedIn(); + IssueQueryResult queryResult = loadIssue(issueKey); + DefaultIssue issue = (DefaultIssue) queryResult.first(); + User user = null; + if (!Strings.isNullOrEmpty(assignee)) { + user = userFinder.findByLogin(assignee); + if (user == null) { + throw new IllegalArgumentException("Unknown user: " + assignee); + } + } + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + if (issueUpdater.assign(issue, user, context)) { + issueStorage.save(issue); + issueNotifications.sendChanges(issue, context, queryResult); + dryRunCache.reportResourceModification(issue.componentKey()); + } + return issue; + } + + public Issue plan(String issueKey, @Nullable String actionPlanKey) { + verifyLoggedIn(); + ActionPlan actionPlan = null; + if (!Strings.isNullOrEmpty(actionPlanKey)) { + actionPlan = actionPlanService.findByKey(actionPlanKey, UserSession.get()); + if (actionPlan == null) { + throw new IllegalArgumentException("Unknown action plan: " + actionPlanKey); + } + } + IssueQueryResult queryResult = loadIssue(issueKey); + DefaultIssue issue = (DefaultIssue) queryResult.first(); + + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + if (issueUpdater.plan(issue, actionPlan, context)) { + issueStorage.save(issue); + issueNotifications.sendChanges(issue, context, queryResult); + dryRunCache.reportResourceModification(issue.componentKey()); + } + return issue; + } + + public Issue setSeverity(String issueKey, String severity) { + verifyLoggedIn(); + IssueQueryResult queryResult = loadIssue(issueKey); + DefaultIssue issue = (DefaultIssue) queryResult.first(); + UserSession.get().checkProjectPermission(UserRole.ISSUE_ADMIN, issue.projectKey()); + + IssueChangeContext context = IssueChangeContext.createUser(new Date(), UserSession.get().login()); + if (issueUpdater.setManualSeverity(issue, severity, context)) { + issueStorage.save(issue); + issueNotifications.sendChanges(issue, context, queryResult); + dryRunCache.reportResourceModification(issue.componentKey()); + } + return issue; + } + + public DefaultIssue createManualIssue(String componentKey, RuleKey ruleKey, @Nullable Integer line, @Nullable String message, @Nullable String severity, + @Nullable Double effortToFix) { + verifyLoggedIn(); + ResourceDto component = resourceDao.getResource(ResourceQuery.create().setKey(componentKey)); + ResourceDto project = resourceDao.getRootProjectByComponentKey(componentKey); + if (component == null || project == null) { + throw new IllegalArgumentException("Unknown component: " + componentKey); + } + if (!authorizationDao.isAuthorizedComponentKey(component.getKey(), UserSession.get().userId(), UserRole.USER)) { + // TODO throw unauthorized + throw new IllegalStateException("User does not have the required role"); + } + if (!ruleKey.isManual()) { + throw new IllegalArgumentException("Issues can be created only on rules marked as 'manual': " + ruleKey); + } + Rule rule = findRule(ruleKey); + + DefaultIssue issue = (DefaultIssue) new DefaultIssueBuilder() + .componentKey(component.getKey()) + .projectKey(project.getKey()) + .line(line) + .message(!Strings.isNullOrEmpty(message) ? message : rule.getName()) + .severity(Objects.firstNonNull(severity, Severity.MAJOR)) + .effortToFix(effortToFix) + .ruleKey(ruleKey) + .reporter(UserSession.get().login()) + .build(); + + Date now = new Date(); + issue.setCreationDate(now); + issue.setUpdateDate(now); + issueStorage.save(issue); + dryRunCache.reportResourceModification(component.getKey()); + return issue; + } + + private Rule findRule(RuleKey ruleKey) { + Rule rule = ruleFinder.findByKey(ruleKey); + if (rule == null) { + throw new IllegalArgumentException("Unknown rule: " + ruleKey); + } + return rule; + } + + public IssueQueryResult loadIssue(String issueKey) { + // TODO use IssueIndex for ACL + // TODO load DTO + IssueQueryResult result = finder.find(IssueQuery.builder().issueKeys(Arrays.asList(issueKey)).requiredRole(UserRole.USER).build()); + if (result.issues().size() != 1) { + // TODO throw 404 + throw new IllegalArgumentException("Issue not found: " + issueKey); + } + return result; + } + + public List listStatus() { + return workflow.statusKeys(); + } + + private void verifyLoggedIn() { + if (!UserSession.get().isLoggedIn()) { + // must be logged + throw new IllegalStateException("User is not logged in"); + } + } + + // TODO result should be replaced by an aggregation object in IssueIndex + public RulesAggregation findRulesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { + RulesAggregation rulesAggregation = new RulesAggregation(); + for (RuleDto ruleDto : issueDao.findRulesByComponent(componentKey, periodDate, session)) { + rulesAggregation.add(ruleDto); + } + return rulesAggregation; + } + + // TODO result should be replaced by an aggregation object in IssueIndex + public Multiset findSeveritiesByComponent(String componentKey, @Nullable Date periodDate, DbSession session) { + Multiset aggregation = HashMultiset.create(); + for (String severity : issueDao.findSeveritiesByComponent(componentKey, periodDate, session)) { + aggregation.add(severity); + } + return aggregation; + } + +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java index e4d7cfa04da..4057524f0f8 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/IssueActionsWriter.java @@ -48,7 +48,7 @@ public class IssueActionsWriter implements ServerComponent { public void writeTransitions(Issue issue, JsonWriter json) { json.name("transitions").beginArray(); if (UserSession.get().isLoggedIn()) { - List transitions = issueService.listTransitions(issue, UserSession.get()); + List transitions = issueService.listTransitions(issue); for (Transition transition : transitions) { json.value(transition.key()); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java index 20ceda385ea..30fd11a0cc2 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java @@ -24,11 +24,7 @@ import com.google.common.base.Strings; import com.google.common.collect.Iterables; import com.google.common.io.Resources; import org.sonar.api.i18n.I18n; -import org.sonar.api.issue.ActionPlan; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.IssueComment; -import org.sonar.api.issue.IssueQuery; -import org.sonar.api.issue.IssueQueryResult; +import org.sonar.api.issue.*; import org.sonar.api.rule.RuleKey; import org.sonar.api.rule.Severity; import org.sonar.api.server.ws.Request; @@ -45,6 +41,7 @@ import org.sonar.core.persistence.DbSession; import org.sonar.markdown.Markdown; import org.sonar.server.component.DefaultComponentFinder; import org.sonar.server.db.DbClient; +import org.sonar.server.issue.DefaultIssueService; import org.sonar.server.issue.IssueService; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.issue.filter.IssueFilterParameters; @@ -58,13 +55,7 @@ import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import static com.google.common.collect.Lists.newArrayList; @@ -229,7 +220,7 @@ public class SearchAction extends SearchRequestHandler { @Override protected Result doSearch(IssueQuery query, QueryContext context) { - return service.search(query, context); + return ((DefaultIssueService)service).search(query, context); } @Override diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java index 9ccca614c62..4e4caaae932 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java @@ -51,13 +51,7 @@ import org.sonar.core.measure.db.MeasureFilterDao; import org.sonar.core.metric.DefaultMetricFinder; import org.sonar.core.notification.DefaultNotificationManager; import org.sonar.core.permission.PermissionFacade; -import org.sonar.core.persistence.DaoUtils; -import org.sonar.core.persistence.DatabaseVersion; -import org.sonar.core.persistence.DefaultDatabase; -import org.sonar.core.persistence.MyBatis; -import org.sonar.core.persistence.PreviewDatabaseFactory; -import org.sonar.core.persistence.SemaphoreUpdater; -import org.sonar.core.persistence.SemaphoresImpl; +import org.sonar.core.persistence.*; import org.sonar.core.preview.PreviewCache; import org.sonar.core.profiling.Profiling; import org.sonar.core.purge.PurgeProfiler; @@ -83,57 +77,27 @@ import org.sonar.server.activity.index.ActivityNormalizer; import org.sonar.server.activity.ws.ActivitiesWebService; import org.sonar.server.activity.ws.ActivityMapping; import org.sonar.server.authentication.ws.AuthenticationWs; -import org.sonar.server.batch.BatchIndex; -import org.sonar.server.batch.BatchWs; -import org.sonar.server.batch.GlobalReferentialsAction; -import org.sonar.server.batch.ProjectReferentialsAction; -import org.sonar.server.batch.UploadReportAction; +import org.sonar.server.batch.*; import org.sonar.server.charts.ChartFactory; import org.sonar.server.component.ComponentCleanerService; import org.sonar.server.component.DefaultComponentFinder; import org.sonar.server.component.DefaultRubyComponentService; import org.sonar.server.component.db.ComponentDao; import org.sonar.server.component.db.SnapshotDao; -import org.sonar.server.component.ws.ComponentAppAction; -import org.sonar.server.component.ws.ComponentsWs; -import org.sonar.server.component.ws.EventsWs; -import org.sonar.server.component.ws.ProjectsWs; -import org.sonar.server.component.ws.ResourcesWs; +import org.sonar.server.component.ws.*; import org.sonar.server.config.ws.PropertiesWs; import org.sonar.server.db.DatabaseChecker; import org.sonar.server.db.DbClient; import org.sonar.server.db.EmbeddedDatabaseFactory; import org.sonar.server.db.migrations.DatabaseMigrations; import org.sonar.server.db.migrations.DatabaseMigrator; -import org.sonar.server.debt.DebtCharacteristicsXMLImporter; -import org.sonar.server.debt.DebtModelBackup; -import org.sonar.server.debt.DebtModelLookup; -import org.sonar.server.debt.DebtModelOperations; -import org.sonar.server.debt.DebtModelPluginRepository; -import org.sonar.server.debt.DebtModelService; -import org.sonar.server.debt.DebtModelXMLExporter; -import org.sonar.server.debt.DebtRulesXMLImporter; +import org.sonar.server.debt.*; import org.sonar.server.design.FileDesignWidget; import org.sonar.server.design.PackageDesignWidget; import org.sonar.server.duplication.ws.DuplicationsJsonWriter; import org.sonar.server.duplication.ws.DuplicationsParser; import org.sonar.server.duplication.ws.DuplicationsWs; -import org.sonar.server.issue.ActionService; -import org.sonar.server.issue.AssignAction; -import org.sonar.server.issue.CommentAction; -import org.sonar.server.issue.DefaultIssueFinder; -import org.sonar.server.issue.InternalRubyIssueService; -import org.sonar.server.issue.IssueBulkChangeService; -import org.sonar.server.issue.IssueChangelogFormatter; -import org.sonar.server.issue.IssueChangelogService; -import org.sonar.server.issue.IssueCommentService; -import org.sonar.server.issue.IssueService; -import org.sonar.server.issue.IssueStatsFinder; -import org.sonar.server.issue.PlanAction; -import org.sonar.server.issue.PublicRubyIssueService; -import org.sonar.server.issue.ServerIssueStorage; -import org.sonar.server.issue.SetSeverityAction; -import org.sonar.server.issue.TransitionAction; +import org.sonar.server.issue.*; import org.sonar.server.issue.actionplan.ActionPlanService; import org.sonar.server.issue.actionplan.ActionPlanWs; import org.sonar.server.issue.db.IssueAuthorizationDao; @@ -167,84 +131,22 @@ import org.sonar.server.platform.ws.L10nWs; import org.sonar.server.platform.ws.RestartHandler; import org.sonar.server.platform.ws.ServerWs; import org.sonar.server.platform.ws.SystemWs; -import org.sonar.server.plugins.InstalledPluginReferentialFactory; -import org.sonar.server.plugins.PluginDownloader; -import org.sonar.server.plugins.ServerExtensionInstaller; -import org.sonar.server.plugins.ServerPluginJarInstaller; -import org.sonar.server.plugins.ServerPluginJarsInstaller; -import org.sonar.server.plugins.ServerPluginRepository; -import org.sonar.server.plugins.UpdateCenterClient; -import org.sonar.server.plugins.UpdateCenterMatrixFactory; +import org.sonar.server.plugins.*; import org.sonar.server.qualitygate.QgateProjectFinder; import org.sonar.server.qualitygate.QualityGates; import org.sonar.server.qualitygate.RegisterQualityGates; -import org.sonar.server.qualitygate.ws.QGatesAppAction; -import org.sonar.server.qualitygate.ws.QGatesCopyAction; -import org.sonar.server.qualitygate.ws.QGatesCreateAction; -import org.sonar.server.qualitygate.ws.QGatesCreateConditionAction; -import org.sonar.server.qualitygate.ws.QGatesDeleteConditionAction; -import org.sonar.server.qualitygate.ws.QGatesDeselectAction; -import org.sonar.server.qualitygate.ws.QGatesDestroyAction; -import org.sonar.server.qualitygate.ws.QGatesListAction; -import org.sonar.server.qualitygate.ws.QGatesRenameAction; -import org.sonar.server.qualitygate.ws.QGatesSearchAction; -import org.sonar.server.qualitygate.ws.QGatesSelectAction; -import org.sonar.server.qualitygate.ws.QGatesSetAsDefaultAction; -import org.sonar.server.qualitygate.ws.QGatesShowAction; -import org.sonar.server.qualitygate.ws.QGatesUnsetDefaultAction; -import org.sonar.server.qualitygate.ws.QGatesUpdateConditionAction; -import org.sonar.server.qualitygate.ws.QGatesWs; -import org.sonar.server.qualityprofile.BuiltInProfiles; -import org.sonar.server.qualityprofile.QProfileBackuper; -import org.sonar.server.qualityprofile.QProfileCopier; -import org.sonar.server.qualityprofile.QProfileExporters; -import org.sonar.server.qualityprofile.QProfileFactory; -import org.sonar.server.qualityprofile.QProfileLoader; -import org.sonar.server.qualityprofile.QProfileLookup; -import org.sonar.server.qualityprofile.QProfileProjectLookup; -import org.sonar.server.qualityprofile.QProfileProjectOperations; -import org.sonar.server.qualityprofile.QProfileRepositoryExporter; -import org.sonar.server.qualityprofile.QProfileReset; -import org.sonar.server.qualityprofile.QProfileService; -import org.sonar.server.qualityprofile.QProfiles; -import org.sonar.server.qualityprofile.RegisterQualityProfiles; -import org.sonar.server.qualityprofile.RuleActivator; -import org.sonar.server.qualityprofile.RuleActivatorContextFactory; +import org.sonar.server.qualitygate.ws.*; +import org.sonar.server.qualityprofile.*; import org.sonar.server.qualityprofile.db.ActiveRuleDao; import org.sonar.server.qualityprofile.index.ActiveRuleIndex; import org.sonar.server.qualityprofile.index.ActiveRuleNormalizer; -import org.sonar.server.qualityprofile.ws.BulkRuleActivationActions; -import org.sonar.server.qualityprofile.ws.ProfilesWs; -import org.sonar.server.qualityprofile.ws.QProfileRestoreBuiltInAction; -import org.sonar.server.qualityprofile.ws.QProfilesWs; -import org.sonar.server.qualityprofile.ws.RuleActivationActions; -import org.sonar.server.rule.DefaultRuleFinder; -import org.sonar.server.rule.DeprecatedRulesDefinition; -import org.sonar.server.rule.RegisterRules; -import org.sonar.server.rule.RubyRuleService; -import org.sonar.server.rule.RuleCreator; -import org.sonar.server.rule.RuleDefinitionsLoader; -import org.sonar.server.rule.RuleDeleter; -import org.sonar.server.rule.RuleOperations; -import org.sonar.server.rule.RuleRepositories; -import org.sonar.server.rule.RuleService; -import org.sonar.server.rule.RuleUpdater; +import org.sonar.server.qualityprofile.ws.*; +import org.sonar.server.rule.*; import org.sonar.server.rule.db.RuleDao; import org.sonar.server.rule.index.RuleIndex; import org.sonar.server.rule.index.RuleNormalizer; -import org.sonar.server.rule.ws.ActiveRuleCompleter; -import org.sonar.server.rule.ws.AppAction; -import org.sonar.server.rule.ws.DeleteAction; -import org.sonar.server.rule.ws.RuleMapping; -import org.sonar.server.rule.ws.RulesWebService; -import org.sonar.server.rule.ws.SearchAction; -import org.sonar.server.rule.ws.TagsAction; -import org.sonar.server.rule.ws.UpdateAction; -import org.sonar.server.search.IndexClient; -import org.sonar.server.search.IndexQueue; -import org.sonar.server.search.IndexSynchronizer; -import org.sonar.server.search.SearchClient; -import org.sonar.server.search.SearchHealth; +import org.sonar.server.rule.ws.*; +import org.sonar.server.search.*; import org.sonar.server.source.CodeColorizers; import org.sonar.server.source.DeprecatedSourceDecorator; import org.sonar.server.source.HtmlSourceDecorator; @@ -253,27 +155,9 @@ import org.sonar.server.source.ws.ScmAction; import org.sonar.server.source.ws.ScmWriter; import org.sonar.server.source.ws.ShowAction; import org.sonar.server.source.ws.SourcesWs; -import org.sonar.server.startup.CleanPreviewAnalysisCache; -import org.sonar.server.startup.CopyRequirementsFromCharacteristicsToRules; -import org.sonar.server.startup.GeneratePluginIndex; -import org.sonar.server.startup.GwtPublisher; -import org.sonar.server.startup.JdbcDriverDeployer; -import org.sonar.server.startup.LogServerId; -import org.sonar.server.startup.RegisterDashboards; -import org.sonar.server.startup.RegisterDebtModel; -import org.sonar.server.startup.RegisterMetrics; -import org.sonar.server.startup.RegisterNewMeasureFilters; -import org.sonar.server.startup.RegisterPermissionTemplates; -import org.sonar.server.startup.RegisterServletFilters; -import org.sonar.server.startup.RenameDeprecatedPropertyKeys; -import org.sonar.server.startup.ServerMetadataPersister; +import org.sonar.server.startup.*; import org.sonar.server.test.CoverageService; -import org.sonar.server.test.ws.CoverageShowAction; -import org.sonar.server.test.ws.CoverageWs; -import org.sonar.server.test.ws.TestsCoveredFilesAction; -import org.sonar.server.test.ws.TestsShowAction; -import org.sonar.server.test.ws.TestsTestCasesAction; -import org.sonar.server.test.ws.TestsWs; +import org.sonar.server.test.ws.*; import org.sonar.server.text.MacroInterpreter; import org.sonar.server.text.RubyTextService; import org.sonar.server.ui.JRubyI18n; @@ -281,23 +165,12 @@ import org.sonar.server.ui.JRubyProfiling; import org.sonar.server.ui.PageDecorations; import org.sonar.server.ui.Views; import org.sonar.server.updatecenter.ws.UpdateCenterWs; -import org.sonar.server.user.DefaultUserService; -import org.sonar.server.user.DoPrivileged; -import org.sonar.server.user.GroupMembershipFinder; -import org.sonar.server.user.GroupMembershipService; -import org.sonar.server.user.NewUserNotifier; -import org.sonar.server.user.SecurityRealmFactory; +import org.sonar.server.user.*; import org.sonar.server.user.db.GroupDao; import org.sonar.server.user.ws.FavoritesWs; import org.sonar.server.user.ws.UserPropertiesWs; import org.sonar.server.user.ws.UsersWs; -import org.sonar.server.util.BooleanTypeValidation; -import org.sonar.server.util.FloatTypeValidation; -import org.sonar.server.util.IntegerTypeValidation; -import org.sonar.server.util.StringListTypeValidation; -import org.sonar.server.util.StringTypeValidation; -import org.sonar.server.util.TextTypeValidation; -import org.sonar.server.util.TypeValidations; +import org.sonar.server.util.*; import org.sonar.server.ws.ListingWs; import org.sonar.server.ws.WebServiceEngine; @@ -595,7 +468,6 @@ class ServerComponents { pico.addSingleton(IssueUpdater.class); pico.addSingleton(FunctionExecutor.class); pico.addSingleton(IssueWorkflow.class); - pico.addSingleton(IssueService.class); pico.addSingleton(IssueCommentService.class); pico.addSingleton(DefaultIssueFinder.class); pico.addSingleton(IssueStatsFinder.class); @@ -613,8 +485,10 @@ class ServerComponents { // Switch Issue search if (properties.getProperty("sonar.issues.use_es_backend", null) != null) { pico.addSingleton(org.sonar.server.issue.ws.SearchAction.class); + pico.addSingleton(DefaultIssueService.class); } else { pico.addSingleton(IssueSearchAction.class); + pico.addSingleton(OldIssueService.class); } pico.addSingleton(IssueActionsWriter.class); diff --git a/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java b/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java index 8a847ef35ea..1ffcb84bf46 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java +++ b/server/sonar-server/src/main/java/org/sonar/server/search/IndexSynchronizer.java @@ -21,6 +21,7 @@ package org.sonar.server.search; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.sonar.api.config.Settings; import org.sonar.core.persistence.DbSession; import org.sonar.server.activity.index.ActivityIndex; import org.sonar.server.db.Dao; @@ -41,10 +42,12 @@ public class IndexSynchronizer { private final DbClient db; private final IndexClient index; + private final Settings settings; - public IndexSynchronizer(DbClient db, IndexClient index) { + public IndexSynchronizer(DbClient db, IndexClient index, Settings settings) { this.db = db; this.index = index; + this.settings = settings; } public void execute() { @@ -53,8 +56,13 @@ public class IndexSynchronizer { LOG.info("Starting DB to Index synchronization"); long start = System.currentTimeMillis(); synchronize(session, db.ruleDao(), index.get(RuleIndex.class)); - synchronize(session, db.issueDao(), index.get(IssueIndex.class)); - synchronize(session, db.issueAuthorizationDao(), index.get(IssueAuthorizationIndex.class)); + + // Switch Issue search + if (settings.getString("sonar.issues.use_es_backend") != null) { + synchronize(session, db.issueDao(), index.get(IssueIndex.class)); + synchronize(session, db.issueAuthorizationDao(), index.get(IssueAuthorizationIndex.class)); + } + synchronize(session, db.activeRuleDao(), index.get(ActiveRuleIndex.class)); synchronize(session, db.activityDao(), index.get(ActivityIndex.class)); session.commit(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java index 5de5a0bac76..34a0835706e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java @@ -350,7 +350,6 @@ public class ComponentAppActionTest { MockUserSession.set().addComponentPermission(UserRole.USER, SUB_PROJECT_KEY, COMPONENT_KEY); addComponent(); - addMeasure(CoreMetrics.IT_COVERAGE_KEY, 85.2); WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); @@ -365,7 +364,7 @@ public class ComponentAppActionTest { when(resourceDao.getLastSnapshotByResourceId(eq(1L), eq(session))).thenReturn( new SnapshotDto().setPeriodMode(1, "previous_analysis").setPeriodDate(1, DateUtils.parseDate("2014-05-08")) - ); + ); when(periods.label(anyString(), anyString(), any(Date.class))).thenReturn("since previous analysis (May 08 2014)"); WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); @@ -409,7 +408,7 @@ public class ComponentAppActionTest { addComponent(); when(issueService.findRulesByComponent(COMPONENT_KEY, null, session)).thenReturn( new RulesAggregation().add(new RuleDto().setRuleKey("AvoidCycle").setRepositoryKey("squid").setName("Avoid Cycle")) - ); + ); WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); request.execute().assertJson(getClass(), "app_with_rules.json"); @@ -424,12 +423,12 @@ public class ComponentAppActionTest { Date periodDate = DateUtils.parseDate("2014-05-08"); when(resourceDao.getLastSnapshotByResourceId(eq(1L), eq(session))).thenReturn( new SnapshotDto().setPeriodMode(1, "previous_analysis").setPeriodDate(1, periodDate) - ); + ); when(periods.label(anyString(), anyString(), any(Date.class))).thenReturn("since previous analysis (May 08 2014)"); when(issueService.findRulesByComponent(COMPONENT_KEY, periodDate, session)).thenReturn( new RulesAggregation().add(new RuleDto().setRuleKey("AvoidCycle").setRepositoryKey("squid").setName("Avoid Cycle")) - ); + ); WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY).setParam("period", "1"); request.execute().assertJson(getClass(), "app_with_rules_when_period_is_set.json"); @@ -485,11 +484,11 @@ public class ComponentAppActionTest { when(componentDao.getById(1L, session)).thenReturn(new ComponentDto().setId(1L).setLongName("SonarQube").setKey("org.codehaus.sonar:sonar")); } - private void addPeriod(){ + private void addPeriod() { Date periodDate = DateUtils.parseDate("2014-05-08"); when(resourceDao.getLastSnapshotByResourceId(eq(1L), eq(session))).thenReturn( new SnapshotDto().setPeriodMode(1, "previous_analysis").setPeriodDate(1, periodDate) - ); + ); when(periods.label(anyString(), anyString(), any(Date.class))).thenReturn("since previous analysis (May 08 2014)"); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueServiceMediumTest.java new file mode 100644 index 00000000000..708b5dc80cb --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/DefaultIssueServiceMediumTest.java @@ -0,0 +1,391 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.collect.Multiset; +import org.junit.After; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.sonar.api.issue.DefaultTransitions; +import org.sonar.api.issue.Issue; +import org.sonar.api.issue.IssueQuery; +import org.sonar.api.resources.Qualifiers; +import org.sonar.api.resources.Scopes; +import org.sonar.api.rule.Severity; +import org.sonar.api.security.DefaultGroups; +import org.sonar.api.utils.DateUtils; +import org.sonar.api.web.UserRole; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.issue.db.ActionPlanDto; +import org.sonar.core.issue.db.IssueDto; +import org.sonar.core.issue.workflow.Transition; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.permission.PermissionFacade; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.rule.RuleDto; +import org.sonar.core.user.UserDto; +import org.sonar.server.component.SnapshotTesting; +import org.sonar.server.component.db.ComponentDao; +import org.sonar.server.component.db.SnapshotDao; +import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.issue.db.IssueDao; +import org.sonar.server.issue.index.IssueIndex; +import org.sonar.server.rule.RuleTesting; +import org.sonar.server.rule.db.RuleDao; +import org.sonar.server.search.IndexClient; +import org.sonar.server.search.QueryContext; +import org.sonar.server.tester.ServerTester; +import org.sonar.server.user.MockUserSession; + +import java.util.Date; +import java.util.List; +import java.util.UUID; + +import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; + +public class DefaultIssueServiceMediumTest { + + @ClassRule + public static ServerTester tester = new ServerTester() + .setProperty("sonar.issues.use_es_backend", "true"); + + DbClient db; + IndexClient indexClient; + DbSession session; + IssueService service; + + RuleDto rule; + ComponentDto project; + ComponentDto file; + UserDto connectedUser; + + @Before + public void setUp() throws Exception { + tester.clearDbAndIndexes(); + db = tester.get(DbClient.class); + indexClient = tester.get(IndexClient.class); + session = db.openSession(false); + service = tester.get(IssueService.class); + + rule = RuleTesting.newXooX1(); + tester.get(RuleDao.class).insert(session, rule); + + project = new ComponentDto() + .setKey("MyProject") + .setLongName("My Project") + .setQualifier(Qualifiers.PROJECT) + .setScope(Scopes.PROJECT); + tester.get(ComponentDao.class).insert(session, project); + tester.get(SnapshotDao.class).insert(session, SnapshotTesting.createForComponent(project)); + + file = new ComponentDto() + .setProjectId(project.getId()) + .setSubProjectId(project.getId()) + .setKey("MyComponent") + .setLongName("My Component"); + tester.get(ComponentDao.class).insert(session, file); + tester.get(SnapshotDao.class).insert(session, SnapshotTesting.createForComponent(file)); + + // project can be seen by anyone + tester.get(PermissionFacade.class).insertGroupPermission(project.getId(), DefaultGroups.ANYONE, UserRole.USER, session); + db.issueAuthorizationDao().synchronizeAfter(session, new Date(0)); + + connectedUser = new UserDto().setLogin("gandalf").setName("Gandalf"); + db.userDao().insert(session, connectedUser); + tester.get(PermissionFacade.class).insertUserPermission(project.getId(), connectedUser.getId(), UserRole.USER, session); + + MockUserSession.set() + .setLogin(connectedUser.getLogin()) + .setUserId(connectedUser.getId().intValue()) + .setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN) + .addProjectPermissions(UserRole.USER, project.key()) + .addProjectPermissions(UserRole.ISSUE_ADMIN, project.key()); + + session.commit(); + } + + @After + public void after() { + session.close(); + } + + @Test + public void can_facet() throws Exception { + IssueDto issue1 = newIssue().setActionPlanKey("P1"); + IssueDto issue2 = newIssue().setActionPlanKey("P2").setResolution("NONE"); + tester.get(IssueDao.class).insert(session, issue1, issue2); + session.commit(); + + org.sonar.server.search.Result result = ((DefaultIssueService) service).search(IssueQuery.builder().build(), new QueryContext()); + assertThat(result.getHits()).hasSize(2); + assertThat(result.getFacets()).isEmpty(); + + result = ((DefaultIssueService) service).search(IssueQuery.builder().build(), new QueryContext().setFacet(true)); + assertThat(result.getFacets().keySet()).hasSize(4); + assertThat(result.getFacetKeys("actionPlan")).hasSize(2); + } + + @Test + public void list_status() { + assertThat(service.listStatus()).containsExactly("OPEN", "CONFIRMED", "REOPENED", "RESOLVED", "CLOSED"); + } + + @Test + public void list_transitions() { + IssueDto issue = newIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE); + tester.get(IssueDao.class).insert(session, issue); + session.commit(); + + List result = service.listTransitions(issue.getKey()); + assertThat(result).hasSize(1); + assertThat(result.get(0).key()).isEqualTo("reopen"); + } + + @Test + public void do_transition() { + IssueDto issue = newIssue().setStatus(Issue.STATUS_OPEN); + tester.get(IssueDao.class).insert(session, issue); + session.commit(); + + assertThat(db.issueDao().getByKey(session, issue.getKey())).isNotNull(); + IssueTesting.assertIsEquivalent(issue, indexClient.get(IssueIndex.class).getByKey(issue.getKey())); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).status()).isEqualTo(Issue.STATUS_OPEN); + + service.doTransition(issue.getKey(), DefaultTransitions.CONFIRM); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).status()).isEqualTo(Issue.STATUS_CONFIRMED); + } + + @Test + public void assign() { + IssueDto issue = newIssue(); + tester.get(IssueDao.class).insert(session, issue); + + UserDto user = new UserDto().setLogin("perceval").setName("Perceval"); + db.userDao().insert(session, user); + session.commit(); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).assignee()).isNull(); + + service.assign(issue.getKey(), user.getLogin()); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).assignee()).isEqualTo("perceval"); + } + + @Test + public void un_assign() { + IssueDto issue = newIssue().setAssignee("perceval"); + tester.get(IssueDao.class).insert(session, issue); + + UserDto user = new UserDto().setLogin("perceval").setName("Perceval"); + db.userDao().insert(session, user); + session.commit(); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).assignee()).isEqualTo("perceval"); + + service.assign(issue.getKey(), ""); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).assignee()).isNull(); + } + + @Test + public void fail_to_assign_on_unknown_user() { + IssueDto issue = newIssue(); + tester.get(IssueDao.class).insert(session, issue); + session.commit(); + + try { + service.assign(issue.getKey(), "unknown"); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(NotFoundException.class).hasMessage("Unknown user: unknown"); + } + } + + @Test + public void plan() { + IssueDto issue = newIssue(); + tester.get(IssueDao.class).insert(session, issue); + + String actionPlanKey = "EFGH"; + db.actionPlanDao().save(new ActionPlanDto().setKey(actionPlanKey).setProjectId(project.getId())); + session.commit(); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).actionPlanKey()).isNull(); + + service.plan(issue.getKey(), actionPlanKey); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).actionPlanKey()).isEqualTo(actionPlanKey); + } + + @Test + public void un_plan() { + String actionPlanKey = "EFGH"; + db.actionPlanDao().save(new ActionPlanDto().setKey(actionPlanKey).setProjectId(project.getId())); + + IssueDto issue = newIssue().setActionPlanKey(actionPlanKey); + tester.get(IssueDao.class).insert(session, issue); + session.commit(); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).actionPlanKey()).isEqualTo(actionPlanKey); + + service.plan(issue.getKey(), null); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).actionPlanKey()).isNull(); + } + + @Test + public void fail_plan_if_action_plan_not_found() { + tester.get(IssueDao.class).insert(session, newIssue()); + session.commit(); + + try { + service.plan("ABCD", "unknown"); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(NotFoundException.class).hasMessage("Unknown action plan: unknown"); + } + } + + @Test + public void set_severity() { + IssueDto issue = newIssue().setSeverity(Severity.BLOCKER); + tester.get(IssueDao.class).insert(session, issue); + session.commit(); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).severity()).isEqualTo(Severity.BLOCKER); + + service.setSeverity(issue.getKey(), Severity.MINOR); + + assertThat(indexClient.get(IssueIndex.class).getByKey(issue.getKey()).severity()).isEqualTo(Severity.MINOR); + } + + @Test + public void create_manual_issue() { + RuleDto manualRule = RuleTesting.newManualRule("manualRuleKey"); + tester.get(RuleDao.class).insert(session, manualRule); + session.commit(); + + Issue result = service.createManualIssue(file.key(), manualRule.getKey(), 10, "Fix it", Severity.MINOR, 2d); + + Issue manualIssue = indexClient.get(IssueIndex.class).getByKey(result.key()); + assertThat(manualIssue.componentKey()).isEqualTo(file.key()); + assertThat(manualIssue.projectKey()).isEqualTo(project.key()); + assertThat(manualIssue.ruleKey()).isEqualTo(manualRule.getKey()); + assertThat(manualIssue.message()).isEqualTo("Fix it"); + assertThat(manualIssue.line()).isEqualTo(10); + assertThat(manualIssue.severity()).isEqualTo(Severity.MINOR); + assertThat(manualIssue.effortToFix()).isEqualTo(2d); + assertThat(manualIssue.reporter()).isEqualTo(connectedUser.getLogin()); + } + + @Test + public void create_manual_issue_with_major_severity_when_no_severity() { + RuleDto manualRule = RuleTesting.newManualRule("manualRuleKey"); + tester.get(RuleDao.class).insert(session, manualRule); + session.commit(); + + Issue result = service.createManualIssue(file.key(), manualRule.getKey(), 10, "Fix it", null, 2d); + + Issue manualIssue = indexClient.get(IssueIndex.class).getByKey(result.key()); + assertThat(manualIssue.severity()).isEqualTo(Severity.MAJOR); + } + + @Test + public void create_manual_issue_with_rule_name_when_no_message() { + RuleDto manualRule = RuleTesting.newManualRule("manualRuleKey").setName("Manual rule name"); + tester.get(RuleDao.class).insert(session, manualRule); + session.commit(); + + Issue result = service.createManualIssue(file.key(), manualRule.getKey(), 10, null, null, 2d); + + Issue manualIssue = indexClient.get(IssueIndex.class).getByKey(result.key()); + assertThat(manualIssue.message()).isEqualTo("Manual rule name"); + } + + @Test + public void fail_create_manual_issue_on_not_manual_rule() { + try { + service.createManualIssue(file.key(), rule.getKey(), 10, "Fix it", null, 2d); + fail(); + } catch (Exception e) { + assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Issues can be created only on rules marked as 'manual': xoo:x1"); + } + } + + @Test(expected = ForbiddenException.class) + public void fail_create_manual_issue_if_not_having_required_role() { + // User has not the 'user' role on the project + MockUserSession.set() + .setLogin(connectedUser.getLogin()) + .setUserId(connectedUser.getId().intValue()) + .setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN) + .addProjectPermissions(UserRole.CODEVIEWER, project.key()); + + RuleDto manualRule = RuleTesting.newManualRule("manualRuleKey"); + tester.get(RuleDao.class).insert(session, manualRule); + session.commit(); + + service.createManualIssue(file.key(), rule.getKey(), 10, "Fix it", null, 2d); + } + + @Test + public void find_rules_by_component() throws Exception { + // 2 issues on the same rule + tester.get(IssueDao.class).insert(session, newIssue().setRule(rule)); + tester.get(IssueDao.class).insert(session, newIssue().setRule(rule)); + session.commit(); + + RulesAggregation result = service.findRulesByComponent(file.key(), null, session); + assertThat(result.rules()).hasSize(1); + } + + @Test + public void find_rules_by_severity() throws Exception { + tester.get(IssueDao.class).insert(session, newIssue().setSeverity(Severity.MAJOR)); + tester.get(IssueDao.class).insert(session, newIssue().setSeverity(Severity.MAJOR)); + tester.get(IssueDao.class).insert(session, newIssue().setSeverity(Severity.INFO)); + session.commit(); + + Multiset result = service.findSeveritiesByComponent(file.key(), null, session); + assertThat(result.count("MAJOR")).isEqualTo(2); + assertThat(result.count("INFO")).isEqualTo(1); + assertThat(result.count("UNKNOWN")).isEqualTo(0); + } + + private IssueDto newIssue() { + return new IssueDto() + .setIssueCreationDate(DateUtils.parseDate("2014-09-04")) + .setIssueUpdateDate(DateUtils.parseDate("2014-12-04")) + .setRule(rule) + .setDebt(10L) + .setRootComponent(project) + .setComponent(file) + .setStatus(Issue.STATUS_OPEN) + .setResolution(null) + .setSeverity(Severity.MAJOR) + .setKee(UUID.randomUUID().toString()); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyDefaultIssueServiceTest.java similarity index 96% rename from server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java rename to server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyDefaultIssueServiceTest.java index 508890be52b..b807c0bc5f2 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyIssueServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/InternalRubyDefaultIssueServiceTest.java @@ -59,34 +59,34 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) -public class InternalRubyIssueServiceTest { +public class InternalRubyDefaultIssueServiceTest { @Mock IssueService issueService; @Mock - IssueCommentService commentService = mock(IssueCommentService.class); + IssueCommentService commentService; @Mock - IssueChangelogService changelogService = mock(IssueChangelogService.class); + IssueChangelogService changelogService; @Mock - ActionPlanService actionPlanService = mock(ActionPlanService.class); + ActionPlanService actionPlanService; @Mock - ResourceDao resourceDao = mock(ResourceDao.class); + ResourceDao resourceDao; @Mock - IssueStatsFinder issueStatsFinder = mock(IssueStatsFinder.class); + IssueStatsFinder issueStatsFinder; @Mock - ActionService actionService = mock(ActionService.class); + ActionService actionService; @Mock - IssueFilterService issueFilterService = mock(IssueFilterService.class); + IssueFilterService issueFilterService; @Mock - IssueBulkChangeService issueBulkChangeService = mock(IssueBulkChangeService.class); + IssueBulkChangeService issueBulkChangeService; InternalRubyIssueService service; @@ -107,14 +107,14 @@ public class InternalRubyIssueServiceTest { @Test public void list_transitions_by_issue_key() throws Exception { service.listTransitions("ABCD"); - verify(issueService).listTransitions(eq("ABCD"), any(UserSession.class)); + verify(issueService).listTransitions(eq("ABCD")); } @Test public void list_transitions_by_issue() throws Exception { Issue issue = new DefaultIssue().setKey("ABCD"); service.listTransitions(issue); - verify(issueService).listTransitions(eq(issue), any(UserSession.class)); + verify(issueService).listTransitions(eq(issue)); } @Test @@ -141,7 +141,7 @@ public class InternalRubyIssueServiceTest { @Test public void do_transition() throws Exception { service.doTransition("ABCD", Issue.STATUS_RESOLVED); - verify(issueService).doTransition(eq("ABCD"), eq(Issue.STATUS_RESOLVED), any(UserSession.class)); + verify(issueService).doTransition(eq("ABCD"), eq(Issue.STATUS_RESOLVED)); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java deleted file mode 100644 index ac4e5069a02..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceMediumTest.java +++ /dev/null @@ -1,165 +0,0 @@ -/* - * SonarQube, open source software quality management tool. - * Copyright (C) 2008-2014 SonarSource - * mailto:contact AT sonarsource DOT com - * - * SonarQube 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. - * - * SonarQube 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 org.junit.After; -import org.junit.Before; -import org.junit.ClassRule; -import org.junit.Test; -import org.sonar.api.issue.DefaultTransitions; -import org.sonar.api.issue.Issue; -import org.sonar.api.issue.IssueQuery; -import org.sonar.api.resources.Qualifiers; -import org.sonar.api.resources.Scopes; -import org.sonar.api.rule.Severity; -import org.sonar.api.security.DefaultGroups; -import org.sonar.api.utils.DateUtils; -import org.sonar.api.web.UserRole; -import org.sonar.core.component.ComponentDto; -import org.sonar.core.issue.db.IssueDto; -import org.sonar.core.permission.GlobalPermissions; -import org.sonar.core.permission.PermissionFacade; -import org.sonar.core.persistence.DbSession; -import org.sonar.core.rule.RuleDto; -import org.sonar.core.user.UserDto; -import org.sonar.server.component.SnapshotTesting; -import org.sonar.server.component.db.ComponentDao; -import org.sonar.server.component.db.SnapshotDao; -import org.sonar.server.db.DbClient; -import org.sonar.server.issue.db.IssueDao; -import org.sonar.server.issue.index.IssueIndex; -import org.sonar.server.rule.RuleTesting; -import org.sonar.server.rule.db.RuleDao; -import org.sonar.server.search.IndexClient; -import org.sonar.server.search.QueryContext; -import org.sonar.server.tester.ServerTester; -import org.sonar.server.user.MockUserSession; -import org.sonar.server.user.UserSession; - -import java.util.Date; -import java.util.UUID; - -import static org.fest.assertions.Assertions.assertThat; - -public class IssueServiceMediumTest { - - @ClassRule - public static ServerTester tester = new ServerTester(); - - DbClient db; - IndexClient indexClient; - DbSession session; - IssueService service; - UserSession userSession; - - RuleDto rule; - ComponentDto project; - ComponentDto resource; - - @Before - public void setUp() throws Exception { - tester.clearDbAndIndexes(); - db = tester.get(DbClient.class); - indexClient = tester.get(IndexClient.class); - session = db.openSession(false); - service = tester.get(IssueService.class); - - rule = RuleTesting.newXooX1(); - tester.get(RuleDao.class).insert(session, rule); - - project = new ComponentDto() - .setKey("MyProject") - .setLongName("My Project") - .setQualifier(Qualifiers.PROJECT) - .setScope(Scopes.PROJECT); - tester.get(ComponentDao.class).insert(session, project); - tester.get(SnapshotDao.class).insert(session, SnapshotTesting.createForComponent(project)); - - resource = new ComponentDto() - .setProjectId(project.getId()) - .setKey("MyComponent") - .setLongName("My Component"); - tester.get(ComponentDao.class).insert(session, resource); - tester.get(SnapshotDao.class).insert(session, SnapshotTesting.createForComponent(resource)); - - // project can be seen by anyone - tester.get(PermissionFacade.class).insertGroupPermission(project.getId(), DefaultGroups.ANYONE, UserRole.USER, session); - db.issueAuthorizationDao().synchronizeAfter(session, new Date(0)); - - UserDto user = new UserDto().setLogin("gandalf").setName("Gandalf"); - db.userDao().insert(session, user); - tester.get(PermissionFacade.class).insertUserPermission(project.getId(), user.getId(), UserRole.USER, session); - - userSession = MockUserSession.create().setLogin(user.getLogin()).setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN) - .addProjectPermissions(UserRole.USER, project.key()); - - session.commit(); - } - - @After - public void after() { - session.close(); - } - - @Test - public void can_facet() throws Exception { - IssueDto issue1 = getIssue().setActionPlanKey("P1"); - IssueDto issue2 = getIssue().setActionPlanKey("P2").setResolution("NONE"); - tester.get(IssueDao.class).insert(session, issue1, issue2); - session.commit(); - - org.sonar.server.search.Result result = service.search(IssueQuery.builder().build(), new QueryContext()); - assertThat(result.getHits()).hasSize(2); - assertThat(result.getFacets()).isEmpty(); - - result = service.search(IssueQuery.builder().build(), new QueryContext().setFacet(true)); - assertThat(result.getFacets().keySet()).hasSize(4); - assertThat(result.getFacetKeys("actionPlan")).hasSize(2); - } - - @Test - public void do_transition() { - IssueDto issue = getIssue(); - tester.get(IssueDao.class).insert(session, issue); - session.commit(); - - assertThat(db.issueDao().getByKey(session, issue.getKey())).isNotNull(); - - IssueTesting.assertIsEquivalent(issue, indexClient.get(IssueIndex.class).getByKey(issue.getKey())); - - service.doTransition(issue.getKey(), DefaultTransitions.CONFIRM, userSession); - Issue issueDoc = indexClient.get(IssueIndex.class).getByKey(issue.getKey()); - assertThat(issueDoc.status()).isEqualTo(Issue.STATUS_CONFIRMED); - } - - private IssueDto getIssue() { - return new IssueDto() - .setIssueCreationDate(DateUtils.parseDate("2014-09-04")) - .setIssueUpdateDate(DateUtils.parseDate("2014-12-04")) - .setRule(rule) - .setDebt(10L) - .setRootComponent(project) - .setComponent(resource) - .setStatus(Issue.STATUS_OPEN) - .setResolution(null) - .setSeverity(Severity.MAJOR) - .setKee(UUID.randomUUID().toString()); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/OldDefaultIssueServiceTest.java similarity index 92% rename from server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java rename to server/sonar-server/src/test/java/org/sonar/server/issue/OldDefaultIssueServiceTest.java index 4f555e56a9b..ceb1db14f4b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/IssueServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/OldDefaultIssueServiceTest.java @@ -46,6 +46,7 @@ import org.sonar.core.issue.db.IssueDao; import org.sonar.core.issue.db.IssueStorage; import org.sonar.core.issue.workflow.IssueWorkflow; import org.sonar.core.issue.workflow.Transition; +import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.DbSession; import org.sonar.core.preview.PreviewCache; import org.sonar.core.resource.ResourceDao; @@ -55,6 +56,7 @@ import org.sonar.core.rule.RuleDto; import org.sonar.core.user.AuthorizationDao; import org.sonar.core.user.DefaultUser; import org.sonar.server.issue.actionplan.ActionPlanService; +import org.sonar.server.user.MockUserSession; import org.sonar.server.user.UserSession; import java.util.Collections; @@ -71,7 +73,7 @@ import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; @RunWith(MockitoJUnitRunner.class) -public class IssueServiceTest { +public class OldDefaultIssueServiceTest { @Mock DefaultIssueFinder finder; @@ -106,7 +108,6 @@ public class IssueServiceTest { @Mock UserFinder userFinder; - @Mock UserSession userSession; @Mock @@ -122,13 +123,16 @@ public class IssueServiceTest { DefaultIssue issue = new DefaultIssue().setKey("ABCD"); - IssueService issueService; + OldIssueService issueService; @Before public void before() { - when(userSession.isLoggedIn()).thenReturn(true); - when(userSession.userId()).thenReturn(10); - when(userSession.login()).thenReturn("arthur"); + userSession = MockUserSession.set() + .setLogin("arthur") + .setUserId(10) + .setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN) + .addProjectPermissions(UserRole.USER, project.getKey()) + .addProjectPermissions(UserRole.ISSUE_ADMIN, project.getKey()); when(authorizationDao.isAuthorizedComponentKey(anyString(), eq(10), anyString())).thenReturn(true); when(finder.find(any(IssueQuery.class))).thenReturn(issueQueryResult); @@ -138,7 +142,7 @@ public class IssueServiceTest { when(resource.getKey()).thenReturn("org.sonar.Sample"); when(project.getKey()).thenReturn("Sample"); - issueService = new IssueService(finder, workflow, issueStorage, issueUpdater, issueNotifications, actionPlanService, ruleFinder, resourceDao, issueDao, + issueService = new OldIssueService(finder, workflow, issueStorage, issueUpdater, issueNotifications, actionPlanService, ruleFinder, resourceDao, issueDao, authorizationDao, userFinder, mock(PreviewCache.class)); } @@ -172,7 +176,7 @@ public class IssueServiceTest { List transitions = newArrayList(transition); when(workflow.outTransitions(issue)).thenReturn(transitions); - List result = issueService.listTransitions("ABCD", userSession); + List result = issueService.listTransitions("ABCD"); assertThat(result).hasSize(1); assertThat(result.get(0)).isEqualTo(transition); } @@ -182,7 +186,7 @@ public class IssueServiceTest { when(issueQueryResult.first()).thenReturn(null); when(issueQueryResult.issues()).thenReturn(newArrayList((Issue) new DefaultIssue())); - assertThat(issueService.listTransitions("ABCD", userSession)).isEmpty(); + assertThat(issueService.listTransitions("ABCD")).isEmpty(); verifyZeroInteractions(workflow); } @@ -190,7 +194,7 @@ public class IssueServiceTest { public void do_transition() { when(workflow.doTransition(eq(issue), eq(transition.key()), any(IssueChangeContext.class))).thenReturn(true); - Issue result = issueService.doTransition("ABCD", transition.key(), userSession); + Issue result = issueService.doTransition("ABCD", transition.key()); assertThat(result).isNotNull(); ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); @@ -208,7 +212,7 @@ public class IssueServiceTest { public void not_do_transition() { when(workflow.doTransition(eq(issue), eq(transition.key()), any(IssueChangeContext.class))).thenReturn(false); - Issue result = issueService.doTransition("ABCD", transition.key(), userSession); + Issue result = issueService.doTransition("ABCD", transition.key()); assertThat(result).isNotNull(); verify(workflow).doTransition(eq(issue), eq(transition.key()), any(IssueChangeContext.class)); verifyZeroInteractions(issueStorage); @@ -217,9 +221,9 @@ public class IssueServiceTest { @Test public void fail_do_transition_if_not_logged() { - when(userSession.isLoggedIn()).thenReturn(false); + MockUserSession.set(); try { - issueService.doTransition("ABCD", transition.key(), userSession); + issueService.doTransition("ABCD", transition.key()); fail(); } catch (Exception e) { assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("User is not logged in"); @@ -235,7 +239,7 @@ public class IssueServiceTest { when(userFinder.findByLogin(assignee)).thenReturn(user); when(issueUpdater.assign(eq(issue), eq(user), any(IssueChangeContext.class))).thenReturn(true); - Issue result = issueService.assign("ABCD", assignee, userSession); + Issue result = issueService.assign("ABCD", assignee); assertThat(result).isNotNull(); ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); @@ -253,7 +257,7 @@ public class IssueServiceTest { public void unassign() { when(issueUpdater.assign(eq(issue), eq((User) null), any(IssueChangeContext.class))).thenReturn(true); - Issue result = issueService.assign("ABCD", null, userSession); + Issue result = issueService.assign("ABCD", null); assertThat(result).isNotNull(); ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); @@ -276,7 +280,7 @@ public class IssueServiceTest { when(userFinder.findByLogin(assignee)).thenReturn(user); when(issueUpdater.assign(eq(issue), eq(user), any(IssueChangeContext.class))).thenReturn(false); - Issue result = issueService.assign("ABCD", assignee, userSession); + Issue result = issueService.assign("ABCD", assignee); assertThat(result).isNotNull(); verify(issueUpdater).assign(eq(issue), eq(user), any(IssueChangeContext.class)); @@ -291,7 +295,7 @@ public class IssueServiceTest { when(userFinder.findByLogin(assignee)).thenReturn(null); try { - issueService.assign("ABCD", assignee, userSession); + issueService.assign("ABCD", assignee); fail(); } catch (Exception e) { assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown user: perceval"); @@ -311,7 +315,7 @@ public class IssueServiceTest { when(actionPlanService.findByKey(actionPlanKey, userSession)).thenReturn(actionPlan); when(issueUpdater.plan(eq(issue), eq(actionPlan), any(IssueChangeContext.class))).thenReturn(true); - Issue result = issueService.plan("ABCD", actionPlanKey, userSession); + Issue result = issueService.plan("ABCD", actionPlanKey); assertThat(result).isNotNull(); ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); @@ -329,7 +333,7 @@ public class IssueServiceTest { public void unplan() { when(issueUpdater.plan(eq(issue), eq((ActionPlan) null), any(IssueChangeContext.class))).thenReturn(true); - Issue result = issueService.plan("ABCD", null, userSession); + Issue result = issueService.plan("ABCD", null); assertThat(result).isNotNull(); ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); @@ -353,7 +357,7 @@ public class IssueServiceTest { when(actionPlanService.findByKey(actionPlanKey, userSession)).thenReturn(actionPlan); when(issueUpdater.plan(eq(issue), eq(actionPlan), any(IssueChangeContext.class))).thenReturn(false); - Issue result = issueService.plan("ABCD", actionPlanKey, userSession); + Issue result = issueService.plan("ABCD", actionPlanKey); assertThat(result).isNotNull(); verify(issueUpdater).plan(eq(issue), eq(actionPlan), any(IssueChangeContext.class)); verifyZeroInteractions(issueStorage); @@ -366,7 +370,7 @@ public class IssueServiceTest { when(actionPlanService.findByKey(actionPlanKey, userSession)).thenReturn(null); try { - issueService.plan("ABCD", actionPlanKey, userSession); + issueService.plan("ABCD", actionPlanKey); fail(); } catch (Exception e) { assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown action plan: EFGH"); @@ -382,7 +386,7 @@ public class IssueServiceTest { String severity = "MINOR"; when(issueUpdater.setManualSeverity(eq(issue), eq(severity), any(IssueChangeContext.class))).thenReturn(true); - Issue result = issueService.setSeverity("ABCD", severity, userSession); + Issue result = issueService.setSeverity("ABCD", severity); assertThat(result).isNotNull(); ArgumentCaptor measureCaptor = ArgumentCaptor.forClass(IssueChangeContext.class); @@ -401,7 +405,7 @@ public class IssueServiceTest { String severity = "MINOR"; when(issueUpdater.setManualSeverity(eq(issue), eq(severity), any(IssueChangeContext.class))).thenReturn(false); - Issue result = issueService.setSeverity("ABCD", severity, userSession); + Issue result = issueService.setSeverity("ABCD", severity); assertThat(result).isNotNull(); verify(issueUpdater).setManualSeverity(eq(issue), eq(severity), any(IssueChangeContext.class)); verifyZeroInteractions(issueStorage); @@ -415,7 +419,7 @@ public class IssueServiceTest { when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource); when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project); - Issue result = issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, "Fix it", null, null, userSession); + Issue result = issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, "Fix it", null, null); assertThat(result).isNotNull(); assertThat(result.message()).isEqualTo("Fix it"); assertThat(result.creationDate()).isNotNull(); @@ -432,7 +436,7 @@ public class IssueServiceTest { when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(resource); when(resourceDao.getRootProjectByComponentKey(anyString())).thenReturn(project); - Issue result = issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, "", null, null, userSession); + Issue result = issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, "", null, null); assertThat(result).isNotNull(); assertThat(result.message()).isEqualTo("Manual Rule"); assertThat(result.creationDate()).isNotNull(); @@ -451,7 +455,7 @@ public class IssueServiceTest { when(authorizationDao.isAuthorizedComponentKey(anyString(), eq(10), anyString())).thenReturn(false); try { - issueService.createManualIssue("org.sonar.Sample", ruleKey, null, null, null, null, userSession); + issueService.createManualIssue("org.sonar.Sample", ruleKey, null, null, null, null); fail(); } catch (Exception e) { assertThat(e).isInstanceOf(IllegalStateException.class).hasMessage("User does not have the required role"); @@ -465,7 +469,7 @@ public class IssueServiceTest { RuleKey ruleKey = RuleKey.of("squid", "s100"); try { - issueService.createManualIssue("org.sonar.Sample", ruleKey, null, null, null, null, userSession); + issueService.createManualIssue("org.sonar.Sample", ruleKey, null, null, null, null); fail(); } catch (Exception e) { assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Issues can be created only on rules marked as 'manual': squid:s100"); @@ -481,7 +485,7 @@ public class IssueServiceTest { RuleKey ruleKey = RuleKey.of("manual", "manualRuleKey"); when(ruleFinder.findByKey(ruleKey)).thenReturn(null); try { - issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, null, null, null, userSession); + issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, null, null, null); fail(); } catch (Exception e) { assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown rule: manual:manualRuleKey"); @@ -495,7 +499,7 @@ public class IssueServiceTest { when(ruleFinder.findByKey(ruleKey)).thenReturn(Rule.create("manual", "manualRuleKey")); when(resourceDao.getResource(any(ResourceQuery.class))).thenReturn(null); try { - issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, null, null, null, userSession); + issueService.createManualIssue("org.sonar.Sample", RuleKey.of("manual", "manualRuleKey"), null, null, null, null); fail(); } catch (Exception e) { assertThat(e).isInstanceOf(IllegalArgumentException.class).hasMessage("Unknown component: org.sonar.Sample"); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyIssueServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyDefaultIssueServiceTest.java similarity index 99% rename from server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyIssueServiceTest.java rename to server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyDefaultIssueServiceTest.java index 95bc3a3fcf2..27d10b67c22 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyIssueServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/PublicRubyDefaultIssueServiceTest.java @@ -38,7 +38,7 @@ import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.*; -public class PublicRubyIssueServiceTest { +public class PublicRubyDefaultIssueServiceTest { IssueFinder finder = mock(IssueFinder.class); PublicRubyIssueService facade = new PublicRubyIssueService(finder); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueBackendMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueBackendMediumTest.java index b2a231eb694..7cae13f4c4d 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueBackendMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueBackendMediumTest.java @@ -50,7 +50,8 @@ import static org.fest.assertions.Assertions.assertThat; public class IssueBackendMediumTest { @ClassRule - public static ServerTester tester = new ServerTester(); + public static ServerTester tester = new ServerTester() + .setProperty("sonar.issues.use_es_backend", "true"); DbClient dbClient; IndexClient indexClient; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueDaoTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueDaoTest.java index 44d4efb6b31..5034e196290 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueDaoTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/db/IssueDaoTest.java @@ -23,15 +23,20 @@ import com.google.common.collect.ImmutableMap; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.sonar.api.rule.RuleKey; import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.System2; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.issue.db.IssueDto; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.persistence.DbSession; +import org.sonar.server.rule.RuleTesting; import java.util.Date; import static org.fest.assertions.Assertions.assertThat; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class IssueDaoTest extends AbstractDaoTestCase { @@ -51,9 +56,42 @@ public class IssueDaoTest extends AbstractDaoTestCase { this.session.close(); } + @Test + public void get_by_key() { + setupData("shared", "get_by_key"); + + IssueDto issue = dao.getByKey(session, "ABCDE"); + assertThat(issue.getKee()).isEqualTo("ABCDE"); + assertThat(issue.getId()).isEqualTo(100L); + assertThat(issue.getComponentId()).isEqualTo(401); + assertThat(issue.getRootComponentId()).isEqualTo(399); + assertThat(issue.getRuleId()).isEqualTo(500); + assertThat(issue.getSeverity()).isEqualTo("BLOCKER"); + assertThat(issue.isManualSeverity()).isFalse(); + assertThat(issue.getMessage()).isNull(); + assertThat(issue.getLine()).isEqualTo(200); + assertThat(issue.getEffortToFix()).isEqualTo(4.2); + assertThat(issue.getStatus()).isEqualTo("OPEN"); + assertThat(issue.getResolution()).isEqualTo("FIXED"); + assertThat(issue.getChecksum()).isEqualTo("XXX"); + assertThat(issue.getAuthorLogin()).isEqualTo("karadoc"); + assertThat(issue.getReporter()).isEqualTo("arthur"); + assertThat(issue.getAssignee()).isEqualTo("perceval"); + assertThat(issue.getIssueAttributes()).isEqualTo("JIRA=FOO-1234"); + assertThat(issue.getIssueCreationDate()).isNotNull(); + assertThat(issue.getIssueUpdateDate()).isNotNull(); + assertThat(issue.getIssueCloseDate()).isNotNull(); + assertThat(issue.getCreatedAt()).isNotNull(); + assertThat(issue.getUpdatedAt()).isNotNull(); + assertThat(issue.getRuleRepo()).isEqualTo("squid"); + assertThat(issue.getRule()).isEqualTo("AvoidCycle"); + assertThat(issue.getComponentKey()).isEqualTo("Action.java"); + assertThat(issue.getRootComponentKey()).isEqualTo("struts"); + } + @Test public void find_after_dates() throws Exception { - setupData("shared", "should_select_all"); + setupData("shared", "some_issues"); Date t0 = new Date(0); assertThat(dao.findAfterDate(session, t0)).hasSize(3); @@ -68,4 +106,76 @@ public class IssueDaoTest extends AbstractDaoTestCase { assertThat(dao.findAfterDate(session, DateUtils.parseDate("2014-01-01"), ImmutableMap.of("project", "struts"))).hasSize(1); } + + @Test + public void insert() throws Exception { + when(system2.now()).thenReturn(DateUtils.parseDate("2013-05-22").getTime()); + + IssueDto dto = new IssueDto(); + dto.setComponent(new ComponentDto().setKey("struts:Action").setId(123L)); + dto.setRootComponent(new ComponentDto().setKey("struts").setId(100L)); + dto.setRule(RuleTesting.newDto(RuleKey.of("squid", "S001")).setId(200)); + dto.setKee("ABCDE"); + dto.setLine(500); + dto.setEffortToFix(3.14); + dto.setDebt(10L); + dto.setResolution("FIXED"); + dto.setStatus("RESOLVED"); + dto.setSeverity("BLOCKER"); + dto.setReporter("emmerik"); + dto.setAuthorLogin("morgan"); + dto.setAssignee("karadoc"); + dto.setActionPlanKey("current_sprint"); + dto.setIssueAttributes("JIRA=FOO-1234"); + dto.setChecksum("123456789"); + dto.setMessage("the message"); + + dto.setIssueCreationDate(DateUtils.parseDate("2013-05-18")); + dto.setIssueUpdateDate(DateUtils.parseDate("2013-05-19")); + dto.setIssueCloseDate(DateUtils.parseDate("2013-05-20")); + dto.setCreatedAt(DateUtils.parseDate("2013-05-21")); + dto.setUpdatedAt(DateUtils.parseDate("2013-05-22")); + + dao.insert(session, dto); + session.commit(); + + checkTables("insert", new String[]{"id"}, "issues"); + } + + @Test + public void update() throws Exception { + when(system2.now()).thenReturn(DateUtils.parseDate("2013-05-22").getTime()); + + setupData("update"); + + IssueDto dto = new IssueDto(); + dto.setComponent(new ComponentDto().setKey("struts:Action").setId(123L)); + dto.setRootComponent(new ComponentDto().setKey("struts").setId(101L)); + dto.setRule(RuleTesting.newDto(RuleKey.of("squid", "S001")).setId(200)); + dto.setKee("ABCDE"); + dto.setLine(500); + dto.setEffortToFix(3.14); + dto.setDebt(10L); + dto.setResolution("FIXED"); + dto.setStatus("RESOLVED"); + dto.setSeverity("BLOCKER"); + dto.setReporter("emmerik"); + dto.setAuthorLogin("morgan"); + dto.setAssignee("karadoc"); + dto.setActionPlanKey("current_sprint"); + dto.setIssueAttributes("JIRA=FOO-1234"); + dto.setChecksum("123456789"); + dto.setMessage("the message"); + + dto.setIssueCreationDate(DateUtils.parseDate("2013-05-18")); + dto.setIssueUpdateDate(DateUtils.parseDate("2013-05-19")); + dto.setIssueCloseDate(DateUtils.parseDate("2013-05-20")); + dto.setCreatedAt(DateUtils.parseDate("2013-05-21")); + dto.setUpdatedAt(DateUtils.parseDate("2013-05-22")); + + dao.update(session, dto); + session.commit(); + + checkTables("update", new String[]{"id"}, "issues"); + } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueAuthorizationIndexMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueAuthorizationIndexMediumTest.java index 0895eedaa5b..6d9942bbbc7 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueAuthorizationIndexMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueAuthorizationIndexMediumTest.java @@ -42,7 +42,8 @@ import static org.fest.assertions.Assertions.assertThat; public class IssueAuthorizationIndexMediumTest { @ClassRule - public static ServerTester tester = new ServerTester(); + public static ServerTester tester = new ServerTester() + .setProperty("sonar.issues.use_es_backend", "true"); DbClient db; DbSession session; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java index 0fdc78077e7..fb95a3f198c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/index/IssueIndexMediumTest.java @@ -54,7 +54,8 @@ import static org.fest.assertions.Assertions.assertThat; public class IssueIndexMediumTest { @ClassRule - public static ServerTester tester = new ServerTester(); + public static ServerTester tester = new ServerTester() + .setProperty("sonar.issues.use_es_backend", "true"); DbClient db; DbSession session; diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java index bb8d97e87b0..0970c222332 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueActionsWriterTest.java @@ -37,12 +37,10 @@ import org.sonar.core.issue.workflow.Transition; import org.sonar.server.issue.ActionService; import org.sonar.server.issue.IssueService; import org.sonar.server.user.MockUserSession; -import org.sonar.server.user.UserSession; import java.io.StringWriter; import static com.google.common.collect.Lists.newArrayList; -import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -77,8 +75,7 @@ public class IssueActionsWriterTest { "{\"actions\": " + "[" + "\"comment\", \"assign\", \"assign_to_me\", \"plan\", \"set_severity\"\n" + - "]}" - ); + "]}"); } @Test @@ -98,8 +95,7 @@ public class IssueActionsWriterTest { "{\"actions\": " + "[" + "\"comment\", \"assign\", \"assign_to_me\", \"plan\", \"link-to-jira\"\n" + - "]}" - ); + "]}"); } @Test @@ -117,8 +113,7 @@ public class IssueActionsWriterTest { "{\"actions\": " + "[" + "\"comment\"" + - "]}" - ); + "]}"); } @Test @@ -132,8 +127,7 @@ public class IssueActionsWriterTest { MockUserSession.set(); testActions(issue, - "{\"actions\": []}" - ); + "{\"actions\": []}"); } @Test @@ -151,8 +145,7 @@ public class IssueActionsWriterTest { "{\"actions\": " + "[" + "\"comment\", \"assign\", \"plan\"\n" + - "]}" - ); + "]}"); } @Test @@ -163,14 +156,13 @@ public class IssueActionsWriterTest { .setProjectKey("sample") .setRuleKey(RuleKey.of("squid", "AvoidCycle")); - when(issueService.listTransitions(eq(issue), any(UserSession.class))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); + when(issueService.listTransitions(eq(issue))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); MockUserSession.set().setLogin("john"); testTransitions(issue, "{\"transitions\": [\n" + " \"reopen\"\n" + - " ]}" - ); + " ]}"); } @Test @@ -184,8 +176,7 @@ public class IssueActionsWriterTest { MockUserSession.set().setLogin("john"); testTransitions(issue, - "{\"transitions\": []}" - ); + "{\"transitions\": []}"); } private void testActions(Issue issue, String expected) throws JSONException { diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java index 95c8ff3da4d..9325f7745c3 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueSearchActionTest.java @@ -52,7 +52,6 @@ import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.issue.ActionService; import org.sonar.server.issue.IssueService; import org.sonar.server.user.MockUserSession; -import org.sonar.server.user.UserSession; import org.sonar.server.ws.WsTester; import java.util.*; @@ -243,7 +242,7 @@ public class IssueSearchActionTest { issues.add(issue); MockUserSession.set().setLogin("john"); - when(issueService.listTransitions(eq(issue), any(UserSession.class))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); + when(issueService.listTransitions(eq(issue))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); result.addActionPlans(newArrayList((ActionPlan) new DefaultActionPlan().setKey("AP-ABCD").setName("1.0"))); diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java index 2871fa8cd7c..66a73144b72 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/IssueShowActionTest.java @@ -53,7 +53,6 @@ import org.sonar.server.issue.IssueChangelog; import org.sonar.server.issue.IssueChangelogService; import org.sonar.server.issue.IssueService; import org.sonar.server.user.MockUserSession; -import org.sonar.server.user.UserSession; import org.sonar.server.ws.WsTester; import java.util.ArrayList; @@ -431,7 +430,7 @@ public class IssueShowActionTest { .setResolution("FIXED"); issues.add(issue); - when(issueService.listTransitions(eq(issue), any(UserSession.class))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); + when(issueService.listTransitions(eq(issue))).thenReturn(newArrayList(Transition.create("reopen", "RESOLVED", "REOPEN"))); MockUserSession.set().setLogin("john"); WsTester.TestRequest request = tester.newGetRequest("api/issues", "show").setParam("key", issue.key()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleTesting.java b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleTesting.java index d86c17e1ff8..6cfd273bce4 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/rule/RuleTesting.java +++ b/server/sonar-server/src/test/java/org/sonar/server/rule/RuleTesting.java @@ -97,6 +97,7 @@ public class RuleTesting { public static RuleDto newManualRule(String manualKey){ return new RuleDto().setRuleKey(manualKey) + .setName("Name " + manualKey) .setRepositoryKey("manual") .setDescription("Description " + manualKey) .setStatus(RuleStatus.READY); diff --git a/server/sonar-server/src/test/java/org/sonar/server/search/IndexSynchronizerMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/search/IndexSynchronizerMediumTest.java index 8048b57beca..1e313236343 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/search/IndexSynchronizerMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/search/IndexSynchronizerMediumTest.java @@ -23,6 +23,7 @@ import org.junit.After; import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; +import org.sonar.api.config.Settings; import org.sonar.api.rule.RuleKey; import org.sonar.core.persistence.DbSession; import org.sonar.server.db.DbClient; @@ -36,7 +37,8 @@ import static org.fest.assertions.Assertions.assertThat; public class IndexSynchronizerMediumTest { @ClassRule - public static ServerTester tester = new ServerTester(); + public static ServerTester tester = new ServerTester() + .setProperty("sonar.issues.use_es_backend", "true"); IndexSynchronizer synchronizer; DbClient dbClient; @@ -50,7 +52,7 @@ public class IndexSynchronizerMediumTest { indexClient = tester.get(IndexClient.class); platform = tester.get(Platform.class); dbSession = dbClient.openSession(false); - synchronizer = new IndexSynchronizer(dbClient, indexClient); + synchronizer = new IndexSynchronizer(dbClient, indexClient, tester.get(Settings.class)); tester.clearDbAndIndexes(); } diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/get_by_key.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/get_by_key.xml new file mode 100644 index 00000000000..9a5cc947917 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/get_by_key.xml @@ -0,0 +1,28 @@ + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/insert-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/insert-result.xml new file mode 100644 index 00000000000..3d736381a9e --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/insert-result.xml @@ -0,0 +1,28 @@ + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/should_select_all.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/some_issues.xml similarity index 100% rename from server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/should_select_all.xml rename to server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/some_issues.xml diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update-result.xml new file mode 100644 index 00000000000..a923dfa54e0 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update-result.xml @@ -0,0 +1,28 @@ + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update.xml new file mode 100644 index 00000000000..4c6f701dc2c --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/update.xml @@ -0,0 +1,28 @@ + + + diff --git a/sonar-core/src/main/java/org/sonar/core/issue/IssueNotifications.java b/sonar-core/src/main/java/org/sonar/core/issue/IssueNotifications.java index 39b56b1644d..44262e5dc5d 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/IssueNotifications.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/IssueNotifications.java @@ -37,6 +37,7 @@ import org.sonar.api.utils.DateUtils; import javax.annotation.CheckForNull; import javax.annotation.Nullable; + import java.io.Serializable; import java.util.List; import java.util.Map; @@ -71,7 +72,14 @@ public class IssueNotifications implements BatchComponent, ServerComponent { public List sendChanges(DefaultIssue issue, IssueChangeContext context, IssueQueryResult queryResult) { Map issues = Maps.newHashMap(); issues.put(issue, queryResult.rule(issue)); - return sendChanges(issues, context, queryResult.project(issue), queryResult.component(issue)); + return sendChanges(issue, context, queryResult.rule(issue), queryResult.project(issue), queryResult.component(issue)); + } + + @CheckForNull + public List sendChanges(DefaultIssue issue, IssueChangeContext context, Rule rule, Component project, @Nullable Component component) { + Map issues = Maps.newHashMap(); + issues.put(issue, rule); + return sendChanges(issues, context, project, component); } @CheckForNull @@ -131,6 +139,7 @@ public class IssueNotifications implements BatchComponent, ServerComponent { return notification; } + @CheckForNull private String ruleName(@Nullable Rule rule) { // this code should definitely be shared in api if (rule == null) { diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java index 63278903bf2..fa67f6872a9 100644 --- a/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/IssueDto.java @@ -136,7 +136,7 @@ public final class IssueDto extends Dto implements Serializable { } /** - * @deprecated please use setRootComponent; + * @deprecated please use setRootComponent */ @Deprecated public IssueDto setRootComponentId(Long rootComponentId) { @@ -349,6 +349,10 @@ public final class IssueDto extends Dto implements Serializable { return ruleRepo; } + public RuleKey getRuleKey(){ + return RuleKey.of(ruleRepo, ruleKey); + } + public String getComponentKey() { return componentKey; } @@ -369,6 +373,8 @@ public final class IssueDto extends Dto implements Serializable { /** * Should only be used to persist in E/S + * + * Please use {@link #setRule(org.sonar.core.rule.RuleDto)} instead */ public IssueDto setRuleKey(String repo, String rule) { this.ruleRepo = repo; @@ -378,6 +384,8 @@ public final class IssueDto extends Dto implements Serializable { /** * Should only be used to persist in E/S + * + * Please use {@link #setComponent(org.sonar.core.component.ComponentDto)} instead */ public IssueDto setComponentKey(String componentKey) { this.componentKey = componentKey; @@ -386,6 +394,8 @@ public final class IssueDto extends Dto implements Serializable { /** * Should only be used to persist in E/S + * + * Please use {@link #setRootComponent(org.sonar.core.component.ComponentDto)} instead */ public IssueDto setRootComponentKey(String rootComponentKey) { this.rootComponentKey = rootComponentKey; @@ -474,7 +484,7 @@ public final class IssueDto extends Dto implements Serializable { issue.setComponentId(componentId); issue.setProjectKey(rootComponentKey); issue.setManualSeverity(manualSeverity); - issue.setRuleKey(RuleKey.of(ruleRepo, ruleKey)); + issue.setRuleKey(getRuleKey()); issue.setActionPlanKey(actionPlanKey); issue.setAuthorLogin(authorLogin); issue.setNew(false); diff --git a/sonar-core/src/main/java/org/sonar/core/permission/PermissionFacade.java b/sonar-core/src/main/java/org/sonar/core/permission/PermissionFacade.java index 6bf273c5176..7fb5c14598d 100644 --- a/sonar-core/src/main/java/org/sonar/core/permission/PermissionFacade.java +++ b/sonar-core/src/main/java/org/sonar/core/permission/PermissionFacade.java @@ -42,6 +42,8 @@ import java.util.List; * This facade wraps db operations related to permissions * * Should be removed when batch will no more create permission, and be replaced by a new PermissionService in module server (probably be a merge with InternalPermissionService) + * + * WARNING, this class is called by Views to apply default permission template on new views */ public class PermissionFacade implements TaskComponent, ServerComponent { diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQueryResult.java b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQueryResult.java index b327c37d4f3..37ea218345f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQueryResult.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/issue/IssueQueryResult.java @@ -31,7 +31,9 @@ import java.util.List; /** * @since 3.6 + * @deprecated since 5.0 */ +@Deprecated public interface IssueQueryResult { /** * Non-null paginated list of issues. -- 2.39.5