diff options
16 files changed, 83 insertions, 12 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureMapper.java b/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureMapper.java index fc3db6097b4..4fcb4ac938d 100644 --- a/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureMapper.java +++ b/sonar-core/src/main/java/org/sonar/core/measure/db/MeasureMapper.java @@ -30,4 +30,6 @@ public interface MeasureMapper { List<MeasureDto> selectByComponentAndMetrics(@Param("componentKey") String componentKey, @Param("metricKeys") List<String> metricKeys); + long count(@Param("key") MeasureKey key); + } diff --git a/sonar-core/src/main/resources/org/sonar/core/measure/db/MeasureMapper.xml b/sonar-core/src/main/resources/org/sonar/core/measure/db/MeasureMapper.xml index e7553613f56..d1adf669fdc 100644 --- a/sonar-core/src/main/resources/org/sonar/core/measure/db/MeasureMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/measure/db/MeasureMapper.xml @@ -45,4 +45,19 @@ </where> </select> + <select id="count" parameterType="map" resultType="long"> + SELECT count(pm.id) + FROM project_measures pm + INNER JOIN snapshots s ON s.id=pm.snapshot_id AND s.islast=${_true} + INNER JOIN metrics metric ON metric.id=pm.metric_id + INNER JOIN projects p ON p.id=s.project_id AND p.enabled=${_true} + <where> + AND p.kee = #{key.componentKey} + AND metric.name = #{key.metricKey} + AND pm.rule_id IS NULL + AND pm.characteristic_id IS NULL + AND pm.person_id IS NULL + </where> + </select> + </mapper> diff --git a/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java b/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java index 4dd47f6880d..47ddd61e687 100644 --- a/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java +++ b/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java @@ -52,6 +52,7 @@ import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.issue.IssueService; import org.sonar.server.issue.RulesAggregation; import org.sonar.server.measure.persistence.MeasureDao; +import org.sonar.server.source.SourceService; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; @@ -69,13 +70,15 @@ public class ComponentAppAction implements RequestHandler { private final DbClient dbClient; private final IssueService issueService; + private final SourceService sourceService; private final Periods periods; private final Durations durations; private final I18n i18n; - public ComponentAppAction(DbClient dbClient, IssueService issueService, Periods periods, Durations durations, I18n i18n) { + public ComponentAppAction(DbClient dbClient, IssueService issueService, SourceService sourceService, Periods periods, Durations durations, I18n i18n) { this.dbClient = dbClient; this.issueService = issueService; + this.sourceService = sourceService; this.periods = periods; this.durations = durations; this.i18n = i18n; @@ -146,6 +149,7 @@ public class ComponentAppAction implements RequestHandler { json.prop("projectName", project.longName()); json.prop("fav", isFavourite); + json.prop("scmAvailable", sourceService.hasScmData(component.key(), session)); } private void appendPermissions(JsonWriter json, ComponentDto component, UserSession userSession) { diff --git a/sonar-server/src/main/java/org/sonar/server/measure/persistence/MeasureDao.java b/sonar-server/src/main/java/org/sonar/server/measure/persistence/MeasureDao.java index 9303d974145..8446ba71cb5 100644 --- a/sonar-server/src/main/java/org/sonar/server/measure/persistence/MeasureDao.java +++ b/sonar-server/src/main/java/org/sonar/server/measure/persistence/MeasureDao.java @@ -52,6 +52,10 @@ public class MeasureDao extends BaseDao<MeasureMapper, MeasureDto, MeasureKey> i return session.getMapper(MeasureMapper.class).selectByKey(key); } + public boolean exists(MeasureKey key, DbSession session) { + return session.getMapper(MeasureMapper.class).count(key) > 0; + } + public List<MeasureDto> findByComponentKeyAndMetricKeys(String componentKey, List<String> metricKeys, DbSession session){ if (metricKeys.isEmpty()) { return Collections.emptyList(); diff --git a/sonar-server/src/main/java/org/sonar/server/source/SourceService.java b/sonar-server/src/main/java/org/sonar/server/source/SourceService.java index ad3ead6d929..d2c09186e06 100644 --- a/sonar-server/src/main/java/org/sonar/server/source/SourceService.java +++ b/sonar-server/src/main/java/org/sonar/server/source/SourceService.java @@ -27,6 +27,7 @@ import org.sonar.core.measure.db.MeasureDto; import org.sonar.core.measure.db.MeasureKey; import org.sonar.core.persistence.DbSession; import org.sonar.core.persistence.MyBatis; +import org.sonar.server.db.DbClient; import org.sonar.server.measure.persistence.MeasureDao; import org.sonar.server.user.UserSession; @@ -37,7 +38,7 @@ import java.util.List; public class SourceService implements ServerComponent { - private final MyBatis myBatis; + private final DbClient dbClient; private final HtmlSourceDecorator sourceDecorator; @@ -48,11 +49,11 @@ public class SourceService implements ServerComponent { private final MeasureDao measureDao; - public SourceService(MyBatis myBatis, HtmlSourceDecorator sourceDecorator, DeprecatedSourceDecorator deprecatedSourceDecorator, MeasureDao measureDao) { - this.myBatis = myBatis; + public SourceService(DbClient dbClient, HtmlSourceDecorator sourceDecorator, DeprecatedSourceDecorator deprecatedSourceDecorator) { + this.dbClient = dbClient; this.sourceDecorator = sourceDecorator; this.deprecatedSourceDecorator = deprecatedSourceDecorator; - this.measureDao = measureDao; + this.measureDao = dbClient.getDao(MeasureDao.class); } public List<String> getLinesAsHtml(String fileKey) { @@ -81,13 +82,27 @@ public class SourceService implements ServerComponent { return findDataFromComponent(fileKey, CoreMetrics.SCM_LAST_COMMIT_DATETIMES_BY_LINE_KEY); } + public boolean hasScmData(String fileKey, DbSession session) { + return measureDao.exists(MeasureKey.of(fileKey, CoreMetrics.SCM_AUTHORS_BY_LINE_KEY), session); + } + + public boolean hasScmData(String fileKey) { + checkPermission(fileKey); + DbSession session = dbClient.openSession(false); + try { + return hasScmData(fileKey, session); + } finally { + MyBatis.closeQuietly(session); + } + } + private void checkPermission(String fileKey) { UserSession.get().checkComponentPermission(UserRole.CODEVIEWER, fileKey); } @CheckForNull private String findDataFromComponent(String fileKey, String metricKey) { - DbSession session = myBatis.openSession(false); + DbSession session = dbClient.openSession(false); try { MeasureDto data = measureDao.getByKey(MeasureKey.of(fileKey, metricKey), session); if (data != null) { diff --git a/sonar-server/src/main/resources/org/sonar/server/component/ws/components-app-example-show.json b/sonar-server/src/main/resources/org/sonar/server/component/ws/components-app-example-show.json index 7c2670fa21f..5798fdac578 100644 --- a/sonar-server/src/main/resources/org/sonar/server/component/ws/components-app-example-show.json +++ b/sonar-server/src/main/resources/org/sonar/server/component/ws/components-app-example-show.json @@ -6,6 +6,7 @@ "subProjectName": "SonarQube :: Plugin API", "projectName": "SonarQube", "fav": true, + "scmAvailable": true, "canMarkAsFavourite": true, "canBulkChange": false, "periods": [ diff --git a/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java b/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java index fd2900c0bba..510d648a572 100644 --- a/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java +++ b/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java @@ -49,6 +49,7 @@ import org.sonar.server.db.DbClient; import org.sonar.server.issue.IssueService; import org.sonar.server.issue.RulesAggregation; import org.sonar.server.measure.persistence.MeasureDao; +import org.sonar.server.source.SourceService; import org.sonar.server.user.MockUserSession; import org.sonar.server.ws.WsTester; @@ -86,6 +87,9 @@ public class ComponentAppActionTest { IssueService issueService; @Mock + SourceService sourceService; + + @Mock Periods periods; @Mock @@ -111,7 +115,7 @@ public class ComponentAppActionTest { when(issueService.findRulesByComponent(anyString(), eq(session))).thenReturn(mock(RulesAggregation.class)); when(measureDao.findByComponentKeyAndMetricKeys(anyString(), anyListOf(String.class), eq(session))).thenReturn(measures); - tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, issueService, periods, durations, i18n))); + tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, issueService, sourceService, periods, durations, i18n))); } @Test @@ -124,6 +128,7 @@ public class ComponentAppActionTest { when(componentDao.getById(5L, session)).thenReturn(new ComponentDto().setId(5L).setLongName("SonarQube :: Plugin API")); when(componentDao.getById(1L, session)).thenReturn(new ComponentDto().setId(1L).setLongName("SonarQube")); when(propertiesDao.selectByQuery(any(PropertyQuery.class), eq(session))).thenReturn(newArrayList(new PropertyDto())); + when(sourceService.hasScmData(eq(COMPONENT_KEY), eq(session))).thenReturn(true); WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); request.execute().assertJson(getClass(), "app.json"); diff --git a/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java b/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java index c6358a587b1..dc952770f8a 100644 --- a/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java +++ b/sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java @@ -29,6 +29,7 @@ import org.sonar.api.utils.Durations; import org.sonar.core.timemachine.Periods; import org.sonar.server.db.DbClient; import org.sonar.server.issue.IssueService; +import org.sonar.server.source.SourceService; import org.sonar.server.ws.WsTester; import static org.fest.assertions.Assertions.assertThat; @@ -40,7 +41,8 @@ public class ComponentsWsTest { @Before public void setUp() throws Exception { - WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(DbClient.class), mock(IssueService.class), mock(Periods.class), mock(Durations.class), mock(I18n.class)))); + WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(DbClient.class), mock(IssueService.class), mock(SourceService.class), + mock(Periods.class), mock(Durations.class), mock(I18n.class)))); controller = tester.controller("api/components"); } diff --git a/sonar-server/src/test/java/org/sonar/server/measure/persistence/MeasureDaoTest.java b/sonar-server/src/test/java/org/sonar/server/measure/persistence/MeasureDaoTest.java index 27755289041..f3ac394599f 100644 --- a/sonar-server/src/test/java/org/sonar/server/measure/persistence/MeasureDaoTest.java +++ b/sonar-server/src/test/java/org/sonar/server/measure/persistence/MeasureDaoTest.java @@ -93,4 +93,12 @@ public class MeasureDaoTest extends AbstractDaoTestCase { assertThat(result.getValue()).isEqualTo(10d); assertThat(result.getKey()).isNotNull(); } + + @Test + public void exists() throws Exception { + setupData("shared"); + + assertThat(dao.exists(MeasureKey.of("org.struts:struts-core:src/org/struts/RequestContext.java", "ncloc"), session)).isTrue(); + assertThat(dao.exists(MeasureKey.of("org.struts:struts-core:src/org/struts/RequestContext.java", "unknown"), session)).isFalse(); + } } diff --git a/sonar-server/src/test/java/org/sonar/server/source/SourceServiceTest.java b/sonar-server/src/test/java/org/sonar/server/source/SourceServiceTest.java index feae50a14dc..2370356e815 100644 --- a/sonar-server/src/test/java/org/sonar/server/source/SourceServiceTest.java +++ b/sonar-server/src/test/java/org/sonar/server/source/SourceServiceTest.java @@ -29,7 +29,7 @@ import org.sonar.api.measures.CoreMetrics; import org.sonar.api.web.UserRole; import org.sonar.core.measure.db.MeasureKey; import org.sonar.core.persistence.DbSession; -import org.sonar.core.persistence.MyBatis; +import org.sonar.server.db.DbClient; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.measure.persistence.MeasureDao; import org.sonar.server.user.MockUserSession; @@ -62,9 +62,10 @@ public class SourceServiceTest { @Before public void setUp() throws Exception { - MyBatis myBatis = mock(MyBatis.class); - when(myBatis.openSession(false)).thenReturn(session); - service = new SourceService(myBatis, sourceDecorator, deprecatedSourceDecorator, measureDao); + DbClient dbClient = mock(DbClient.class); + when(dbClient.openSession(false)).thenReturn(session); + when(dbClient.getDao(MeasureDao.class)).thenReturn(measureDao); + service = new SourceService(dbClient, sourceDecorator, deprecatedSourceDecorator); } @Test @@ -133,4 +134,12 @@ public class SourceServiceTest { when(measureDao.getByKey(any(MeasureKey.class), eq(session))).thenReturn(null); assertThat(service.getScmDateData(COMPONENT_KEY)).isNull(); } + + @Test + public void has_scm_data() throws Exception { + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY); + + when(measureDao.exists(any(MeasureKey.class), eq(session))).thenReturn(true); + assertThat(service.hasScmData(COMPONENT_KEY)).isTrue(); + } } diff --git a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app.json b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app.json index 9d71aad91ca..466fa162d50 100644 --- a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app.json +++ b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app.json @@ -6,6 +6,7 @@ "subProjectName": "SonarQube :: Plugin API", "projectName": "SonarQube", "fav": true, + "scmAvailable": true, "canMarkAsFavourite": true, "canBulkChange": true, "periods": [], diff --git a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_measures.json b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_measures.json index 34c784044b3..0a888c70c9d 100644 --- a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_measures.json +++ b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_measures.json @@ -6,6 +6,7 @@ "subProjectName": "SonarQube :: Plugin API", "projectName": "SonarQube", "fav": false, + "scmAvailable": false, "canMarkAsFavourite": false, "canBulkChange": false, "periods": [], diff --git a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_periods.json b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_periods.json index 646cc2464e3..bed31c83110 100644 --- a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_periods.json +++ b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_periods.json @@ -6,6 +6,7 @@ "subProjectName": "SonarQube :: Plugin API", "projectName": "SonarQube", "fav": false, + "scmAvailable": false, "canMarkAsFavourite": false, "canBulkChange": false, "periods": [ diff --git a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_rules.json b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_rules.json index 78afaae5404..eec5ca7c609 100644 --- a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_rules.json +++ b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_rules.json @@ -6,6 +6,7 @@ "subProjectName": "SonarQube :: Plugin API", "projectName": "SonarQube", "fav": false, + "scmAvailable": false, "canMarkAsFavourite": false, "canBulkChange": false, "periods": [], diff --git a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_severities.json b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_severities.json index 1dcbac620c7..a479050f9e1 100644 --- a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_severities.json +++ b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_severities.json @@ -6,6 +6,7 @@ "subProjectName": "SonarQube :: Plugin API", "projectName": "SonarQube", "fav": false, + "scmAvailable": false, "canMarkAsFavourite": false, "canBulkChange": false, "periods": [], diff --git a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_without_sub_project.json b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_without_sub_project.json index 9d6f9806489..d0bbe41275e 100644 --- a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_without_sub_project.json +++ b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_without_sub_project.json @@ -4,6 +4,7 @@ "q": "TRK", "projectName": "SonarQube", "fav": true, + "scmAvailable": false, "canMarkAsFavourite": true, "canBulkChange": true, "periods": [], |