]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5305 Create a /api/components/app WS, returning only component information...
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 15 May 2014 11:42:03 +0000 (13:42 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 15 May 2014 11:42:03 +0000 (13:42 +0200)
13 files changed:
sonar-core/src/main/java/org/sonar/core/resource/ResourceDao.java
sonar-core/src/main/java/org/sonar/core/resource/ResourceMapper.java
sonar-core/src/main/resources/org/sonar/core/resource/ResourceMapper.xml
sonar-core/src/test/java/org/sonar/core/resource/ResourceDaoTest.java
sonar-server/src/main/java/org/sonar/server/component/ws/ComponentAppAction.java [new file with mode: 0644]
sonar-server/src/main/java/org/sonar/server/component/ws/ComponentsWs.java
sonar-server/src/main/java/org/sonar/server/issue/DefaultIssueFinder.java
sonar-server/src/main/java/org/sonar/server/issue/ws/IssueShowAction.java
sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
sonar-server/src/main/resources/org/sonar/server/component/ws/components-app-example-show.json [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/component/ws/ComponentAppActionTest.java [new file with mode: 0644]
sonar-server/src/test/java/org/sonar/server/component/ws/ComponentsWsTest.java
sonar-server/src/test/resources/org/sonar/server/component/ws/ComponentAppActionTest/app.json [new file with mode: 0644]

index 22582dc158f4c3247ac5556cc3448953be481964..7637e4c70e10345c35407f7f6d4ddf3017fc13a6 100644 (file)
@@ -171,6 +171,16 @@ public class ResourceDao {
     }
   }
 
+  @CheckForNull
+  public ComponentDto selectComponentByKey(String key) {
+    SqlSession session = mybatis.openSession(false);
+    try {
+      return session.getMapper(ResourceMapper.class).selectComponentByKey(key);
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+  }
+
   @CheckForNull
   public Component findByKey(String key) {
     ResourceDto resourceDto = getResource(ResourceQuery.create().setKey(key));
index 7c899784eb95005dc6a77fa7549d5edecdbc892d..88d0ac10d29e0c88535337c539509c5bd4c0a2de 100644 (file)
@@ -61,6 +61,8 @@ public interface ResourceMapper {
 
   List<ComponentDto> selectComponentsByIds(@Param("ids") List<Long> ids);
 
+  ComponentDto selectComponentByKey(String key);
+
   /**
    * @since 3.6
    */
index 6488c247480225cf7185b05f75610d308b1bdf7c..d7fc5a305b67a3268bb607967bd5909bbb747815 100644 (file)
     </where>
   </select>
 
+  <select id="selectComponentByKey" parameterType="String" resultType="Component">
+    select <include refid="componentColumns"/>
+    from projects p
+    inner join snapshots s on s.project_id=p.id and s.islast=${_true}
+    <where>
+      p.enabled=${_true}
+      and p.kee=#{key}
+    </where>
+  </select>
+
   <select id="selectProjectsIncludingNotCompletedOnesByQualifiers" parameterType="map" resultMap="resourceResultMap">
     select * from projects p
     <where>
index 013e9b220740bf6dd38b15ff4ec2a3e089d5f891..2ab9a786848c677e46f1c019ee050bd61e5e313b 100644 (file)
@@ -209,6 +209,14 @@ public class ResourceDaoTest extends AbstractDaoTestCase {
     assertThat(component.path()).isNull();
   }
 
+  @Test
+  public void select_component_by_key() {
+    setupData("fixture");
+
+    assertThat(dao.selectComponentByKey("org.struts:struts-core:src/org/struts/RequestContext.java")).isNotNull();
+    assertThat(dao.selectComponentByKey("unknown")).isNull();
+  }
+
   @Test
   public void select_components_by_ids_on_huge_number_of_ids() {
     setupData("fixture");
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
new file mode 100644 (file)
index 0000000..e783244
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * 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.component.ws;
+
+import com.google.common.io.Resources;
+import org.sonar.api.component.Component;
+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.utils.text.JsonWriter;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.resource.ResourceDao;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.UserSession;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+public class ComponentAppAction implements RequestHandler {
+
+  private static final String KEY = "key";
+
+  private final ResourceDao resourceDao;
+
+  public ComponentAppAction(ResourceDao resourceDao) {
+    this.resourceDao = resourceDao;
+  }
+
+  void define(WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction("app")
+      .setDescription("Coverage data required for rendering the component viewer")
+      .setSince("4.4")
+      .setInternal(true)
+      .setHandler(this)
+      .setResponseExample(Resources.getResource(this.getClass(), "components-app-example-show.json"));
+
+    action
+      .createParam(KEY)
+      .setRequired(true)
+      .setDescription("File key")
+      .setExampleValue("my_project:/src/foo/Bar.php");
+  }
+
+  @Override
+  public void handle(Request request, Response response) {
+    String fileKey = request.mandatoryParam(KEY);
+    UserSession.get().checkComponentPermission(UserRole.CODEVIEWER, fileKey);
+
+    JsonWriter json = response.newJsonWriter();
+    json.beginObject();
+
+    ComponentDto component = resourceDao.selectComponentByKey(fileKey);
+    if (component == null) {
+      throw new NotFoundException(String.format("Component '%s' does not exists.", fileKey));
+    }
+
+    json.prop("key", component.key());
+    json.prop("path", component.path());
+    json.prop("name", component.name());
+    json.prop("q", component.qualifier());
+
+    Component subProject = componentById(component.subProjectId());
+    json.prop("subProjectName", subProject != null ? subProject.longName() : null);
+
+    Component project = componentById(component.projectId());
+    json.prop("projectName", project != null ? project.longName() : null);
+
+    json.endObject();
+    json.close();
+  }
+
+  @CheckForNull
+  private Component componentById(@Nullable Long componentId) {
+    if (componentId != null) {
+      return resourceDao.findById(componentId);
+    }
+    return null;
+  }
+
+}
index 0dc9390e5b521a94dad66c29f67bdf209b1d4f73..edf407a500dc9fa6c30bd6c030d04b3de23d8096 100644 (file)
@@ -26,12 +26,19 @@ import org.sonar.api.server.ws.WebService;
 
 public class ComponentsWs implements WebService {
 
+  private final ComponentAppAction appAction;
+
+  public ComponentsWs(ComponentAppAction appAction) {
+    this.appAction = appAction;
+  }
+
   @Override
   public void define(Context context) {
     NewController controller = context.createController("api/components")
       .setSince("4.2")
       .setDescription("Components management");
 
+    appAction.define(controller);
     defineSuggestionsAction(controller);
 
     controller.done();
index 9882b276348ebd5592d556ecf3c3c47d433339d8..cc9230326d7d65303521a36322b2d8240b76e68f 100644 (file)
@@ -46,6 +46,7 @@ import org.sonar.server.issue.actionplan.ActionPlanService;
 import org.sonar.server.user.UserSession;
 
 import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
 
 import java.util.*;
 
@@ -144,8 +145,8 @@ public class DefaultIssueFinder implements IssueFinder {
       }
 
       Collection<Component> components = findComponents(componentIds);
-      Collection<Component> groupComponents = findGroupComponents(components);
-      Collection<Component> rootComponents = findRootComponents(components);
+      Collection<Component> groupComponents = findSubProjects(components);
+      Collection<Component> rootComponents = findProjects(components);
 
       Set<Component> allComponents = newHashSet(components);
       allComponents.addAll(groupComponents);
@@ -203,20 +204,20 @@ public class DefaultIssueFinder implements IssueFinder {
     return Lists.<Component>newArrayList(resourceDao.selectComponentsByIds(componentIds));
   }
 
-  private Collection<Component> findGroupComponents(Collection<Component> components) {
+  private Collection<Component> findSubProjects(Collection<Component> components) {
     return findComponents(newHashSet(Iterables.transform(components, new Function<Component, Long>() {
       @Override
-      public Long apply(Component input) {
-        return ((ComponentDto) input).subProjectId();
+      public Long apply(@Nullable Component input) {
+        return input != null ? ((ComponentDto) input).subProjectId() : null;
       }
     })));
   }
 
-  private Collection<Component> findRootComponents(Collection<Component> components) {
+  private Collection<Component> findProjects(Collection<Component> components) {
     return findComponents(newHashSet(Iterables.transform(components, new Function<Component, Long>() {
       @Override
-      public Long apply(Component input) {
-        return ((ComponentDto) input).projectId();
+      public Long apply(@Nullable Component input) {
+        return input != null ? ((ComponentDto) input).projectId() : null;
       }
     })));
   }
index 4406a8ffbaad9d3cdbf3f7c5df47ad8982f8adaf..07f0455d630ecd895c4ffae6353f3cdb522f439e 100644 (file)
@@ -179,8 +179,8 @@ public class IssueShowAction implements RequestHandler {
       return Iterables.find(result.components(), new Predicate<Component>() {
         @Override
         public boolean apply(Component input) {
-          Long groupId = component.subProjectId();
-          return groupId != null && groupId.equals(((ComponentDto) input).getId());
+          Long subProjectId = component.subProjectId();
+          return subProjectId != null && subProjectId.equals(((ComponentDto) input).getId());
         }
       }, null);
     }
index 3e92fc1e203eff822ca8f73f327b0cc0f4a7f155..9bfaf3e41c0ede56cdaba2537b9b3f8597dfd6db 100644 (file)
@@ -82,6 +82,7 @@ import org.sonar.server.cluster.LocalNonBlockingWorkQueue;
 import org.sonar.server.cluster.LocalQueueWorker;
 import org.sonar.server.component.DefaultComponentFinder;
 import org.sonar.server.component.DefaultRubyComponentService;
+import org.sonar.server.component.ws.ComponentAppAction;
 import org.sonar.server.component.ws.ComponentsWs;
 import org.sonar.server.component.ws.ProjectsWs;
 import org.sonar.server.component.ws.ResourcesWs;
@@ -411,6 +412,7 @@ class ServerComponents {
     pico.addSingleton(ResourcesWs.class);
     pico.addSingleton(ComponentsWs.class);
     pico.addSingleton(ProjectsWs.class);
+    pico.addSingleton(ComponentAppAction.class);
 
     // issues
     pico.addSingleton(ServerIssueStorage.class);
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
new file mode 100644 (file)
index 0000000..ce23f70
--- /dev/null
@@ -0,0 +1,19 @@
+{
+  "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": true,
+  "periods": [],
+  "measures": {
+    "ncloc": 200,
+    "coverage": 2,
+    "duplication_density": 1,
+    "debt": 1,
+    "issues": 1,
+    "blocker_issues": 1,
+    "info_issues": 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
new file mode 100644 (file)
index 0000000..d2a3b02
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * 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.component.ws;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+import org.sonar.core.resource.ResourceDao;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.ws.WsTester;
+
+import static org.mockito.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class ComponentAppActionTest {
+
+  @Mock
+  ResourceDao resourceDao;
+
+  WsTester tester;
+
+  @Before
+  public void setUp() throws Exception {
+    tester = new WsTester(new ComponentsWs(new ComponentAppAction(resourceDao)));
+  }
+
+  @Test
+  public void app() throws Exception {
+    String projectKey = "org.codehaus.sonar:sonar-plugin-api:api";
+    String componentKey = "org.codehaus.sonar:sonar-plugin-api:src/main/java/org/sonar/api/Plugin.java";
+    MockUserSession.set().addProjectPermissions(UserRole.CODEVIEWER, projectKey).addComponent(componentKey, projectKey);
+
+    ComponentDto file = new ComponentDto().setId(10L).setQualifier("FIL").setKey(componentKey).setName("Plugin.java")
+      .setPath("src/main/java/org/sonar/api/Plugin.java").setSubProjectId(5L).setProjectId(1L);
+    when(resourceDao.selectComponentByKey(componentKey)).thenReturn(file);
+    when(resourceDao.findById(5L)).thenReturn(new ComponentDto().setId(5L).setLongName("SonarQube :: Plugin API"));
+    when(resourceDao.findById(1L)).thenReturn(new ComponentDto().setId(1L).setLongName("SonarQube"));
+
+    WsTester.TestRequest request = tester.newGetRequest("api/components", "app").setParam("key", componentKey);
+    request.execute().assertJson(getClass(), "app.json");
+  }
+
+}
index b1fe015bc65f02dba89a88bac41131dccd95cda3..25c817d3ca77976a9cfd28113675faad4eb0579b 100644 (file)
@@ -24,9 +24,11 @@ import org.junit.Before;
 import org.junit.Test;
 import org.sonar.api.server.ws.RailsHandler;
 import org.sonar.api.server.ws.WebService;
+import org.sonar.core.resource.ResourceDao;
 import org.sonar.server.ws.WsTester;
 
 import static org.fest.assertions.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
 
 public class ComponentsWsTest {
 
@@ -34,7 +36,7 @@ public class ComponentsWsTest {
 
   @Before
   public void setUp() throws Exception {
-    WsTester tester = new WsTester(new ComponentsWs());
+    WsTester tester = new WsTester(new ComponentsWs(new ComponentAppAction(mock(ResourceDao.class))));
     controller = tester.controller("api/components");
   }
 
@@ -43,7 +45,7 @@ public class ComponentsWsTest {
     assertThat(controller).isNotNull();
     assertThat(controller.description()).isNotEmpty();
     assertThat(controller.since()).isEqualTo("4.2");
-    assertThat(controller.actions()).hasSize(1);
+    assertThat(controller.actions()).hasSize(2);
   }
 
   @Test
@@ -57,4 +59,15 @@ public class ComponentsWsTest {
     assertThat(action.params()).hasSize(2);
   }
 
+  @Test
+  public void define_app_action() throws Exception {
+    WebService.Action action = controller.action("app");
+    assertThat(action).isNotNull();
+    assertThat(action.isInternal()).isTrue();
+    assertThat(action.isPost()).isFalse();
+    assertThat(action.handler()).isNotNull();
+    assertThat(action.responseExampleAsString()).isNotEmpty();
+    assertThat(action.params()).hasSize(1);
+  }
+
 }
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
new file mode 100644 (file)
index 0000000..28f3d67
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "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",
+}