From b978d17cc73d70e4a38baf902fb0be401fcf3004 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Mon, 19 Jan 2015 16:57:17 +0100 Subject: [PATCH] SONAR-6012 Create WS /batch/issues --- .../java/org/sonar/server/batch/BatchWs.java | 5 +- .../server/batch/GlobalRepositoryAction.java | 2 +- .../org/sonar/server/batch/IssuesAction.java | 127 ++++++++++++++ .../server/batch/ProjectRepositoryLoader.java | 4 +- .../org/sonar/server/issue/db/IssueDao.java | 9 + .../sonar/server/issue/index/IssueDoc.java | 81 +++++---- .../sonar/server/issue/index/IssueIndex.java | 24 +-- .../server/platform/ServerComponents.java | 2 + .../org/sonar/server/batch/BatchWsTest.java | 3 +- .../batch/GlobalRepositoryActionTest.java | 6 +- .../sonar/server/batch/IssuesActionTest.java | 125 ++++++++++++++ .../batch/ProjectRepositoryActionTest.java | 6 +- .../ProjectRepositoryLoaderMediumTest.java | 36 +++- .../java/org/sonar/server/es/EsTester.java | 8 +- .../sonar/server/issue/db/IssueDaoTest.java | 67 +++++++- .../issue/filter/IssueFilterServiceTest.java | 4 +- ...InternalPermissionTemplateServiceTest.java | 15 +- .../issues_on_module-expected.json | 16 ++ .../issues_on_project-expected.json | 16 ++ .../server/batch/IssuesActionTest/shared.xml | 40 +++++ ...elect_non_closed_issues_by_module_uuid.xml | 159 ++++++++++++++++++ ...lect_non_closed_issues_by_project_uuid.xml | 159 ++++++++++++++++++ .../server/issue/db/IssueDaoTest/shared.xml | 19 ++- .../input/issues/PreviousIssueHelper.java | 32 ++-- .../input/issues/PreviousIssueHelperTest.java | 25 ++- .../sonar/core/component/ComponentDto.java | 5 + .../sonar/core/issue/db/BatchIssueDto.java | 135 +++++++++++++++ .../core/permission/GlobalPermissions.java | 4 +- .../org/sonar/core/persistence/MyBatis.java | 76 ++------- .../org/sonar/core/issue/db/IssueMapper.xml | 44 ++++- .../java/org/sonar/core/user/RoleDaoTest.java | 8 +- 31 files changed, 1078 insertions(+), 184 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/issues_on_module-expected.json create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/issues_on_project-expected.json create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/shared.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_module_uuid.xml create mode 100644 server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_project_uuid.xml create mode 100644 sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/BatchWs.java b/server/sonar-server/src/main/java/org/sonar/server/batch/BatchWs.java index defb80f84d1..fdddd299e55 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/batch/BatchWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/batch/BatchWs.java @@ -35,11 +35,13 @@ public class BatchWs implements WebService { private final BatchIndex batchIndex; private final GlobalRepositoryAction globalRepositoryAction; private final ProjectRepositoryAction projectRepositoryAction; + private final IssuesAction issuesAction; - public BatchWs(BatchIndex batchIndex, GlobalRepositoryAction globalRepositoryAction, ProjectRepositoryAction projectRepositoryAction) { + public BatchWs(BatchIndex batchIndex, GlobalRepositoryAction globalRepositoryAction, ProjectRepositoryAction projectRepositoryAction, IssuesAction issuesAction) { this.batchIndex = batchIndex; this.globalRepositoryAction = globalRepositoryAction; this.projectRepositoryAction = projectRepositoryAction; + this.issuesAction = issuesAction; } @Override @@ -52,6 +54,7 @@ public class BatchWs implements WebService { defineFileAction(controller); globalRepositoryAction.define(controller); projectRepositoryAction.define(controller); + issuesAction.define(controller); controller.done(); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/GlobalRepositoryAction.java b/server/sonar-server/src/main/java/org/sonar/server/batch/GlobalRepositoryAction.java index 59451e4c2a0..b939ac22566 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/batch/GlobalRepositoryAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/batch/GlobalRepositoryAction.java @@ -58,7 +58,7 @@ public class GlobalRepositoryAction implements RequestHandler { public void handle(Request request, Response response) throws Exception { UserSession userSession = UserSession.get(); boolean hasScanPerm = userSession.hasGlobalPermission(GlobalPermissions.SCAN_EXECUTION); - boolean hasDryRunPerm = userSession.hasGlobalPermission(GlobalPermissions.DRY_RUN_EXECUTION); + boolean hasDryRunPerm = userSession.hasGlobalPermission(GlobalPermissions.PREVIEW_EXECUTION); DbSession session = dbClient.openSession(false); try { diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java b/server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java new file mode 100644 index 00000000000..cc4cfcce10b --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/batch/IssuesAction.java @@ -0,0 +1,127 @@ +/* + * 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.batch; + +import com.google.common.base.Charsets; +import org.apache.ibatis.session.ResultContext; +import org.apache.ibatis.session.ResultHandler; +import org.sonar.api.server.ws.Request; +import org.sonar.api.server.ws.RequestHandler; +import org.sonar.api.server.ws.Response; +import org.sonar.api.server.ws.WebService; +import org.sonar.api.web.UserRole; +import org.sonar.batch.protocol.input.issues.PreviousIssue; +import org.sonar.batch.protocol.input.issues.PreviousIssueHelper; +import org.sonar.core.component.ComponentDto; +import org.sonar.core.issue.db.BatchIssueDto; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.MyBatis; +import org.sonar.server.db.DbClient; +import org.sonar.server.plugins.MimeTypes; +import org.sonar.server.user.UserSession; + +import javax.annotation.Nullable; + +import java.io.OutputStreamWriter; + +public class IssuesAction implements RequestHandler { + + private static final String PARAM_KEY = "key"; + + private final DbClient dbClient; + + public IssuesAction(DbClient dbClient) { + this.dbClient = dbClient; + } + + void define(WebService.NewController controller) { + WebService.NewAction action = controller.createAction("issues") + .setDescription("Return open issues") + .setSince("5.1") + .setInternal(true) + .setHandler(this); + + action + .createParam(PARAM_KEY) + .setRequired(true) + .setDescription("Project or module key") + .setExampleValue("org.codehaus.sonar:sonar"); + } + + @Override + public void handle(Request request, Response response) throws Exception { + UserSession.get().checkGlobalPermission(GlobalPermissions.PREVIEW_EXECUTION); + final String moduleKey = request.mandatoryParam(PARAM_KEY); + + PreviousIssueHelper previousIssueHelper = PreviousIssueHelper.create(new OutputStreamWriter(response.stream().output(), Charsets.UTF_8)); + DbSession session = dbClient.openSession(false); + try { + ComponentDto moduleOrProject = dbClient.componentDao().getByKey(session, moduleKey); + UserSession.get().checkComponentPermission(UserRole.USER, moduleKey); + + response.stream().setMediaType(MimeTypes.JSON); + BatchIssueResultHandler batchIssueResultHandler = new BatchIssueResultHandler(previousIssueHelper); + if (moduleOrProject.isRootProject()) { + dbClient.issueDao().selectNonClosedIssuesByProjectUuid(session, moduleOrProject.uuid(), batchIssueResultHandler); + } else { + dbClient.issueDao().selectNonClosedIssuesByModuleUuid(session, moduleOrProject.uuid(), batchIssueResultHandler); + } + + } finally { + previousIssueHelper.close(); + MyBatis.closeQuietly(session); + } + } + + private static class BatchIssueResultHandler implements ResultHandler { + private final PreviousIssueHelper previousIssueHelper; + + public BatchIssueResultHandler(PreviousIssueHelper previousIssueHelper) { + this.previousIssueHelper = previousIssueHelper; + } + + @Override + public void handleResult(ResultContext rc) { + previousIssueHelper.addIssue((BatchIssueDto) rc.getResultObject(), new BatchIssueFunction()); + } + } + + private static class BatchIssueFunction implements PreviousIssueHelper.Function { + @Override + public PreviousIssue apply(@Nullable BatchIssueDto batchIssueDto) { + if (batchIssueDto != null) { + return new PreviousIssue() + .setKey(batchIssueDto.getKey()) + .setComponentPath(batchIssueDto.getComponentPath()) + .setChecksum(batchIssueDto.getChecksum()) + .setAssigneeLogin(batchIssueDto.getAssigneeLogin()) + .setAssigneeFullname(batchIssueDto.getAssigneeName()) + .setLine(batchIssueDto.getLine()) + .setRuleKey(batchIssueDto.getRuleRepo(), batchIssueDto.getRuleKey()) + .setMessage(batchIssueDto.getMessage()) + .setResolution(batchIssueDto.getResolution()) + .setStatus(batchIssueDto.getStatus()); + } + return null; + } + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectRepositoryLoader.java b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectRepositoryLoader.java index 90c10f94b5a..61917a48193 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectRepositoryLoader.java +++ b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectRepositoryLoader.java @@ -135,7 +135,7 @@ public class ProjectRepositoryLoader implements ServerComponent { } private void addSettingsToChildrenModules(ProjectReferentials ref, String moduleKey, Map parentProperties, TreeModuleSettings treeModuleSettings, - boolean hasScanPerm, DbSession session) { + boolean hasScanPerm, DbSession session) { Map currentParentProperties = newHashMap(); currentParentProperties.putAll(parentProperties); currentParentProperties.putAll(getPropertiesMap(treeModuleSettings.findModuleSettings(moduleKey), hasScanPerm)); @@ -235,7 +235,7 @@ public class ProjectRepositoryLoader implements ServerComponent { private void checkPermission(boolean preview) { UserSession userSession = UserSession.get(); boolean hasScanPerm = userSession.hasGlobalPermission(GlobalPermissions.SCAN_EXECUTION); - boolean hasPreviewPerm = userSession.hasGlobalPermission(GlobalPermissions.DRY_RUN_EXECUTION); + boolean hasPreviewPerm = userSession.hasGlobalPermission(GlobalPermissions.PREVIEW_EXECUTION); if (!hasPreviewPerm && !hasScanPerm) { throw new ForbiddenException("You're not authorized to execute any SonarQube analysis. Please contact your SonarQube administrator."); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/db/IssueDao.java b/server/sonar-server/src/main/java/org/sonar/server/issue/db/IssueDao.java index 0b2aba8a5a8..f8bd5f91d53 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/db/IssueDao.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/db/IssueDao.java @@ -19,6 +19,7 @@ */ package org.sonar.server.issue.db; +import org.apache.ibatis.session.ResultHandler; import org.sonar.core.issue.db.IssueDto; import org.sonar.core.issue.db.IssueMapper; import org.sonar.core.persistence.DaoComponent; @@ -58,6 +59,14 @@ public class IssueDao extends org.sonar.core.issue.db.IssueDao implements DaoCom return mapper(session).selectByKeys(keys); } + public void selectNonClosedIssuesByModuleUuid(DbSession session, String moduleUuid, ResultHandler handler) { + session.select("org.sonar.core.issue.db.IssueMapper.selectNonClosedIssuesByModuleUuid", moduleUuid, handler); + } + + public void selectNonClosedIssuesByProjectUuid(DbSession session, String projectUuid, ResultHandler handler) { + session.select("org.sonar.core.issue.db.IssueMapper.selectNonClosedIssuesByProjectUuid", projectUuid, handler); + } + public void insert(DbSession session, IssueDto dto) { mapper(session).insert(dto); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueDoc.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueDoc.java index c156bf24d2c..dbda8319ab7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueDoc.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueDoc.java @@ -30,11 +30,7 @@ import org.sonar.server.search.BaseDoc; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.List; -import java.util.Map; +import java.util.*; public class IssueDoc extends BaseDoc implements Issue { @@ -199,101 +195,125 @@ public class IssueDoc extends BaseDoc implements Issue { return getNullableField(IssueIndexDefinition.FIELD_ISSUE_FILE_PATH); } - public void setKey(@Nullable String s) { + public IssueDoc setKey(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_KEY, s); + return this; } - public void setComponentUuid(@Nullable String s) { + public IssueDoc setComponentUuid(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_COMPONENT_UUID, s); + return this; } - public void setModuleUuid(@Nullable String s) { + public IssueDoc setModuleUuid(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_MODULE_UUID, s); + return this; } - public void setProjectUuid(@Nullable String s) { + public IssueDoc setProjectUuid(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_PROJECT_UUID, s); + return this; } - public void setRuleKey(@Nullable String s) { + public IssueDoc setRuleKey(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_RULE_KEY, s); + return this; } - public void setLanguage(@Nullable String s) { + public IssueDoc setLanguage(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_LANGUAGE, s); + return this; } - public void setSeverity(@Nullable String s) { + public IssueDoc setSeverity(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_SEVERITY, s); setField(IssueIndexDefinition.FIELD_ISSUE_SEVERITY_VALUE, Severity.ALL.indexOf(s)); + return this; } - public void setMessage(@Nullable String s) { + public IssueDoc setMessage(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_MESSAGE, s); + return this; } - public void setLine(@Nullable Integer i) { + public IssueDoc setLine(@Nullable Integer i) { setField(IssueIndexDefinition.FIELD_ISSUE_LINE, i); + return this; } - public void setEffortToFix(@Nullable Double d) { + public IssueDoc setEffortToFix(@Nullable Double d) { setField(IssueIndexDefinition.FIELD_ISSUE_EFFORT, d); + return this; } - public void setStatus(@Nullable String s) { + public IssueDoc setStatus(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_STATUS, s); + return this; } - public void setResolution(@Nullable String s) { + public IssueDoc setResolution(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_RESOLUTION, s); + return this; } - public void setReporter(@Nullable String s) { + public IssueDoc setReporter(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_REPORTER, s); + return this; } - public void setAssignee(@Nullable String s) { + public IssueDoc setAssignee(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_ASSIGNEE, s); + return this; } - public void setFuncUpdateDate(@Nullable Date d) { + public IssueDoc setFuncUpdateDate(@Nullable Date d) { setField(IssueIndexDefinition.FIELD_ISSUE_FUNC_UPDATED_AT, d); + return this; } - public void setFuncCreationDate(@Nullable Date d) { + public IssueDoc setFuncCreationDate(@Nullable Date d) { setField(IssueIndexDefinition.FIELD_ISSUE_FUNC_CREATED_AT, d); + return this; } - public void setTechnicalUpdateDate(@Nullable Date d) { + public IssueDoc setTechnicalUpdateDate(@Nullable Date d) { setField(IssueIndexDefinition.FIELD_ISSUE_TECHNICAL_UPDATED_AT, d); + return this; } - public void setFuncCloseDate(@Nullable Date d) { + public IssueDoc setFuncCloseDate(@Nullable Date d) { setField(IssueIndexDefinition.FIELD_ISSUE_FUNC_CLOSED_AT, d); + return this; } - public void setAttributes(@Nullable String s) { + public IssueDoc setAttributes(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_ATTRIBUTES, s); + return this; } - public void setAuthorLogin(@Nullable String s) { + public IssueDoc setAuthorLogin(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_AUTHOR_LOGIN, s); + return this; } - public void setActionPlanKey(@Nullable String s) { + public IssueDoc setActionPlanKey(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_ACTION_PLAN, s); + return this; } - public void setDebt(@Nullable Long l) { + public IssueDoc setDebt(@Nullable Long l) { setField(IssueIndexDefinition.FIELD_ISSUE_DEBT, l); + return this; } - public void setFilePath(@Nullable String s) { + public IssueDoc setFilePath(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_FILE_PATH, s); + return this; } - public void setModuleUuidPath(@Nullable String s) { + public IssueDoc setModuleUuidPath(@Nullable String s) { setField(IssueIndexDefinition.FIELD_ISSUE_MODULE_PATH, s); + return this; } @Override @@ -302,7 +322,8 @@ public class IssueDoc extends BaseDoc implements Issue { return getNullableField(IssueIndexDefinition.FIELD_ISSUE_TAGS); } - public void setTags(@Nullable Collection tags) { + public IssueDoc setTags(@Nullable Collection tags) { setField(IssueIndexDefinition.FIELD_ISSUE_TAGS, tags); + return this; } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java index 9d055d022ed..396ad9010be 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java +++ b/server/sonar-server/src/main/java/org/sonar/server/issue/index/IssueIndex.java @@ -30,12 +30,7 @@ import org.elasticsearch.action.search.SearchRequestBuilder; import org.elasticsearch.action.search.SearchResponse; import org.elasticsearch.action.search.SearchType; import org.elasticsearch.common.unit.TimeValue; -import org.elasticsearch.index.query.BoolFilterBuilder; -import org.elasticsearch.index.query.FilterBuilder; -import org.elasticsearch.index.query.FilterBuilders; -import org.elasticsearch.index.query.OrFilterBuilder; -import org.elasticsearch.index.query.QueryBuilder; -import org.elasticsearch.index.query.QueryBuilders; +import org.elasticsearch.index.query.*; import org.elasticsearch.search.aggregations.AggregationBuilder; import org.elasticsearch.search.aggregations.AggregationBuilders; import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder; @@ -49,24 +44,13 @@ import org.sonar.api.rule.Severity; import org.sonar.server.es.Sorting; import org.sonar.server.issue.IssueQuery; import org.sonar.server.issue.filter.IssueFilterParameters; -import org.sonar.server.search.BaseIndex; -import org.sonar.server.search.FacetValue; -import org.sonar.server.search.IndexDefinition; -import org.sonar.server.search.QueryContext; -import org.sonar.server.search.Result; -import org.sonar.server.search.SearchClient; -import org.sonar.server.search.StickyFacetBuilder; +import org.sonar.server.search.*; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; import javax.annotation.Nullable; -import java.util.Arrays; -import java.util.Collection; -import java.util.Date; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; import static com.google.common.collect.Lists.newArrayList; @@ -528,7 +512,7 @@ public class IssueIndex extends BaseIndex { Terms result = count.addAggregation(aggreg).get().getAggregations().get("_ref"); Map map = Maps.newHashMap(); - for (Bucket bucket: result.getBuckets()) { + for (Bucket bucket : result.getBuckets()) { map.put(bucket.getKey(), bucket.getDocCount()); } return map; 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 77457db98cb..ef987e1575d 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 @@ -352,6 +352,8 @@ class ServerComponents { pico.addSingleton(ProjectRepositoryAction.class); pico.addSingleton(ProjectRepositoryLoader.class); pico.addSingleton(SubmitReportWsAction.class); + pico.addSingleton(IssuesSearchService.class); + pico.addSingleton(IssuesAction.class); pico.addSingleton(BatchWs.class); // update center diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsTest.java index 58b5fcf6a0d..539d2cff045 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/batch/BatchWsTest.java @@ -57,7 +57,8 @@ public class BatchWsTest { public void before() throws IOException { tester = new WsTester(new BatchWs(batchIndex, new GlobalRepositoryAction(mock(DbClient.class), mock(PropertiesDao.class)), - new ProjectRepositoryAction(mock(ProjectRepositoryLoader.class)))); + new ProjectRepositoryAction(mock(ProjectRepositoryLoader.class)), + new IssuesAction(mock(DbClient.class)))); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/GlobalRepositoryActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/GlobalRepositoryActionTest.java index 8ec5ca9145e..fdce8473e1e 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/batch/GlobalRepositoryActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/batch/GlobalRepositoryActionTest.java @@ -59,7 +59,7 @@ public class GlobalRepositoryActionTest { when(dbClient.openSession(false)).thenReturn(session); when(dbClient.metricDao()).thenReturn(metricDao); - tester = new WsTester(new BatchWs(mock(BatchIndex.class), new GlobalRepositoryAction(dbClient, propertiesDao), mock(ProjectRepositoryAction.class))); + tester = new WsTester(new BatchWs(mock(BatchIndex.class), new GlobalRepositoryAction(dbClient, propertiesDao), mock(ProjectRepositoryAction.class), mock(IssuesAction.class))); } @Test @@ -75,7 +75,7 @@ public class GlobalRepositoryActionTest { @Test public void return_global_settings() throws Exception { - MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.DRY_RUN_EXECUTION); + MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION, GlobalPermissions.PREVIEW_EXECUTION); when(propertiesDao.selectGlobalProperties(session)).thenReturn(newArrayList( new PropertyDto().setKey("foo").setValue("bar"), @@ -89,7 +89,7 @@ public class GlobalRepositoryActionTest { @Test public void return_only_license_settings_without_scan_but_with_preview_permission() throws Exception { - MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.DRY_RUN_EXECUTION); + MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.PREVIEW_EXECUTION); when(propertiesDao.selectGlobalProperties(session)).thenReturn(newArrayList( new PropertyDto().setKey("foo").setValue("bar"), diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java new file mode 100644 index 00000000000..ca142cd4827 --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/batch/IssuesActionTest.java @@ -0,0 +1,125 @@ +/* + * 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.batch; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.platform.Server; +import org.sonar.api.web.UserRole; +import org.sonar.core.permission.GlobalPermissions; +import org.sonar.core.persistence.DbSession; +import org.sonar.core.persistence.DbTester; +import org.sonar.core.properties.PropertiesDao; +import org.sonar.server.component.db.ComponentDao; +import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.issue.db.IssueDao; +import org.sonar.server.user.MockUserSession; +import org.sonar.server.ws.WsTester; + +import static org.mockito.Mockito.mock; + +public class IssuesActionTest { + + private final static String PROJECT_KEY = "struts"; + private final static String MODULE_KEY = "struts-core"; + + WsTester tester; + + IssuesAction issuesAction; + + @Rule + public DbTester db = new DbTester(); + + private DbSession session; + + @Before + public void before() throws Exception { + this.session = db.myBatis().openSession(false); + + DbClient dbClient = new DbClient(db.database(), db.myBatis(), new IssueDao(db.myBatis()), new ComponentDao()); + issuesAction = new IssuesAction(dbClient); + + tester = new WsTester(new BatchWs( + new BatchIndex(mock(Server.class)), + new GlobalRepositoryAction(mock(DbClient.class), mock(PropertiesDao.class)), + new ProjectRepositoryAction(mock(ProjectRepositoryLoader.class)), + issuesAction) + ); + } + + @After + public void after() { + this.session.close(); + } + + @Test + public void return_issues_on_project() throws Exception { + db.prepareDbUnit(getClass(), "shared.xml"); + + MockUserSession.set().setLogin("henry").setGlobalPermissions(GlobalPermissions.PREVIEW_EXECUTION).addComponentPermission(UserRole.USER, PROJECT_KEY, PROJECT_KEY); + + WsTester.TestRequest request = tester.newGetRequest("batch", "issues").setParam("key", PROJECT_KEY); + request.execute().assertJson(getClass(), "issues_on_project-expected.json"); + } + + @Test + public void return_issues_on_module() throws Exception { + db.prepareDbUnit(getClass(), "shared.xml"); + + MockUserSession.set().setLogin("henry").setGlobalPermissions(GlobalPermissions.PREVIEW_EXECUTION).addComponentPermission(UserRole.USER, PROJECT_KEY, MODULE_KEY); + + WsTester.TestRequest request = tester.newGetRequest("batch", "issues").setParam("key", MODULE_KEY); + request.execute().assertJson(getClass(), "issues_on_module-expected.json"); + } + + @Test(expected = ForbiddenException.class) + public void fail_without_preview_permission() throws Exception { + db.prepareDbUnit(getClass(), "shared.xml"); + + MockUserSession.set().setLogin("henry").setGlobalPermissions(GlobalPermissions.PROVISIONING).addComponentPermission(UserRole.USER, PROJECT_KEY, MODULE_KEY); + + WsTester.TestRequest request = tester.newGetRequest("batch", "issues").setParam("key", MODULE_KEY); + request.execute(); + } + + @Test(expected = ForbiddenException.class) + public void fail_without_user_permission_on_project() throws Exception { + db.prepareDbUnit(getClass(), "shared.xml"); + + MockUserSession.set().setLogin("henry").setGlobalPermissions(GlobalPermissions.PREVIEW_EXECUTION).addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, PROJECT_KEY); + + WsTester.TestRequest request = tester.newGetRequest("batch", "issues").setParam("key", MODULE_KEY); + request.execute(); + } + + @Test(expected = ForbiddenException.class) + public void fail_without_user_permission_on_module() throws Exception { + db.prepareDbUnit(getClass(), "shared.xml"); + + MockUserSession.set().setLogin("henry").setGlobalPermissions(GlobalPermissions.PREVIEW_EXECUTION).addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, MODULE_KEY); + + WsTester.TestRequest request = tester.newGetRequest("batch", "issues").setParam("key", MODULE_KEY); + request.execute(); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryActionTest.java index 53183b4ff5b..4dd4ae8e46f 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryActionTest.java @@ -37,14 +37,14 @@ import static org.mockito.Mockito.when; public class ProjectRepositoryActionTest { @Mock - ProjectRepositoryLoader projectReferentialsLoader; + ProjectRepositoryLoader projectRepositoryLoader; WsTester tester; @Before public void setUp() throws Exception { tester = new WsTester(new BatchWs(mock(BatchIndex.class), mock(GlobalRepositoryAction.class), - new ProjectRepositoryAction(projectReferentialsLoader))); + new ProjectRepositoryAction(projectRepositoryLoader), mock(IssuesAction.class))); } @Test @@ -55,7 +55,7 @@ public class ProjectRepositoryActionTest { when(projectReferentials.toJson()).thenReturn("{\"settingsByModule\": {}}"); ArgumentCaptor queryArgumentCaptor = ArgumentCaptor.forClass(ProjectRepositoryQuery.class); - when(projectReferentialsLoader.load(queryArgumentCaptor.capture())).thenReturn(projectReferentials); + when(projectRepositoryLoader.load(queryArgumentCaptor.capture())).thenReturn(projectReferentials); WsTester.TestRequest request = tester.newGetRequest("batch", "project") .setParam("key", projectKey) diff --git a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java index 7e5b2f6cd1f..bcc53133743 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/batch/ProjectRepositoryLoaderMediumTest.java @@ -111,7 +111,7 @@ public class ProjectRepositoryLoaderMediumTest { @Test public void not_returned_secured_settings_with_only_preview_permission() throws Exception { - MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.DRY_RUN_EXECUTION); + MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.PREVIEW_EXECUTION); ComponentDto project = ComponentTesting.newProjectDto(); tester.get(DbClient.class).componentDao().insert(dbSession, project); @@ -659,7 +659,7 @@ public class ProjectRepositoryLoaderMediumTest { @Test public void fail_when_not_preview_and_only_dry_run_permission() throws Exception { - MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.DRY_RUN_EXECUTION); + MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.PREVIEW_EXECUTION); ComponentDto project = ComponentTesting.newProjectDto(); tester.get(DbClient.class).componentDao().insert(dbSession, project); @@ -676,7 +676,7 @@ public class ProjectRepositoryLoaderMediumTest { } @Test - public void add_file_data_on_single_project() throws Exception { + public void return_file_data_from_single_project() throws Exception { MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); ComponentDto project = ComponentTesting.newProjectDto(); @@ -696,7 +696,7 @@ public class ProjectRepositoryLoaderMediumTest { } @Test - public void add_file_data_on_multi_modules() throws Exception { + public void return_file_data_from_multi_modules() throws Exception { MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); ComponentDto project = ComponentTesting.newProjectDto(); @@ -723,6 +723,34 @@ public class ProjectRepositoryLoaderMediumTest { assertThat(ref.fileData(module.key(), moduleFile.path()).hash()).isEqualTo("789456"); } + @Test + public void return_file_data_from_module() throws Exception { + MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.SCAN_EXECUTION); + + ComponentDto project = ComponentTesting.newProjectDto(); + tester.get(DbClient.class).componentDao().insert(dbSession, project); + addDefaultProfile(); + + // File on project + ComponentDto projectFile = ComponentTesting.newFileDto(project, "projectFile"); + tester.get(DbClient.class).componentDao().insert(dbSession, projectFile); + tester.get(FileSourceDao.class).insert(newFileSourceDto(projectFile).setSrcHash("123456")); + + ComponentDto module = ComponentTesting.newModuleDto(project); + tester.get(DbClient.class).componentDao().insert(dbSession, module); + + // File on module + ComponentDto moduleFile = ComponentTesting.newFileDto(module, "moduleFile"); + tester.get(DbClient.class).componentDao().insert(dbSession, moduleFile); + tester.get(FileSourceDao.class).insert(newFileSourceDto(moduleFile).setSrcHash("789456")); + + dbSession.commit(); + + ProjectReferentials ref = loader.load(ProjectRepositoryQuery.create().setModuleKey(module.key())); + assertThat(ref.fileData(module.key(), moduleFile.path()).hash()).isEqualTo("789456"); + assertThat(ref.fileData(project.key(), projectFile.path())).isNull(); + } + private void addDefaultProfile() { QualityProfileDto profileDto = QProfileTesting.newDto(QProfileName.createFor(ServerTester.Xoo.KEY, "SonarQube way"), "abcd").setRulesUpdatedAt( DateUtils.formatDateTime(new Date())); diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java b/server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java index 2cc58e8f6a4..1efc1df9d8a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java +++ b/server/sonar-server/src/test/java/org/sonar/server/es/EsTester.java @@ -48,6 +48,7 @@ import org.sonar.core.profiling.Profiling; import org.sonar.server.search.BaseDoc; import org.sonar.test.TestUtils; +import java.io.File; import java.io.FileInputStream; import java.util.Collections; import java.util.List; @@ -147,8 +148,11 @@ public class EsTester extends ExternalResource { public void putDocuments(String index, String type, Class testClass, String... jsonPaths) throws Exception { BulkRequestBuilder bulk = client.prepareBulk().setRefresh(true); for (String path : jsonPaths) { - bulk.add(new IndexRequest(index, type).source(IOUtils.toString( - new FileInputStream(TestUtils.getResource(testClass, path))))); + File file = TestUtils.getResource(testClass, path); + if (file == null) { + throw new IllegalArgumentException(String.format("File '%s' hasn't been found in folder '%s'", path, testClass.getSimpleName())); + } + bulk.add(new IndexRequest(index, type).source(IOUtils.toString(new FileInputStream(file)))); } bulk.get(); } 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 a42a1287b29..1a36eab8aa4 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 @@ -19,13 +19,15 @@ */ package org.sonar.server.issue.db; +import org.apache.ibatis.executor.result.DefaultResultHandler; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.sonar.api.issue.Issue; 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.BatchIssueDto; import org.sonar.core.issue.db.IssueDto; import org.sonar.core.persistence.AbstractDaoTestCase; import org.sonar.core.persistence.DbSession; @@ -35,18 +37,15 @@ import java.util.Arrays; import java.util.List; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; public class IssueDaoTest extends AbstractDaoTestCase { private IssueDao dao; private DbSession session; - private System2 system2; @Before public void before() throws Exception { this.session = getMyBatis().openSession(false); - this.system2 = mock(System2.class); this.dao = new IssueDao(getMyBatis()); } @@ -134,6 +133,62 @@ public class IssueDaoTest extends AbstractDaoTestCase { assertThat(issue.getProjectKey()).isEqualTo("struts"); } + @Test + public void select_non_closed_issues_by_module_uuid() { + setupData("shared", "select_non_closed_issues_by_module_uuid"); + + // BCDE is a non-root module, we should find 2 issues from classes and one on itself + DefaultResultHandler handler = new DefaultResultHandler(); + dao.selectNonClosedIssuesByModuleUuid(session, "BCDE", handler); + assertThat(handler.getResultList()).extracting("key").containsOnly("100", "101", "103"); + + // DBCA is a a simple project with a single file + handler = new DefaultResultHandler(); + dao.selectNonClosedIssuesByModuleUuid(session, "DBCA", handler); + assertThat(handler.getResultList()).hasSize(1); + + BatchIssueDto batchIssueDto = (BatchIssueDto) handler.getResultList().get(0); + assertThat(batchIssueDto.getKey()).isEqualTo("1000"); + assertThat(batchIssueDto.getRuleKey()).isEqualTo("AvoidCycle"); + assertThat(batchIssueDto.getRuleRepo()).isEqualTo("squid"); + assertThat(batchIssueDto.getMessage()).isEqualTo("Avoid this"); + assertThat(batchIssueDto.getLine()).isEqualTo(200); + assertThat(batchIssueDto.getResolution()).isEqualTo(Issue.RESOLUTION_FALSE_POSITIVE); + assertThat(batchIssueDto.getStatus()).isEqualTo(Issue.STATUS_RESOLVED); + assertThat(batchIssueDto.getComponentPath()).isEqualTo("src/main/java/Sample.java"); + assertThat(batchIssueDto.getChecksum()).isEqualTo("123456"); + assertThat(batchIssueDto.getAssigneeLogin()).isEqualTo("john"); + assertThat(batchIssueDto.getAssigneeName()).isEqualTo("John Doo"); + } + + @Test + public void select_non_closed_issues_by_project_uuid() { + setupData("shared", "select_non_closed_issues_by_project_uuid"); + + // ABCD is the root module, we should find all 4 issues + DefaultResultHandler handler = new DefaultResultHandler(); + dao.selectNonClosedIssuesByProjectUuid(session, "ABCD", handler); + assertThat(handler.getResultList()).hasSize(4); + + // DBCA is a a simple project with a single file + handler = new DefaultResultHandler(); + dao.selectNonClosedIssuesByProjectUuid(session, "DBCA", handler); + assertThat(handler.getResultList()).hasSize(1); + + BatchIssueDto batchIssueDto = (BatchIssueDto) handler.getResultList().get(0); + assertThat(batchIssueDto.getKey()).isEqualTo("1000"); + assertThat(batchIssueDto.getRuleKey()).isEqualTo("AvoidCycle"); + assertThat(batchIssueDto.getRuleRepo()).isEqualTo("squid"); + assertThat(batchIssueDto.getMessage()).isEqualTo("Avoid this"); + assertThat(batchIssueDto.getLine()).isEqualTo(200); + assertThat(batchIssueDto.getResolution()).isEqualTo(Issue.RESOLUTION_FALSE_POSITIVE); + assertThat(batchIssueDto.getStatus()).isEqualTo(Issue.STATUS_RESOLVED); + assertThat(batchIssueDto.getComponentPath()).isEqualTo("src/main/java/Sample.java"); + assertThat(batchIssueDto.getChecksum()).isEqualTo("123456"); + assertThat(batchIssueDto.getAssigneeLogin()).isEqualTo("john"); + assertThat(batchIssueDto.getAssigneeName()).isEqualTo("John Doo"); + } + @Test public void insert() throws Exception { IssueDto dto = new IssueDto(); @@ -164,7 +219,7 @@ public class IssueDaoTest extends AbstractDaoTestCase { dao.insert(session, dto); session.commit(); - checkTables("insert", new String[]{"id"}, "issues"); + checkTables("insert", new String[] {"id"}, "issues"); } @Test @@ -199,6 +254,6 @@ public class IssueDaoTest extends AbstractDaoTestCase { dao.update(session, dto); session.commit(); - checkTables("update", new String[]{"id"}, "issues"); + checkTables("update", new String[] {"id"}, "issues"); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterServiceTest.java index 53441e2b8c0..bc1dd5ba7f9 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/filter/IssueFilterServiceTest.java @@ -404,7 +404,7 @@ public class IssueFilterServiceTest { String currentUser = "dave.loper"; IssueFilterDto sharedFilter = new IssueFilterDto().setId(1L).setName("My filter").setUserLogin(currentUser).setShared(true); - when(authorizationDao.selectGlobalPermissions(currentUser)).thenReturn(newArrayList(GlobalPermissions.DRY_RUN_EXECUTION)); + when(authorizationDao.selectGlobalPermissions(currentUser)).thenReturn(newArrayList(GlobalPermissions.PREVIEW_EXECUTION)); when(issueFilterDao.selectById(1L)).thenReturn(sharedFilter); try { @@ -529,7 +529,7 @@ public class IssueFilterServiceTest { public void should_execute_from_issue_query() { IssueQuery issueQuery = IssueQuery.builder().build(); QueryContext queryContext = new QueryContext().setPage(2, 50); - + Result result = mock(Result.class); when(result.getHits()).thenReturn(newArrayList((Issue) new DefaultIssue())); when(result.getTotal()).thenReturn(100L); diff --git a/server/sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionTemplateServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionTemplateServiceTest.java index f6938b9172f..94bd72758c8 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionTemplateServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/permission/InternalPermissionTemplateServiceTest.java @@ -30,12 +30,7 @@ import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import org.sonar.api.web.UserRole; -import org.sonar.core.permission.GlobalPermissions; -import org.sonar.core.permission.PermissionQuery; -import org.sonar.core.permission.PermissionTemplateDao; -import org.sonar.core.permission.PermissionTemplateDto; -import org.sonar.core.permission.PermissionTemplateGroupDto; -import org.sonar.core.permission.PermissionTemplateUserDto; +import org.sonar.core.permission.*; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; import org.sonar.core.properties.PropertiesDao; @@ -160,15 +155,15 @@ public class InternalPermissionTemplateServiceTest { List usersPermissions = Lists.newArrayList( buildUserPermission("user_scan", GlobalPermissions.SCAN_EXECUTION), - buildUserPermission("user_dry_run", GlobalPermissions.DRY_RUN_EXECUTION), + buildUserPermission("user_dry_run", GlobalPermissions.PREVIEW_EXECUTION), buildUserPermission("user_scan_and_dry_run", GlobalPermissions.SCAN_EXECUTION), - buildUserPermission("user_scan_and_dry_run", GlobalPermissions.DRY_RUN_EXECUTION) + buildUserPermission("user_scan_and_dry_run", GlobalPermissions.PREVIEW_EXECUTION) ); List groupsPermissions = Lists.newArrayList( buildGroupPermission("admin_group", GlobalPermissions.SYSTEM_ADMIN), buildGroupPermission("scan_group", GlobalPermissions.SCAN_EXECUTION), - buildGroupPermission(null, GlobalPermissions.DRY_RUN_EXECUTION) + buildGroupPermission(null, GlobalPermissions.PREVIEW_EXECUTION) ); PermissionTemplateDto permissionTemplateDto = new PermissionTemplateDto() @@ -184,7 +179,7 @@ public class InternalPermissionTemplateServiceTest { assertThat(permissionTemplate.getUsersForPermission(GlobalPermissions.DASHBOARD_SHARING)).isEmpty(); assertThat(permissionTemplate.getUsersForPermission(GlobalPermissions.SCAN_EXECUTION)).extracting("userName").containsOnly("user_scan", "user_scan_and_dry_run"); - assertThat(permissionTemplate.getUsersForPermission(GlobalPermissions.DRY_RUN_EXECUTION)).extracting("userName").containsOnly("user_dry_run", "user_scan_and_dry_run"); + assertThat(permissionTemplate.getUsersForPermission(GlobalPermissions.PREVIEW_EXECUTION)).extracting("userName").containsOnly("user_dry_run", "user_scan_and_dry_run"); assertThat(permissionTemplate.getGroupsForPermission(GlobalPermissions.DASHBOARD_SHARING)).isEmpty(); assertThat(permissionTemplate.getGroupsForPermission(GlobalPermissions.SCAN_EXECUTION)).extracting("groupName").containsOnly("scan_group"); assertThat(permissionTemplate.getGroupsForPermission(GlobalPermissions.SYSTEM_ADMIN)).extracting("groupName").containsOnly("admin_group"); diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/issues_on_module-expected.json b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/issues_on_module-expected.json new file mode 100644 index 00000000000..981ebb20ec7 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/issues_on_module-expected.json @@ -0,0 +1,16 @@ +[ + { + "key": "EFGH", + "componentPath": "src/main/java/Action.java", + "ruleKey": "AvoidCycle", + "ruleRepo": "squid", + "line": 200, + "message": "Do not use this method", + "resolution": "FALSE-POSITIVE", + "status": "RESOLVED", + "checksum": "123456", + "assigneeLogin": "john", + "assigneeFullname": "John Doo" + } +] + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/issues_on_project-expected.json b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/issues_on_project-expected.json new file mode 100644 index 00000000000..981ebb20ec7 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/issues_on_project-expected.json @@ -0,0 +1,16 @@ +[ + { + "key": "EFGH", + "componentPath": "src/main/java/Action.java", + "ruleKey": "AvoidCycle", + "ruleRepo": "squid", + "line": 200, + "message": "Do not use this method", + "resolution": "FALSE-POSITIVE", + "status": "RESOLVED", + "checksum": "123456", + "assigneeLogin": "john", + "assigneeFullname": "John Doo" + } +] + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/shared.xml new file mode 100644 index 00000000000..2d199b069a8 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/batch/IssuesActionTest/shared.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_module_uuid.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_module_uuid.xml new file mode 100644 index 00000000000..197ddde6a39 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_module_uuid.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_project_uuid.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_project_uuid.xml new file mode 100644 index 00000000000..197ddde6a39 --- /dev/null +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/select_non_closed_issues_by_project_uuid.xml @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/shared.xml b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/shared.xml index 44deea3ff9c..cae2ab9c007 100644 --- a/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/shared.xml +++ b/server/sonar-server/src/test/resources/org/sonar/server/issue/db/IssueDaoTest/shared.xml @@ -2,10 +2,19 @@ - - - - + + + + + + + + + @@ -19,4 +28,6 @@ + + diff --git a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/issues/PreviousIssueHelper.java b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/issues/PreviousIssueHelper.java index 576cb9105a3..14e4e4a3785 100644 --- a/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/issues/PreviousIssueHelper.java +++ b/sonar-batch-protocol/src/main/java/org/sonar/batch/protocol/input/issues/PreviousIssueHelper.java @@ -26,39 +26,47 @@ import org.sonar.batch.protocol.GsonHelper; import javax.annotation.Nullable; +import java.io.Closeable; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.util.Iterator; import java.util.NoSuchElementException; -public class PreviousIssueHelper { +public class PreviousIssueHelper implements Closeable { private final Gson gson = GsonHelper.create(); + JsonWriter writer; - private PreviousIssueHelper() { + private PreviousIssueHelper(Writer out) { + try { + this.writer = new JsonWriter(out); + writer.setIndent(" "); + writer.beginArray(); + } catch (IOException e) { + throw new IllegalStateException("Unable to open writer", e); + } } - public static PreviousIssueHelper create() { - return new PreviousIssueHelper(); + public static PreviousIssueHelper create(Writer out) { + return new PreviousIssueHelper(out); } public static interface Function { T apply(@Nullable F from); } - public void streamIssues(Writer out, Iterable issues, Function converter) { + public void addIssue(G issue, Function converter) { + gson.toJson(converter.apply(issue), PreviousIssue.class, writer); + } + + @Override + public void close() { try { - JsonWriter writer = new JsonWriter(out); - writer.setIndent(" "); - writer.beginArray(); - for (G issue : issues) { - gson.toJson(converter.apply(issue), PreviousIssue.class, writer); - } writer.endArray(); writer.close(); } catch (IOException e) { - throw new IllegalStateException("Unable to stream issues", e); + throw new IllegalStateException("Unable to close write", e); } } diff --git a/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/input/issues/PreviousIssueHelperTest.java b/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/input/issues/PreviousIssueHelperTest.java index 8719db821f8..1e5264b4ad2 100644 --- a/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/input/issues/PreviousIssueHelperTest.java +++ b/sonar-batch-protocol/src/test/java/org/sonar/batch/protocol/input/issues/PreviousIssueHelperTest.java @@ -25,7 +25,6 @@ import org.skyscreamer.jsonassert.JSONAssert; import java.io.StringReader; import java.io.StringWriter; -import java.util.Arrays; import java.util.Iterator; import static org.assertj.core.api.Assertions.assertThat; @@ -34,8 +33,8 @@ public class PreviousIssueHelperTest { @Test public void writeIssues() throws JSONException { - PreviousIssueHelper helper = PreviousIssueHelper.create(); StringWriter out = new StringWriter(); + PreviousIssueHelper helper = PreviousIssueHelper.create(out); PreviousIssue issue1 = new PreviousIssue(); issue1.setKey("key1"); @@ -52,12 +51,10 @@ public class PreviousIssueHelperTest { PreviousIssue issue2 = new PreviousIssue(); issue2.setKey("key2"); - helper.streamIssues(out, Arrays.asList(issue1, issue2), new PreviousIssueHelper.Function() { - @Override - public PreviousIssue apply(PreviousIssue from) { - return from; - } - }); + PreviousIssueFunction previousIssueFunction = new PreviousIssueFunction(); + helper.addIssue(issue1, previousIssueFunction); + helper.addIssue(issue2, previousIssueFunction); + helper.close(); JSONAssert .assertEquals( @@ -65,11 +62,20 @@ public class PreviousIssueHelperTest { + "{\"key\": \"key2\"}]", out.getBuffer().toString(), true); + + } + + private static class PreviousIssueFunction implements PreviousIssueHelper.Function { + @Override + public PreviousIssue apply(PreviousIssue from) { + return from; + } } @Test public void readIssues() { - PreviousIssueHelper helper = PreviousIssueHelper.create(); + StringWriter out = new StringWriter(); + PreviousIssueHelper helper = PreviousIssueHelper.create(out); StringReader reader = new StringReader( "[{\"key\": \"key1\", \"componentPath\": \"path\", \"ruleKey\": \"rulekey\", \"ruleRepo\": \"repokey\", \"line\": 2,\"message\": \"message\", \"severity\": \"severity\", \"resolution\": \"resolution\", \"status\": \"status\", \"checksum\": \"checksum\",\"assigneeLogin\": \"login\", \"assigneeFullname\": \"fullname\"}," + @@ -95,6 +101,7 @@ public class PreviousIssueHelperTest { assertThat(issue1.assigneeFullname()).isEqualTo("fullname"); assertThat(issue2.key()).isEqualTo("key2"); + helper.close(); } } diff --git a/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java b/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java index f5e887bde58..87f4799525e 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java +++ b/sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java @@ -20,6 +20,7 @@ package org.sonar.core.component; import org.sonar.api.component.Component; +import org.sonar.api.resources.Scopes; import org.sonar.core.persistence.Dto; import javax.annotation.CheckForNull; @@ -223,6 +224,10 @@ public class ComponentDto extends Dto implements Component { return this; } + public boolean isRootProject() { + return MODULE_UUID_PATH_SEP.equals(moduleUuidPath) && Scopes.PROJECT.equals(scope); + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java b/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java new file mode 100644 index 00000000000..2f2343ed426 --- /dev/null +++ b/sonar-core/src/main/java/org/sonar/core/issue/db/BatchIssueDto.java @@ -0,0 +1,135 @@ +/* + * 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.core.issue.db; + +public class BatchIssueDto { + + private String kee; + private String message; + private Integer line; + private String status; + private String resolution; + private String checksum; + private String assigneeLogin; + private String assigneeName; + private String componentPath; + private String ruleKey; + private String ruleRepo; + + public String getAssigneeLogin() { + return assigneeLogin; + } + + public BatchIssueDto setAssigneeLogin(String assigneeLogin) { + this.assigneeLogin = assigneeLogin; + return this; + } + + public String getAssigneeName() { + return assigneeName; + } + + public BatchIssueDto setAssigneeName(String assigneeName) { + this.assigneeName = assigneeName; + return this; + } + + public String getChecksum() { + return checksum; + } + + public BatchIssueDto setChecksum(String checksum) { + this.checksum = checksum; + return this; + } + + public String getComponentPath() { + return componentPath; + } + + public BatchIssueDto setComponentPath(String componentPath) { + this.componentPath = componentPath; + return this; + } + + public String getKey() { + return kee; + } + + public BatchIssueDto setKey(String key) { + this.kee = key; + return this; + } + + public Integer getLine() { + return line; + } + + public BatchIssueDto setLine(Integer line) { + this.line = line; + return this; + } + + public String getMessage() { + return message; + } + + public BatchIssueDto setMessage(String message) { + this.message = message; + return this; + } + + public String getResolution() { + return resolution; + } + + public BatchIssueDto setResolution(String resolution) { + this.resolution = resolution; + return this; + } + + public String getRuleKey() { + return ruleKey; + } + + public BatchIssueDto setRuleKey(String ruleKey) { + this.ruleKey = ruleKey; + return this; + } + + public String getRuleRepo() { + return ruleRepo; + } + + public BatchIssueDto setRuleRepo(String ruleRepo) { + this.ruleRepo = ruleRepo; + return this; + } + + public String getStatus() { + return status; + } + + public BatchIssueDto setStatus(String status) { + this.status = status; + return this; + } +} diff --git a/sonar-core/src/main/java/org/sonar/core/permission/GlobalPermissions.java b/sonar-core/src/main/java/org/sonar/core/permission/GlobalPermissions.java index d8a8d5a5f95..b9893ad1c50 100644 --- a/sonar-core/src/main/java/org/sonar/core/permission/GlobalPermissions.java +++ b/sonar-core/src/main/java/org/sonar/core/permission/GlobalPermissions.java @@ -34,13 +34,13 @@ public final class GlobalPermissions { public static final String QUALITY_PROFILE_ADMIN = "profileadmin"; public static final String DASHBOARD_SHARING = "shareDashboard"; public static final String SCAN_EXECUTION = "scan"; - public static final String DRY_RUN_EXECUTION = "dryRunScan"; + public static final String PREVIEW_EXECUTION = "dryRunScan"; public static final String PROVISIONING = "provisioning"; /** * All the global permissions values, ordered from {@link #SYSTEM_ADMIN} to {@link #PROVISIONING}. */ - public static final List ALL = ImmutableList.of(SYSTEM_ADMIN, QUALITY_PROFILE_ADMIN, DASHBOARD_SHARING, SCAN_EXECUTION, DRY_RUN_EXECUTION, PROVISIONING); + public static final List ALL = ImmutableList.of(SYSTEM_ADMIN, QUALITY_PROFILE_ADMIN, DASHBOARD_SHARING, SCAN_EXECUTION, PREVIEW_EXECUTION, PROVISIONING); private GlobalPermissions() { // only static methods diff --git a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java index 62e559e59fc..75f75cf9089 100644 --- a/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java +++ b/sonar-core/src/main/java/org/sonar/core/persistence/MyBatis.java @@ -25,11 +25,7 @@ import com.google.common.io.Closeables; import org.apache.ibatis.builder.xml.XMLMapperBuilder; import org.apache.ibatis.logging.LogFactory; import org.apache.ibatis.mapping.Environment; -import org.apache.ibatis.session.Configuration; -import org.apache.ibatis.session.ExecutorType; -import org.apache.ibatis.session.SqlSession; -import org.apache.ibatis.session.SqlSessionFactory; -import org.apache.ibatis.session.SqlSessionFactoryBuilder; +import org.apache.ibatis.session.*; import org.apache.ibatis.transaction.jdbc.JdbcTransactionFactory; import org.apache.ibatis.type.JdbcType; import org.slf4j.LoggerFactory; @@ -47,14 +43,7 @@ import org.sonar.core.component.db.SnapshotMapper; import org.sonar.core.computation.db.AnalysisReportDto; import org.sonar.core.computation.db.AnalysisReportMapper; import org.sonar.core.config.Logback; -import org.sonar.core.dashboard.ActiveDashboardDto; -import org.sonar.core.dashboard.ActiveDashboardMapper; -import org.sonar.core.dashboard.DashboardDto; -import org.sonar.core.dashboard.DashboardMapper; -import org.sonar.core.dashboard.WidgetDto; -import org.sonar.core.dashboard.WidgetMapper; -import org.sonar.core.dashboard.WidgetPropertyDto; -import org.sonar.core.dashboard.WidgetPropertyMapper; +import org.sonar.core.dashboard.*; import org.sonar.core.dependency.DependencyDto; import org.sonar.core.dependency.DependencyMapper; import org.sonar.core.dependency.ResourceSnapshotDto; @@ -63,32 +52,11 @@ import org.sonar.core.duplication.DuplicationMapper; import org.sonar.core.duplication.DuplicationUnitDto; import org.sonar.core.graph.jdbc.GraphDto; import org.sonar.core.graph.jdbc.GraphDtoMapper; -import org.sonar.core.issue.db.ActionPlanDto; -import org.sonar.core.issue.db.ActionPlanMapper; -import org.sonar.core.issue.db.ActionPlanStatsDto; -import org.sonar.core.issue.db.ActionPlanStatsMapper; -import org.sonar.core.issue.db.IssueChangeDto; -import org.sonar.core.issue.db.IssueChangeMapper; -import org.sonar.core.issue.db.IssueDto; -import org.sonar.core.issue.db.IssueFilterDto; -import org.sonar.core.issue.db.IssueFilterFavouriteDto; -import org.sonar.core.issue.db.IssueFilterFavouriteMapper; -import org.sonar.core.issue.db.IssueFilterMapper; -import org.sonar.core.issue.db.IssueMapper; -import org.sonar.core.measure.db.MeasureDto; -import org.sonar.core.measure.db.MeasureFilterDto; -import org.sonar.core.measure.db.MeasureFilterMapper; -import org.sonar.core.measure.db.MeasureMapper; -import org.sonar.core.measure.db.MetricDto; -import org.sonar.core.measure.db.MetricMapper; +import org.sonar.core.issue.db.*; +import org.sonar.core.measure.db.*; import org.sonar.core.notification.db.NotificationQueueDto; import org.sonar.core.notification.db.NotificationQueueMapper; -import org.sonar.core.permission.GroupWithPermissionDto; -import org.sonar.core.permission.PermissionTemplateDto; -import org.sonar.core.permission.PermissionTemplateGroupDto; -import org.sonar.core.permission.PermissionTemplateMapper; -import org.sonar.core.permission.PermissionTemplateUserDto; -import org.sonar.core.permission.UserWithPermissionDto; +import org.sonar.core.permission.*; import org.sonar.core.persistence.dialect.Dialect; import org.sonar.core.persistence.migration.v44.Migration44Mapper; import org.sonar.core.persistence.migration.v45.Migration45Mapper; @@ -98,22 +66,9 @@ import org.sonar.core.properties.PropertyDto; import org.sonar.core.purge.IdUuidPair; import org.sonar.core.purge.PurgeMapper; import org.sonar.core.purge.PurgeableSnapshotDto; -import org.sonar.core.qualitygate.db.ProjectQgateAssociationDto; -import org.sonar.core.qualitygate.db.ProjectQgateAssociationMapper; -import org.sonar.core.qualitygate.db.QualityGateConditionDto; -import org.sonar.core.qualitygate.db.QualityGateConditionMapper; -import org.sonar.core.qualitygate.db.QualityGateDto; -import org.sonar.core.qualitygate.db.QualityGateMapper; -import org.sonar.core.qualityprofile.db.ActiveRuleDto; -import org.sonar.core.qualityprofile.db.ActiveRuleMapper; -import org.sonar.core.qualityprofile.db.ActiveRuleParamDto; -import org.sonar.core.qualityprofile.db.QualityProfileDto; -import org.sonar.core.qualityprofile.db.QualityProfileMapper; -import org.sonar.core.resource.ResourceDto; -import org.sonar.core.resource.ResourceIndexDto; -import org.sonar.core.resource.ResourceIndexerMapper; -import org.sonar.core.resource.ResourceKeyUpdaterMapper; -import org.sonar.core.resource.ResourceMapper; +import org.sonar.core.qualitygate.db.*; +import org.sonar.core.qualityprofile.db.*; +import org.sonar.core.resource.*; import org.sonar.core.rule.RuleDto; import org.sonar.core.rule.RuleMapper; import org.sonar.core.rule.RuleParamDto; @@ -123,19 +78,7 @@ import org.sonar.core.technicaldebt.db.CharacteristicMapper; import org.sonar.core.technicaldebt.db.RequirementMigrationDto; import org.sonar.core.template.LoadedTemplateDto; import org.sonar.core.template.LoadedTemplateMapper; -import org.sonar.core.user.AuthorDto; -import org.sonar.core.user.AuthorMapper; -import org.sonar.core.user.GroupDto; -import org.sonar.core.user.GroupMapper; -import org.sonar.core.user.GroupMembershipDto; -import org.sonar.core.user.GroupMembershipMapper; -import org.sonar.core.user.GroupRoleDto; -import org.sonar.core.user.RoleMapper; -import org.sonar.core.user.UserDto; -import org.sonar.core.user.UserGroupDto; -import org.sonar.core.user.UserGroupMapper; -import org.sonar.core.user.UserMapper; -import org.sonar.core.user.UserRoleDto; +import org.sonar.core.user.*; import java.io.InputStream; @@ -218,6 +161,7 @@ public class MyBatis implements BatchComponent, ServerComponent { loadAlias(conf, "Measure", MeasureDto.class); loadAlias(conf, "Metric", MetricDto.class); loadAlias(conf, "Issue", IssueDto.class); + loadAlias(conf, "BatchIssue", BatchIssueDto.class); loadAlias(conf, "IssueChange", IssueChangeDto.class); loadAlias(conf, "IssueFilter", IssueFilterDto.class); loadAlias(conf, "IssueFilterFavourite", IssueFilterFavouriteDto.class); diff --git a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml index 7453e1afa1a..2156d2c6b74 100644 --- a/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/issue/db/IssueMapper.xml @@ -176,13 +176,53 @@ p.kee as componentKey, root.kee as projectKey from issues i - inner join (select p.id,p.kee from projects p where (p.root_id=#{id} and p.qualifier <> 'BRC') or - (p.id=#{id})) p on p.id=i.component_id + inner join (select p.id,p.kee from projects p where (p.root_id=#{id} and p.qualifier <> 'BRC') or (p.id=#{id})) p on p.id=i.component_id inner join rules r on r.id=i.rule_id left outer join projects root on root.id=i.root_component_id where i.status <> 'CLOSED' + + + +