]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5306 Add list of component viewer extensions in /api/component/app WS
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 2 Jun 2014 11:46:19 +0000 (13:46 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 2 Jun 2014 11:46:31 +0000 (13:46 +0200)
sonar-core/src/main/java/org/sonar/core/component/ComponentDto.java
sonar-core/src/main/resources/org/sonar/core/component/db/ComponentMapper.xml
sonar-core/src/test/java/org/sonar/core/component/ComponentDtoTest.java
sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java
sonar-server/src/main/resources/org/sonar/server/component/ws/components-app-example-show.json
sonar-server/src/test/java/org/sonar/server/component/persistence/ComponentDaoTest.java
sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java
sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java
sonar-server/src/test/resources/org/sonar/server/component/persistence/ComponentDaoTest/shared.xml
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app_with_extensions.json [new file with mode: 0644]

index 71b48a6eb3fa3b875dc1c69aad55069767daec2c..a106603b7bfc68bd67b86512a9d3dadf07f1eb40 100644 (file)
@@ -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;
   }
index 27ab64ac750f27ca3c4b00a3106afb1f25928e6b..a604fea396b2f658dfad01c03e42de5d42ef45ee 100644 (file)
@@ -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
index 1b2ea6a641f74cdc7024ed5b325c212c8d35aa2e..b71779f0e06ee4a578ea8473f3d969a920ebd4d6 100644 (file)
@@ -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);
   }
index 7b74b9549dd3069fd8ab20853a8e1e42eca768f0..47d8e4daca005ee64195ecc6a8bdf208415154b2 100644 (file)
@@ -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) {
index 5798fdac578bbb6577928ae62a192a90168b6bdf..a5561d0618724cc720c509c9c4fdddb39df963c4 100644 (file)
@@ -45,5 +45,6 @@
     "fDebt": "4h",
     "fIssues": "4",
     "fInfoIssues": "4"
-  }
+  },
+  "extensions": ["metricsTab"]
 }
index 2e8f075c82bb9be33c4a0a9eb8dbb3e892df2002..9d1cc0fcecf0fc864b390be58a5d32a7cbb13238 100644 (file)
@@ -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);
   }
index 84e20e69d8f013df6766dfb23758b271c519b0fd..c059072f2d67f4fb1cfdb4a56b53fde71ebb8941 100644 (file)
@@ -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;
 
@@ -95,6 +99,9 @@ public class ComponentAppActionTest {
   @Mock
   SourceService sourceService;
 
+  @Mock
+  Views views;
+
   @Mock
   Periods periods;
 
@@ -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";
+    }
+  }
+
 }
index dc952770f8a73a2aae2c2b3e693495792e960956..b61e0a2de9f02faba22c3565acddf9d63fd2e79c 100644 (file)
@@ -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");
   }
index c86f8c330616739f4d23679ee54f454058932fe8..05861ec543f81c1e3e79be8f8cb84a28a245f6c1 100644 (file)
@@ -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 (file)
index 0000000..a8b2efc
--- /dev/null
@@ -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"]
+}