]> source.dussan.org Git - sonarqube.git/commitdiff
Add key in /api/sources/lines WS
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 27 Apr 2015 14:36:40 +0000 (16:36 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 27 Apr 2015 14:36:40 +0000 (16:36 +0200)
server/sonar-server/src/main/java/org/sonar/server/source/ws/LinesAction.java
server/sonar-server/src/test/java/org/sonar/server/source/ws/LinesActionTest.java
server/sonar-server/src/test/java/org/sonar/server/source/ws/SourcesWsTest.java
server/sonar-server/src/test/resources/org/sonar/server/source/ws/LinesActionTest/show_source.json
server/sonar-server/src/test/resources/org/sonar/server/source/ws/LinesActionTest/show_source_by_file_key.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/source/ws/LinesActionTest/show_source_with_params_from_and_to.json

index c5fb6cf9a013a28a5922f7ad6bb4806f9e9058e6..ff750c5c98449e882e170a03f445d700660cede2 100644 (file)
@@ -28,7 +28,9 @@ import org.sonar.api.utils.DateUtils;
 import org.sonar.api.utils.text.JsonWriter;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.component.ComponentDto;
-import org.sonar.server.component.ComponentService;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.MyBatis;
+import org.sonar.server.db.DbClient;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.source.HtmlSourceDecorator;
 import org.sonar.server.source.index.SourceLineDoc;
@@ -40,14 +42,17 @@ import java.util.List;
 
 public class LinesAction implements SourcesAction {
 
+  private static final String PARAM_UUID = "uuid";
+  private static final String PARAM_KEY = "key";
+
   private final SourceLineIndex sourceLineIndex;
   private final HtmlSourceDecorator htmlSourceDecorator;
-  private final ComponentService componentService;
+  private final DbClient dbClient;
 
-  public LinesAction(SourceLineIndex sourceLineIndex, HtmlSourceDecorator htmlSourceDecorator, ComponentService componentService) {
+  public LinesAction(DbClient dbClient, SourceLineIndex sourceLineIndex, HtmlSourceDecorator htmlSourceDecorator) {
     this.sourceLineIndex = sourceLineIndex;
     this.htmlSourceDecorator = htmlSourceDecorator;
-    this.componentService = componentService;
+    this.dbClient = dbClient;
   }
 
   @Override
@@ -74,11 +79,15 @@ public class LinesAction implements SourcesAction {
       .setHandler(this);
 
     action
-      .createParam("uuid")
-      .setRequired(true)
-      .setDescription("File uuid")
+      .createParam(PARAM_UUID)
+      .setDescription("File uuid. Mandatory if param 'key' is not set")
       .setExampleValue("f333aab4-7e3a-4d70-87e1-f4c491f05e5c");
 
+    action
+      .createParam(PARAM_KEY)
+      .setDescription("File key. Mandatory if param 'uuid' is not set. Available since 5.2")
+      .setExampleValue("org.sample:src/main/java/Foo.java");
+
     action
       .createParam("from")
       .setDescription("First line to return. Starts at 1")
@@ -93,16 +102,15 @@ public class LinesAction implements SourcesAction {
 
   @Override
   public void handle(Request request, Response response) {
-    String fileUuid = request.mandatoryParam("uuid");
-    ComponentDto component = componentService.getByUuid(fileUuid);
+    ComponentDto component = loadComponent(request);
     UserSession.get().checkProjectUuidPermission(UserRole.CODEVIEWER, component.projectUuid());
 
     int from = Math.max(request.mandatoryParamAsInt("from"), 1);
     int to = (Integer) ObjectUtils.defaultIfNull(request.paramAsInt("to"), Integer.MAX_VALUE);
 
-    List<SourceLineDoc> sourceLines = sourceLineIndex.getLines(fileUuid, from, to);
+    List<SourceLineDoc> sourceLines = sourceLineIndex.getLines(component.uuid(), from, to);
     if (sourceLines.isEmpty()) {
-      throw new NotFoundException("File '" + fileUuid + "' has no sources");
+      throw new NotFoundException("File '" + component.key() + "' has no sources");
     }
 
     JsonWriter json = response.newJsonWriter().beginObject();
@@ -113,7 +121,7 @@ public class LinesAction implements SourcesAction {
 
   private void writeSource(List<SourceLineDoc> lines, JsonWriter json) {
     json.name("sources").beginArray();
-    for (SourceLineDoc line: lines) {
+    for (SourceLineDoc line : lines) {
       json.beginObject()
         .prop("line", line.line())
         .prop("code", htmlSourceDecorator.getDecoratedSourceAsHtml(line.source(), line.highlighting(), line.symbols()))
@@ -127,11 +135,29 @@ public class LinesAction implements SourcesAction {
         .prop("itLineHits", line.itLineHits())
         .prop("itConditions", line.itConditions())
         .prop("itCoveredConditions", line.itCoveredConditions());
-      if (! line.duplications().isEmpty()) {
+      if (!line.duplications().isEmpty()) {
         json.prop("duplicated", true);
       }
       json.endObject();
     }
     json.endArray();
   }
+
+  private ComponentDto loadComponent(Request request) {
+    DbSession session = dbClient.openSession(false);
+    try {
+      String fileUuid = request.param(PARAM_UUID);
+      if (fileUuid != null) {
+        return dbClient.componentDao().getByUuid(session, fileUuid);
+      }
+      String fileKey = request.param(PARAM_KEY);
+      if (fileKey != null) {
+        return dbClient.componentDao().getByKey(session, fileKey);
+      }
+      throw new IllegalArgumentException(String.format("Param %s or param %s is missing", PARAM_UUID, PARAM_KEY));
+    } finally {
+      MyBatis.closeQuietly(session);
+    }
+  }
+
 }
index 7dbbbb30cfb9892658976ced0d33e6edea08377a..f8a3825b08e0b971ae192f1a396c656dcf104775 100644 (file)
  * 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.source.ws;
 
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import org.apache.commons.lang.StringEscapeUtils;
+import org.junit.After;
 import org.junit.Before;
+import org.junit.ClassRule;
 import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.Mock;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.runners.MockitoJUnitRunner;
-import org.mockito.stubbing.Answer;
+import org.sonar.api.config.Settings;
 import org.sonar.api.web.UserRole;
 import org.sonar.core.component.ComponentDto;
-import org.sonar.server.component.ComponentService;
+import org.sonar.core.persistence.DbSession;
+import org.sonar.core.persistence.DbTester;
+import org.sonar.server.component.ComponentTesting;
+import org.sonar.server.component.db.ComponentDao;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.es.EsTester;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
 import org.sonar.server.source.HtmlSourceDecorator;
@@ -43,54 +45,67 @@ import org.sonar.server.ws.WsTester;
 
 import java.util.Date;
 
-import static com.google.common.collect.Lists.newArrayList;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
 import static org.junit.Assert.fail;
-import static org.mockito.Matchers.*;
-import static org.mockito.Mockito.when;
 
-@RunWith(MockitoJUnitRunner.class)
 public class LinesActionTest {
 
-  @Mock
+  private static final String PROJECT_UUID = "abcd";
+  private static final String FILE_UUID = "efgh";
+  private static final String FILE_KEY = "Foo.java";
+
+  @ClassRule
+  public static EsTester esTester = new EsTester().addDefinitions(new SourceLineIndexDefinition(new Settings()));
+
+  @ClassRule
+  public static DbTester dbTester = new DbTester();
+
   SourceLineIndex sourceLineIndex;
 
-  @Mock
   HtmlSourceDecorator htmlSourceDecorator;
 
-  @Mock
-  ComponentService componentService;
+  ComponentDao componentDao;
+
+  DbSession session;
 
-  WsTester tester;
+  WsTester wsTester;
 
   @Before
   public void setUp() throws Exception {
-    tester = new WsTester(new SourcesWs(new LinesAction(sourceLineIndex, htmlSourceDecorator, componentService)));
-    when(htmlSourceDecorator.getDecoratedSourceAsHtml(anyString(), anyString(), anyString())).thenAnswer(new Answer<String>() {
-      @Override
-      public String answer(InvocationOnMock invocation) throws Throwable {
-        return "<span class=\"" + invocation.getArguments()[1] + " sym-" + invocation.getArguments()[2] + "\">" +
-          StringEscapeUtils.escapeHtml((String) invocation.getArguments()[0]) +
-          "</span>";
-      }
-    });
+    dbTester.truncateTables();
+    esTester.truncateIndices();
+
+    htmlSourceDecorator = new HtmlSourceDecorator();
+    sourceLineIndex = new SourceLineIndex(esTester.client());
+    componentDao = new ComponentDao();
+    DbClient dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), componentDao);
+    session = dbClient.openSession(false);
+    wsTester = new WsTester(new SourcesWs(new LinesAction(dbClient, sourceLineIndex, htmlSourceDecorator)));
+
+    MockUserSession.set();
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    session.close();
   }
 
   @Test
   public void show_source() throws Exception {
-    String projectUuid = "abcd";
-    String componentUuid = "efgh";
+    newFile();
+
     Date updatedAt = new Date();
     String scmDate = "2014-01-01T12:34:56.789Z";
     SourceLineDoc line1 = new SourceLineDoc()
-      .setProjectUuid(projectUuid)
-      .setFileUuid(componentUuid)
+      .setProjectUuid(PROJECT_UUID)
+      .setFileUuid(FILE_UUID)
       .setLine(1)
       .setScmRevision("cafebabe")
       .setScmAuthor("polop")
-      .setSource("class Polop {")
-      .setHighlighting("h1")
-      .setSymbols("palap")
+      .setSource("package org.polop;")
+      .setHighlighting("0,7,k")
+      .setSymbols("8,17,42")
       .setUtLineHits(3)
       .setUtConditions(2)
       .setUtCoveredConditions(1)
@@ -102,33 +117,33 @@ public class LinesActionTest {
     line1.setField(SourceLineIndexDefinition.FIELD_SCM_DATE, scmDate);
 
     SourceLineDoc line2 = new SourceLineDoc()
-      .setProjectUuid(projectUuid)
-      .setFileUuid(componentUuid)
+      .setProjectUuid(PROJECT_UUID)
+      .setFileUuid(FILE_UUID)
       .setLine(2)
       .setScmRevision("cafebabe")
       .setScmAuthor("polop")
-      .setSource("  // Empty")
-      .setHighlighting("h2")
-      .setSymbols("pulup")
+      .setSource("abc")
+      .setHighlighting("0,5,c")
+      .setSymbols("")
       .setUtLineHits(3)
       .setUtConditions(2)
       .setUtCoveredConditions(1)
       .setItLineHits(null)
       .setItConditions(null)
       .setItCoveredConditions(null)
-      .setDuplications(ImmutableList.<Integer>of(1))
+      .setDuplications(ImmutableList.of(1))
       .setUpdateDate(updatedAt);
     line2.setField(SourceLineIndexDefinition.FIELD_SCM_DATE, scmDate);
 
     SourceLineDoc line3 = new SourceLineDoc()
-      .setProjectUuid(projectUuid)
-      .setFileUuid(componentUuid)
+      .setProjectUuid(PROJECT_UUID)
+      .setFileUuid(FILE_UUID)
       .setLine(3)
       .setScmRevision("cafebabe")
       .setScmAuthor("polop")
       .setSource("}")
-      .setHighlighting("h3")
-      .setSymbols("pylyp")
+      .setHighlighting(null)
+      .setSymbols(null)
       .setUtLineHits(null)
       .setUtConditions(null)
       .setUtCoveredConditions(null)
@@ -139,33 +154,22 @@ public class LinesActionTest {
       .setUpdateDate(updatedAt);
     line3.setField(SourceLineIndexDefinition.FIELD_SCM_DATE, scmDate);
 
-    when(sourceLineIndex.getLines(eq(componentUuid), anyInt(), anyInt())).thenReturn(newArrayList(
-      line1,
-      line2,
-      line3
-      ));
+    esTester.putDocuments(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE, line1, line2, line3);
 
-    String componentKey = "componentKey";
-    when(componentService.getByUuid(componentUuid)).thenReturn(new ComponentDto().setKey(componentKey).setProjectUuid(projectUuid));
-    MockUserSession.set().setLogin("login").addProjectUuidPermissions(UserRole.CODEVIEWER, projectUuid);
+    MockUserSession.set().setLogin("login").addProjectUuidPermissions(UserRole.CODEVIEWER, PROJECT_UUID);
 
-    WsTester.TestRequest request = tester.newGetRequest("api/sources", "lines").setParam("uuid", componentUuid);
-    // Using non-strict match b/c of dates
+    WsTester.TestRequest request = wsTester.newGetRequest("api/sources", "lines").setParam("uuid", FILE_UUID);
     request.execute().assertJson(getClass(), "show_source.json");
   }
 
   @Test
   public void fail_to_show_source_if_no_source_found() throws Exception {
-    String componentUuid = "abcd";
-    String projectUuid = "efgh";
-    when(sourceLineIndex.getLines(anyString(), anyInt(), anyInt())).thenReturn(Lists.<SourceLineDoc>newArrayList());
+    newFile();
 
-    String componentKey = "componentKey";
-    when(componentService.getByUuid(componentUuid)).thenReturn(new ComponentDto().setKey(componentKey).setProjectUuid(projectUuid));
-    MockUserSession.set().setLogin("login").addProjectUuidPermissions(UserRole.CODEVIEWER, projectUuid);
+    MockUserSession.set().setLogin("login").addProjectUuidPermissions(UserRole.CODEVIEWER, PROJECT_UUID);
 
     try {
-      WsTester.TestRequest request = tester.newGetRequest("api/sources", "lines").setParam("uuid", componentUuid);
+      WsTester.TestRequest request = wsTester.newGetRequest("api/sources", "lines").setParam("uuid", FILE_UUID);
       request.execute();
       fail();
     } catch (Exception e) {
@@ -175,17 +179,14 @@ public class LinesActionTest {
 
   @Test
   public void show_source_with_from_and_to_params() throws Exception {
-    String projectUuid = "abcd";
-    String fileUuid = "efgh";
+    newFile();
 
-    String componentKey = "componentKey";
-    when(componentService.getByUuid(fileUuid)).thenReturn(new ComponentDto().setKey(componentKey).setProjectUuid(projectUuid));
-    MockUserSession.set().setLogin("login").addProjectUuidPermissions(UserRole.CODEVIEWER, projectUuid);
+    MockUserSession.set().setLogin("login").addProjectUuidPermissions(UserRole.CODEVIEWER, PROJECT_UUID);
 
-    when(sourceLineIndex.getLines(fileUuid, 3, 3)).thenReturn(newArrayList(
+    esTester.putDocuments(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE,
       new SourceLineDoc()
-        .setProjectUuid(projectUuid)
-        .setFileUuid(fileUuid)
+        .setProjectUuid(PROJECT_UUID)
+        .setFileUuid(FILE_UUID)
         .setLine(3)
         .setScmRevision("cafebabe")
         .setScmDate(null)
@@ -201,25 +202,76 @@ public class LinesActionTest {
         .setItCoveredConditions(null)
         .setDuplications(null)
         .setUpdateDate(new Date())
-      ));
-    WsTester.TestRequest request = tester
+    );
+
+    WsTester.TestRequest request = wsTester
       .newGetRequest("api/sources", "lines")
-      .setParam("uuid", fileUuid)
+      .setParam("uuid", FILE_UUID)
       .setParam("from", "3")
       .setParam("to", "3");
     request.execute().assertJson(getClass(), "show_source_with_params_from_and_to.json");
   }
 
+  @Test
+  public void show_source_by_file_key() throws Exception {
+    newFile();
+
+    esTester.putDocuments(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE,
+      new SourceLineDoc()
+        .setProjectUuid(PROJECT_UUID)
+        .setFileUuid(FILE_UUID)
+        .setLine(3)
+        .setScmRevision("cafebabe")
+        .setScmDate(null)
+        .setScmAuthor("polop")
+        .setSource("}")
+        .setHighlighting("")
+        .setSymbols("")
+        .setUtLineHits(null)
+        .setUtConditions(null)
+        .setUtCoveredConditions(null)
+        .setItLineHits(null)
+        .setItConditions(null)
+        .setItCoveredConditions(null)
+        .setDuplications(null)
+        .setUpdateDate(new Date())
+    );
+
+    MockUserSession.set().setLogin("login").addProjectUuidPermissions(UserRole.CODEVIEWER, PROJECT_UUID);
+
+    WsTester.TestRequest request = wsTester.newGetRequest("api/sources", "lines").setParam("key", FILE_KEY);
+    request.execute().assertJson(getClass(), "show_source_by_file_key.json");
+  }
+
+  @Test
+  public void fail_when_no_uuid_or_key_param() throws Exception {
+    newFile();
+    MockUserSession.set().setLogin("login").addProjectUuidPermissions(UserRole.CODEVIEWER, PROJECT_UUID);
+    WsTester.TestRequest request = wsTester.newGetRequest("api/sources", "lines");
+
+    try {
+      request.execute();
+      failBecauseExceptionWasNotThrown(IllegalArgumentException.class);
+    } catch (IllegalArgumentException e) {
+      assertThat(e).hasMessage("Param uuid or param key is missing");
+    }
+  }
+
   @Test(expected = ForbiddenException.class)
   public void should_check_permission() throws Exception {
-    String fileUuid = "efgh";
+    newFile();
 
-    String componentKey = "componentKey";
-    when(componentService.getByUuid(fileUuid)).thenReturn(new ComponentDto().setKey(componentKey));
     MockUserSession.set().setLogin("login");
 
-    tester.newGetRequest("api/sources", "lines")
-      .setParam("uuid", fileUuid)
+    wsTester.newGetRequest("api/sources", "lines")
+      .setParam("uuid", FILE_UUID)
       .execute();
   }
+
+  private void newFile(){
+    ComponentDto project = ComponentTesting.newProjectDto(PROJECT_UUID);
+    ComponentDto file = ComponentTesting.newFileDto(project, FILE_UUID).setKey(FILE_KEY);
+    componentDao.insert(session, project, file);
+    session.commit();
+  }
 }
index 491d42912e68ef6e01c8f8fcbc331c84506169b0..32a7887d33c216363dcd46ac5decb039686f3f18 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.server.source.ws;
 
 import org.junit.Test;
 import org.sonar.api.server.ws.WebService;
-import org.sonar.server.component.ComponentService;
 import org.sonar.server.db.DbClient;
 import org.sonar.server.source.HtmlSourceDecorator;
 import org.sonar.server.source.SourceService;
@@ -36,7 +35,7 @@ public class SourcesWsTest {
 
   ShowAction showAction = new ShowAction(mock(SourceService.class), mock(DbClient.class));
   RawAction rawAction = new RawAction(mock(DbClient.class), mock(SourceService.class));
-  LinesAction linesAction = new LinesAction(mock(SourceLineIndex.class), mock(HtmlSourceDecorator.class), mock(ComponentService.class));
+  LinesAction linesAction = new LinesAction(mock(DbClient.class), mock(SourceLineIndex.class), mock(HtmlSourceDecorator.class));
   HashAction hashAction = new HashAction(mock(DbClient.class));
   IndexAction indexAction = new IndexAction(mock(DbClient.class), mock(SourceService.class));
   WsTester tester = new WsTester(new SourcesWs(showAction, rawAction, linesAction, hashAction, indexAction));
@@ -71,7 +70,7 @@ public class SourcesWsTest {
     assertThat(lines.since()).isEqualTo("5.0");
     assertThat(lines.isInternal()).isTrue();
     assertThat(lines.responseExampleAsString()).isNotEmpty();
-    assertThat(lines.params()).hasSize(3);
+    assertThat(lines.params()).hasSize(4);
 
     WebService.Action hash = controller.action("hash");
     assertThat(hash).isNotNull();
index 7655e20c817ef643b54f51e202164b02f06414dd..eebd43bc502e7761363d663a6985a63d6f836897 100644 (file)
@@ -1,7 +1,7 @@
 {
   "sources": [
     {
-      "code": "<span class=\"h1 sym-palap\">class Polop {</span>",
+      "code": "<span class=\"k\">package</span> <span class=\"sym-42 sym\">org.polop</span>;",
       "line": 1,
       "scmAuthor": "polop",
       "scmRevision": "cafebabe",
@@ -13,7 +13,7 @@
       "itCoveredConditions": 1
     },
     {
-      "code": "<span class=\"h2 sym-pulup\">  // Empty</span>",
+      "code": "<span class=\"c\">abc</span>",
       "line": 2,
       "scmAuthor": "polop",
       "scmRevision": "cafebabe",
@@ -23,7 +23,7 @@
       "duplicated": true
     },
     {
-      "code": "<span class=\"h3 sym-pylyp\">}</span>",
+      "code": "}",
       "line": 3,
       "scmAuthor": "polop",
       "scmRevision": "cafebabe",
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/ws/LinesActionTest/show_source_by_file_key.json b/server/sonar-server/src/test/resources/org/sonar/server/source/ws/LinesActionTest/show_source_by_file_key.json
new file mode 100644 (file)
index 0000000..ba2a4c6
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "sources": [
+    {
+      "code": "}",
+      "line": 3,
+      "scmAuthor": "polop",
+      "scmRevision": "cafebabe"
+    }
+  ]
+}
index 8845334e1d954ea8cc1ac3e805af8c9e4894e279..ba2a4c6e52b3c923c7f9be3a2e8c564cd68a1852 100644 (file)
@@ -1,7 +1,7 @@
 {
   "sources": [
     {
-      "code": "<span class=\" sym-\">}</span>",
+      "code": "}",
       "line": 3,
       "scmAuthor": "polop",
       "scmRevision": "cafebabe"