diff options
10 files changed, 146 insertions, 7 deletions
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 71b48a6eb3f..a106603b7bf 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 @@ -33,6 +33,8 @@ public class ComponentDto extends Dto<String> implements Component { private String name; private String longName; private String qualifier; + private String scope; + private String language; private Long projectId; private Long subProjectId; @@ -96,6 +98,25 @@ public class ComponentDto extends Dto<String> implements Component { return this; } + public String scope() { + return scope; + } + + public ComponentDto setScope(String scope) { + this.scope = scope; + return this; + } + + @CheckForNull + public String language() { + return language; + } + + public ComponentDto setLanguage(@Nullable String language) { + this.language = language; + return this; + } + public Long projectId() { return projectId; } diff --git a/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml b/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml index 27ab64ac750..a604fea396b 100644 --- a/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml +++ b/sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml @@ -8,6 +8,8 @@ p.name as name, p.long_name as longName, p.qualifier as qualifier, + p.scope as scope, + p.language as language, s.root_project_id as projectId, p.root_id as subProjectId, p.path as path diff --git a/sonar-core/src/test/java/org/sonar/core/component/ComponentDtoTest.java b/sonar-core/src/test/java/org/sonar/core/component/ComponentDtoTest.java index 1b2ea6a641f..b71779f0e06 100644 --- a/sonar-core/src/test/java/org/sonar/core/component/ComponentDtoTest.java +++ b/sonar-core/src/test/java/org/sonar/core/component/ComponentDtoTest.java @@ -34,6 +34,8 @@ public class ComponentDtoTest { .setName("RequestContext.java") .setLongName("org.struts.RequestContext") .setQualifier("FIL") + .setScope("FIL") + .setLanguage("java") .setPath("src/org/struts/RequestContext.java") .setProjectId(2L) .setSubProjectId(3L); @@ -43,7 +45,9 @@ public class ComponentDtoTest { assertThat(componentDto.name()).isEqualTo("RequestContext.java"); assertThat(componentDto.longName()).isEqualTo("org.struts.RequestContext"); assertThat(componentDto.qualifier()).isEqualTo("FIL"); + assertThat(componentDto.scope()).isEqualTo("FIL"); assertThat(componentDto.path()).isEqualTo("src/org/struts/RequestContext.java"); + assertThat(componentDto.language()).isEqualTo("java"); assertThat(componentDto.projectId()).isEqualTo(2L); assertThat(componentDto.subProjectId()).isEqualTo(3L); } 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 7b74b9549dd..47d8e4daca0 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 @@ -35,6 +35,8 @@ import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.Durations; import org.sonar.api.utils.text.JsonWriter; +import org.sonar.api.web.NavigationSection; +import org.sonar.api.web.Page; import org.sonar.api.web.UserRole; import org.sonar.core.component.ComponentDto; import org.sonar.core.measure.db.MeasureDto; @@ -49,6 +51,8 @@ import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.issue.IssueService; import org.sonar.server.issue.RulesAggregation; import org.sonar.server.source.SourceService; +import org.sonar.server.ui.ViewProxy; +import org.sonar.server.ui.Views; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; @@ -67,14 +71,16 @@ public class ComponentAppAction implements RequestHandler { private final IssueService issueService; private final SourceService sourceService; + private final Views views; private final Periods periods; private final Durations durations; private final I18n i18n; - public ComponentAppAction(DbClient dbClient, IssueService issueService, SourceService sourceService, Periods periods, Durations durations, I18n i18n) { + public ComponentAppAction(DbClient dbClient, IssueService issueService, SourceService sourceService, Views views, Periods periods, Durations durations, I18n i18n) { this.dbClient = dbClient; this.issueService = issueService; this.sourceService = sourceService; + this.views = views; this.periods = periods; this.durations = durations; this.i18n = i18n; @@ -116,6 +122,7 @@ public class ComponentAppAction implements RequestHandler { appendPeriods(json, component.projectId(), session); appendIssuesAggregation(json, component.key(), session); appendMeasures(json, component, session); + appendExtensions(json, component, userSession); } finally { MyBatis.closeQuietly(session); } @@ -228,6 +235,35 @@ public class ComponentAppAction implements RequestHandler { json.endArray(); } + private void appendExtensions(JsonWriter json, ComponentDto component, UserSession userSession) { + List<ViewProxy<Page>> extensionPages = views.getPages(NavigationSection.RESOURCE_TAB, component.scope(), component.qualifier(), component.language(), null); + List<String> extensions = extensions(extensionPages, component, userSession); + if (!extensions.isEmpty()) { + json.name("extensions").beginArray(); + json.values(extensions); + json.endArray(); + } + } + + private List<String> extensions(List<ViewProxy<Page>> extensions, ComponentDto component, UserSession userSession){ + List<String> result = newArrayList(); + List<String> providedExtensions = newArrayList("tests_viewer", "coverage", "duplications", "issues", "source"); + for (ViewProxy<Page> page : extensions) { + if (!providedExtensions.contains(page.getId())) { + if (page.getUserRoles().length == 0) { + result.add(page.getId()); + } else { + for (String userRole : page.getUserRoles()) { + if (userSession.hasComponentPermission(userRole, component.key())) { + result.add(page.getId()); + } + } + } + } + } + return result; + } + @CheckForNull private Component componentById(@Nullable Long componentId, DbSession session) { if (componentId != 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 5798fdac578..a5561d06187 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 @@ -45,5 +45,6 @@ "fDebt": "4h", "fIssues": "4", "fInfoIssues": "4" - } + }, + "extensions": ["metricsTab"] } diff --git a/sonar-server/src/test/java/org/sonar/server/component/persistence/ComponentDaoTest.java b/sonar-server/src/test/java/org/sonar/server/component/persistence/ComponentDaoTest.java index 2e8f075c82b..9d1cc0fcecf 100644 --- a/sonar-server/src/test/java/org/sonar/server/component/persistence/ComponentDaoTest.java +++ b/sonar-server/src/test/java/org/sonar/server/component/persistence/ComponentDaoTest.java @@ -56,6 +56,8 @@ public class ComponentDaoTest extends AbstractDaoTestCase { assertThat(result.name()).isEqualTo("RequestContext.java"); assertThat(result.longName()).isEqualTo("org.struts.RequestContext"); assertThat(result.qualifier()).isEqualTo("FIL"); + assertThat(result.scope()).isEqualTo("FIL"); + assertThat(result.language()).isEqualTo("java"); assertThat(result.subProjectId()).isEqualTo(2); assertThat(result.projectId()).isEqualTo(1); @@ -73,6 +75,8 @@ public class ComponentDaoTest extends AbstractDaoTestCase { assertThat(result.name()).isEqualTo("Struts"); assertThat(result.longName()).isEqualTo("Apache Struts"); assertThat(result.qualifier()).isEqualTo("TRK"); + assertThat(result.scope()).isEqualTo("PRJ"); + assertThat(result.language()).isNull(); assertThat(result.subProjectId()).isNull(); assertThat(result.projectId()).isEqualTo(1); } 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 84e20e69d8f..c059072f2d6 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 @@ -34,6 +34,8 @@ import org.sonar.api.measures.CoreMetrics; import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.Duration; import org.sonar.api.utils.Durations; +import org.sonar.api.web.NavigationSection; +import org.sonar.api.web.Page; import org.sonar.api.web.UserRole; import org.sonar.core.component.ComponentDto; import org.sonar.core.measure.db.MeasureDto; @@ -52,6 +54,8 @@ 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.ui.ViewProxy; +import org.sonar.server.ui.Views; import org.sonar.server.user.MockUserSession; import org.sonar.server.ws.WsTester; @@ -96,6 +100,9 @@ public class ComponentAppActionTest { SourceService sourceService; @Mock + Views views; + + @Mock Periods periods; @Mock @@ -124,7 +131,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, sourceService, periods, durations, i18n))); + tester = new WsTester(new ComponentsWs(new ComponentAppAction(dbClient, issueService, sourceService, views, periods, durations, i18n))); } @Test @@ -263,6 +270,18 @@ public class ComponentAppActionTest { request.execute().assertJson(getClass(), "app_with_rules.json"); } + @Test + public void app_with_extensions() throws Exception { + MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY); + addComponent(); + + when(views.getPages(anyString(), anyString(), anyString(), anyString(), any(String[].class))).thenReturn( + newArrayList(new ViewProxy<Page>(new MyExtension()), new ViewProxy<Page>(new MyExtensionWithRole()), new ViewProxy<Page>(new ProvidedExtension()))); + + WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", COMPONENT_KEY); + request.execute().assertJson(getClass(), "app_with_extensions.json"); + } + private void addComponent() { ComponentDto file = new ComponentDto().setId(10L).setQualifier("FIL").setKey(COMPONENT_KEY).setName("Plugin.java") .setPath("src/main/java/org/sonar/api/Plugin.java").setSubProjectId(5L).setProjectId(1L); @@ -281,4 +300,38 @@ public class ComponentAppActionTest { when(i18n.formatDouble(any(Locale.class), eq(value))).thenReturn(Double.toString(value)); } + @NavigationSection(NavigationSection.RESOURCE_TAB) + private static class MyExtension implements Page { + public String getId() { + return "my-extension"; + } + + public String getTitle() { + return "my-extension"; + } + } + + @NavigationSection(NavigationSection.RESOURCE_TAB) + @UserRole(UserRole.CODEVIEWER) + private static class MyExtensionWithRole implements Page { + public String getId() { + return "my-extension-with-role"; + } + + public String getTitle() { + return "my-extension-with-role"; + } + } + + @NavigationSection(NavigationSection.RESOURCE_TAB) + private static class ProvidedExtension implements Page { + public String getId() { + return "issues"; + } + + public String getTitle() { + return "issues"; + } + } + } 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 dc952770f8a..b61e0a2de9f 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 @@ -30,6 +30,7 @@ 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.ui.Views; import org.sonar.server.ws.WsTester; import static org.fest.assertions.Assertions.assertThat; @@ -41,7 +42,7 @@ public class ComponentsWsTest { @Before public void setUp() throws Exception { - WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(DbClient.class), mock(IssueService.class), mock(SourceService.class), + WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(DbClient.class), mock(IssueService.class), mock(SourceService.class), mock(Views.class), mock(Periods.class), mock(Durations.class), mock(I18n.class)))); controller = tester.controller("api/components"); } diff --git a/sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/shared.xml b/sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/shared.xml index c86f8c33061..05861ec543f 100644 --- a/sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/shared.xml +++ b/sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/shared.xml @@ -7,7 +7,7 @@ <!-- root project --> <projects id="1" root_id="[null]" scope="PRJ" qualifier="TRK" kee="org.struts:struts" name="Struts" description="the description" long_name="Apache Struts" - enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="[null]"/> + enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="[null]"/> <snapshots id="1" project_id="1" parent_snapshot_id="[null]" root_project_id="1" root_snapshot_id="[null]" status="P" islast="[true]" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" @@ -30,7 +30,7 @@ <!-- module --> <projects id="2" root_id="1" kee="org.struts:struts-core" name="Struts Core" scope="PRJ" qualifier="BRC" long_name="Struts Core" - description="[null]" enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]"/> + description="[null]" enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]"/> <snapshots id="2" project_id="2" parent_snapshot_id="1" root_project_id="1" root_snapshot_id="1" status="P" islast="[true]" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" @@ -45,7 +45,7 @@ <projects long_name="org.struts" id="3" scope="DIR" qualifier="DIR" kee="org.struts:struts-core:src/org/struts" name="src/org/struts" root_id="2" description="[null]" - enabled="[true]" language="java" copy_resource_id="[null]" person_id="[null]" path="src/org/struts"/> + enabled="[true]" language="[null]" copy_resource_id="[null]" person_id="[null]" path="src/org/struts"/> <snapshots id="3" project_id="3" parent_snapshot_id="2" root_project_id="1" root_snapshot_id="1" status="P" islast="[true]" purge_status="[null]" period1_mode="[null]" period1_param="[null]" period1_date="[null]" diff --git a/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_extensions.json b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_extensions.json new file mode 100644 index 00000000000..a8b2efcb113 --- /dev/null +++ b/sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_extensions.json @@ -0,0 +1,17 @@ +{ + "key": "org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/Plugin.java", + "path": "src/main/java/org/sonar/api/Plugin.java", + "name": "Plugin.java", + "q": "FIL", + "subProjectName": "SonarQube :: Plugin API", + "projectName": "SonarQube", + "fav": false, + "scmAvailable": false, + "canMarkAsFavourite": false, + "canBulkChange": false, + "periods": [], + "severities": [], + "rules": [], + "measures": {}, + "extensions": ["my-extension", "my-extension-with-role"] +} |