]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-6350 /api/sources/scm should read information from index 178/head
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 30 Mar 2015 14:21:33 +0000 (16:21 +0200)
committerJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 30 Mar 2015 14:22:05 +0000 (16:22 +0200)
22 files changed:
server/sonar-server/src/main/java/org/sonar/server/platform/ServerComponents.java
server/sonar-server/src/main/java/org/sonar/server/source/ws/HashAction.java
server/sonar-server/src/main/java/org/sonar/server/source/ws/IndexAction.java
server/sonar-server/src/main/java/org/sonar/server/source/ws/LinesAction.java
server/sonar-server/src/main/java/org/sonar/server/source/ws/RawAction.java
server/sonar-server/src/main/java/org/sonar/server/source/ws/ScmAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/source/ws/ShowAction.java
server/sonar-server/src/main/java/org/sonar/server/source/ws/SourcesAction.java [new file with mode: 0644]
server/sonar-server/src/main/java/org/sonar/server/source/ws/SourcesWs.java
server/sonar-server/src/main/resources/org/sonar/server/source/ws/example-scm.json
server/sonar-server/src/test/java/org/sonar/server/source/ws/HashActionTest.java
server/sonar-server/src/test/java/org/sonar/server/source/ws/IndexActionTest.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/RawActionTest.java
server/sonar-server/src/test/java/org/sonar/server/source/ws/ScmActionTest.java [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/source/ws/ShowActionTest.java
server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/accept_negative_value_in_from_parameter.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/group_lines_by_commit.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/not_group_lines_by_commit.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/return_empty_value_when_no_scm.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/show_scm.json [new file with mode: 0644]
server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/show_scm_from_given_range_lines.json [new file with mode: 0644]

index b313796534305c3c9bdcccd780add004475e5800..3742afb756799a1c0a2e806a64af64413f9affbe 100644 (file)
@@ -601,6 +601,7 @@ class ServerComponents {
     pico.addSingleton(HashAction.class);
     pico.addSingleton(RawAction.class);
     pico.addSingleton(IndexAction.class);
+    pico.addSingleton(ScmAction.class);
     pico.addSingleton(SourceLineIndexDefinition.class);
     pico.addSingleton(SourceLineIndex.class);
     pico.addSingleton(SourceLineIndexer.class);
index b75ddfe711a97cd7de09e5fdc75063b034954d61..de3d87c84275b3ef92de91368b1ccf3dd6cda17f 100644 (file)
@@ -24,7 +24,6 @@ import com.google.common.io.CharStreams;
 import com.google.common.io.Resources;
 import org.apache.commons.io.Charsets;
 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.web.UserRole;
@@ -37,7 +36,7 @@ import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.Reader;
 
-public class HashAction implements RequestHandler {
+public class HashAction implements SourcesAction {
 
   private final DbClient dbClient;
 
@@ -45,7 +44,8 @@ public class HashAction implements RequestHandler {
     this.dbClient = dbClient;
   }
 
-  void define(WebService.NewController controller) {
+  @Override
+  public void define(WebService.NewController controller) {
     WebService.NewAction action = controller.createAction("hash")
       .setDescription("Show line line hashes for a given file. Require See Source Code permission on file's project<br/>")
       .setSince("5.0")
index fd94bb125f66f7e5786d997bf05fb265ab087557..178c49abb0623fb78ca5a513975797a2c9f5c0d2 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.server.source.ws;
 
 import com.google.common.io.Resources;
 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;
@@ -35,7 +34,7 @@ import org.sonar.server.user.UserSession;
 
 import java.util.List;
 
-public class IndexAction implements RequestHandler {
+public class IndexAction implements SourcesAction {
 
   private final DbClient dbClient;
   private final SourceService sourceService;
@@ -45,7 +44,8 @@ public class IndexAction implements RequestHandler {
     this.sourceService = sourceService;
   }
 
-  void define(WebService.NewController controller) {
+  @Override
+  public void define(WebService.NewController controller) {
     WebService.NewAction action = controller.createAction("index")
       .setDescription("Get source code as line number / text pairs. Require See Source Code permission on file")
       .setSince("5.0")
index 4ad075bdb779e57c8ba910f3dcb4192151fb56d8..c5fb6cf9a013a28a5922f7ad6bb4806f9e9058e6 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.server.source.ws;
 import com.google.common.io.Resources;
 import org.apache.commons.lang.ObjectUtils;
 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.DateUtils;
@@ -39,7 +38,7 @@ import org.sonar.server.user.UserSession;
 import java.util.Date;
 import java.util.List;
 
-public class LinesAction implements RequestHandler {
+public class LinesAction implements SourcesAction {
 
   private final SourceLineIndex sourceLineIndex;
   private final HtmlSourceDecorator htmlSourceDecorator;
@@ -51,7 +50,8 @@ public class LinesAction implements RequestHandler {
     this.componentService = componentService;
   }
 
-  void define(WebService.NewController controller) {
+  @Override
+  public void define(WebService.NewController controller) {
     WebService.NewAction action = controller.createAction("lines")
       .setDescription("Show source code with line oriented info. Require See Source Code permission on file's project<br/>" +
         "Each element of the result array is an object which contains:" +
@@ -106,12 +106,12 @@ public class LinesAction implements RequestHandler {
     }
 
     JsonWriter json = response.newJsonWriter().beginObject();
-    writeSource(sourceLines, from, json);
+    writeSource(sourceLines, json);
 
     json.endObject().close();
   }
 
-  private void writeSource(List<SourceLineDoc> lines, int from, JsonWriter json) {
+  private void writeSource(List<SourceLineDoc> lines, JsonWriter json) {
     json.name("sources").beginArray();
     for (SourceLineDoc line: lines) {
       json.beginObject()
index 84fde01fedfea9966d6c5d9a6a5f5c880dddb8c4..ea04157ff4caba929a459a3adda196dd33578cef 100644 (file)
@@ -24,7 +24,6 @@ import com.google.common.io.Resources;
 import org.apache.commons.io.Charsets;
 import org.apache.commons.io.IOUtils;
 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.web.UserRole;
@@ -37,7 +36,7 @@ import org.sonar.server.user.UserSession;
 import java.io.IOException;
 import java.util.List;
 
-public class RawAction implements RequestHandler {
+public class RawAction implements SourcesAction {
 
   private final DbClient dbClient;
   private final SourceService sourceService;
@@ -47,7 +46,8 @@ public class RawAction implements RequestHandler {
     this.sourceService = sourceService;
   }
 
-  void define(WebService.NewController controller) {
+  @Override
+  public void  define(WebService.NewController controller) {
     WebService.NewAction action = controller.createAction("raw")
       .setDescription("Get source code as plain text. Require See Source Code permission on file")
       .setSince("5.0")
diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/ws/ScmAction.java b/server/sonar-server/src/main/java/org/sonar/server/source/ws/ScmAction.java
new file mode 100644 (file)
index 0000000..7abdecb
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * 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.source.ws;
+
+import com.google.common.base.Strings;
+import com.google.common.io.Resources;
+import org.apache.commons.lang.ObjectUtils;
+import org.apache.commons.lang.builder.EqualsBuilder;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+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.core.persistence.DbSession;
+import org.sonar.server.db.DbClient;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.source.index.SourceLineDoc;
+import org.sonar.server.source.index.SourceLineIndex;
+import org.sonar.server.user.UserSession;
+
+import java.util.Date;
+import java.util.List;
+
+public class ScmAction implements SourcesAction {
+
+  private final DbClient dbClient;
+  private final SourceLineIndex sourceLineIndex;
+
+  public ScmAction(DbClient dbClient, SourceLineIndex sourceLineIndex) {
+    this.dbClient = dbClient;
+    this.sourceLineIndex = sourceLineIndex;
+  }
+
+  @Override
+  public void define(WebService.NewController controller) {
+    WebService.NewAction action = controller.createAction("scm")
+      .setDescription("Get SCM information of source files. Require See Source Code permission on file's project<br/>" +
+        "Each element of the result array is composed of:" +
+        "<ol>" +
+        "<li>Line number</li>" +
+        "<li>Author of the commit</li>" +
+        "<li>Datetime of the commit (before 5.2 it was only the Date)</li>" +
+        "<li>Revision of the commit (added in 5.2)</li>" +
+        "</ol>")
+      .setSince("4.4")
+      .setResponseExample(Resources.getResource(getClass(), "example-scm.json"))
+      .setHandler(this);
+
+    action
+      .createParam("key")
+      .setRequired(true)
+      .setDescription("File key")
+      .setExampleValue("my_project:/src/foo/Bar.php");
+
+    action
+      .createParam("from")
+      .setDescription("First line to return. Starts at 1")
+      .setExampleValue("10")
+      .setDefaultValue("1");
+
+    action
+      .createParam("to")
+      .setDescription("Last line to return (inclusive)")
+      .setExampleValue("20");
+
+    action
+      .createParam("commits_by_line")
+      .setDescription("Group lines by SCM commit if value is false, else display commits for each line, even if two " +
+        "consecutive lines relate to the same commit.")
+      .setBooleanPossibleValues()
+      .setDefaultValue("false");
+  }
+
+  @Override
+  public void handle(Request request, Response response) {
+    String fileKey = request.mandatoryParam("key");
+    int from = Math.max(request.mandatoryParamAsInt("from"), 1);
+    int to = (Integer) ObjectUtils.defaultIfNull(request.paramAsInt("to"), Integer.MAX_VALUE);
+    boolean commitsByLine = request.mandatoryParamAsBoolean("commits_by_line");
+
+    DbSession session = dbClient.openSession(false);
+    try {
+      ComponentDto fileDto = dbClient.componentDao().getByKey(session, fileKey);
+      UserSession.get().checkProjectUuidPermission(UserRole.CODEVIEWER, fileDto.projectUuid());
+      List<SourceLineDoc> sourceLines = sourceLineIndex.getLines(fileDto.uuid(), from, to);
+      if (sourceLines.isEmpty()) {
+        throw new NotFoundException("File '" + fileKey + "' has no sources");
+      }
+
+      JsonWriter json = response.newJsonWriter().beginObject();
+      writeSource(sourceLines, commitsByLine, json);
+      json.endObject().close();
+    } finally {
+      session.close();
+    }
+  }
+
+  private static void writeSource(List<SourceLineDoc> lines, boolean showCommitsByLine, JsonWriter json) {
+    json.name("scm").beginArray();
+
+    SourceLineDoc previousLine = null;
+    boolean started = false;
+    for (SourceLineDoc lineDoc : lines) {
+      if (hasScm(lineDoc) && (!started || showCommitsByLine || !isSameCommit(previousLine, lineDoc))) {
+        json.beginArray()
+          .value(lineDoc.line())
+          .value(lineDoc.scmAuthor());
+        Date date = lineDoc.scmDate();
+        json.value(date == null ? null : DateUtils.formatDateTime(date));
+        json.value(lineDoc.scmRevision());
+        json.endArray();
+        started = true;
+      }
+      previousLine = lineDoc;
+    }
+    json.endArray();
+  }
+
+  private static boolean isSameCommit(SourceLineDoc previousLine, SourceLineDoc currentLine) {
+    return new EqualsBuilder()
+      .append(previousLine.scmAuthor(), currentLine.scmAuthor())
+      .append(previousLine.scmDate(), currentLine.scmDate())
+      .append(previousLine.scmRevision(), currentLine.scmRevision())
+      .isEquals();
+  }
+
+  private static boolean hasScm(SourceLineDoc line){
+    return !Strings.isNullOrEmpty(line.scmAuthor()) || line.scmDate() != null || !Strings.isNullOrEmpty(line.scmRevision());
+  }
+}
index 8458630bd5cb3b0751f3a5afd1ccf739e0ad4463..fc8bc31bde0341123c3a2099b6affbc5098494ed 100644 (file)
@@ -22,7 +22,6 @@ package org.sonar.server.source.ws;
 import com.google.common.io.Resources;
 import org.apache.commons.lang.ObjectUtils;
 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;
@@ -35,7 +34,7 @@ import org.sonar.server.user.UserSession;
 
 import java.util.List;
 
-public class ShowAction implements RequestHandler {
+public class ShowAction implements SourcesAction {
 
   private final SourceService sourceService;
   private final DbClient dbClient;
@@ -45,7 +44,8 @@ public class ShowAction implements RequestHandler {
     this.dbClient = dbClient;
   }
 
-  void define(WebService.NewController controller) {
+  @Override
+  public void define(WebService.NewController controller) {
     WebService.NewAction action = controller.createAction("show")
       .setDescription("Get source code. Require See Source Code permission on file's project<br/>" +
         "Each element of the result array is composed of:" +
diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/ws/SourcesAction.java b/server/sonar-server/src/main/java/org/sonar/server/source/ws/SourcesAction.java
new file mode 100644 (file)
index 0000000..6561d60
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.source.ws;
+
+import org.sonar.api.server.ws.RequestHandler;
+import org.sonar.api.server.ws.WebService;
+
+public interface SourcesAction extends RequestHandler {
+
+  void define(WebService.NewController controller);
+
+}
index f87834b8714b81551171be8ff8aeb14a8495690e..e35c8f72b5c9e0446f6e03e65f28c0bdca5001d1 100644 (file)
@@ -24,18 +24,10 @@ import org.sonar.api.server.ws.WebService;
 
 public class SourcesWs implements WebService {
 
-  private final ShowAction showAction;
-  private final LinesAction linesAction;
-  private final RawAction rawAction;
-  private final HashAction hashAction;
-  private final IndexAction indexAction;
+  private final SourcesAction[] actions;
 
-  public SourcesWs(ShowAction showAction, RawAction rawAction, LinesAction linesAction, HashAction hashAction, IndexAction indexAction) {
-    this.showAction = showAction;
-    this.linesAction = linesAction;
-    this.rawAction = rawAction;
-    this.hashAction = hashAction;
-    this.indexAction = indexAction;
+  public SourcesWs(SourcesAction... actions) {
+    this.actions = actions;
   }
 
   @Override
@@ -43,11 +35,9 @@ public class SourcesWs implements WebService {
     NewController controller = context.createController("api/sources")
       .setSince("4.2")
       .setDescription("Display sources information");
-    showAction.define(controller);
-    linesAction.define(controller);
-    rawAction.define(controller);
-    hashAction.define(controller);
-    indexAction.define(controller);
+    for (SourcesAction action : actions) {
+      action.define(controller);
+    }
     controller.done();
   }
 }
index 513f3e4a17f58912b1087843b42b6aeb407ccbf5..c2a14f0246d9458cc4f886bdf5bdc0a6b14c2d3a 100644 (file)
@@ -1,7 +1,7 @@
 {
   "scm": [
-    [1, "julien", "2013-03-13"],
-    [2, "julien", "2013-03-14"],
-    [3, "simon", "2014-01-01"]
+    [1, "julien", "2013-03-13T12:34:56+0100", "a1e2b3e5d6f5"],
+    [2, "julien", "2013-03-14T13:17:22+0100", "b1e2b3e5d6f5"],
+    [3, "simon", "2014-01-01T15:35:36+0100", "c1e2b3e5d6f5"]
   ]
 }
index 5d74342ae998fcf9cb4f20dc839244b956ed38e2..416c1e59daec5a0fb9a9f790676762005466db72 100644 (file)
@@ -40,7 +40,6 @@ import org.sonar.test.DbTests;
 
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
-import static org.mockito.Mockito.mock;
 
 @RunWith(MockitoJUnitRunner.class)
 @Category(DbTests.class)
@@ -63,15 +62,7 @@ public class HashActionTest {
 
     DbClient dbClient = new DbClient(db.database(), db.myBatis(), new FileSourceDao(db.myBatis()), new ComponentDao());
 
-    tester = new WsTester(
-      new SourcesWs(
-        mock(ShowAction.class),
-        mock(RawAction.class),
-        mock(LinesAction.class),
-        new HashAction(dbClient),
-        mock(IndexAction.class)
-      )
-      );
+    tester = new WsTester(new SourcesWs(new HashAction(dbClient)));
   }
 
   @After
index efcb52582293d0087a49225032beee44671b5b96..521afdaa870be09fe02bbcf22a9690d61c060739 100644 (file)
@@ -38,7 +38,6 @@ import org.sonar.server.user.MockUserSession;
 import org.sonar.server.ws.WsTester;
 
 import static com.google.common.collect.Lists.newArrayList;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
@@ -66,8 +65,7 @@ public class IndexActionTest {
   public void setUp() throws Exception {
     when(dbClient.componentDao()).thenReturn(componentDao);
     when(dbClient.openSession(false)).thenReturn(session);
-    tester = new WsTester(new SourcesWs(mock(ShowAction.class), mock(RawAction.class), mock(LinesAction.class),
-      mock(HashAction.class), new IndexAction(dbClient, sourceService)));
+    tester = new WsTester(new SourcesWs(new IndexAction(dbClient, sourceService)));
   }
 
   @Test
index 91e73188d66fbc81cc1aa996efeb98947c05e8ba..7dbbbb30cfb9892658976ced0d33e6edea08377a 100644 (file)
@@ -46,10 +46,7 @@ import java.util.Date;
 import static com.google.common.collect.Lists.newArrayList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
+import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.when;
 
 @RunWith(MockitoJUnitRunner.class)
@@ -68,15 +65,7 @@ public class LinesActionTest {
 
   @Before
   public void setUp() throws Exception {
-    tester = new WsTester(
-      new SourcesWs(
-        mock(ShowAction.class),
-        mock(RawAction.class),
-        new LinesAction(sourceLineIndex, htmlSourceDecorator, componentService),
-        mock(HashAction.class),
-        mock(IndexAction.class)
-      )
-      );
+    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 {
index 28de2fb6f675bd471eea4433325ece9f7b5fecb3..7b50c879a40ca10270fad2fd82223d686aecbcd7 100644 (file)
@@ -38,7 +38,6 @@ import org.sonar.server.ws.WsTester;
 
 import static com.google.common.collect.Lists.newArrayList;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 @RunWith(MockitoJUnitRunner.class)
@@ -65,8 +64,7 @@ public class RawActionTest {
   public void setUp() throws Exception {
     when(dbClient.componentDao()).thenReturn(componentDao);
     when(dbClient.openSession(false)).thenReturn(session);
-    tester = new WsTester(new SourcesWs(mock(ShowAction.class), new RawAction(dbClient, sourceService), mock(LinesAction.class),
-      mock(HashAction.class), mock(IndexAction.class)));
+    tester = new WsTester(new SourcesWs(new RawAction(dbClient, sourceService)));
   }
 
   @Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/source/ws/ScmActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/source/ws/ScmActionTest.java
new file mode 100644 (file)
index 0000000..a4e1f6e
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * 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.source.ws;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonar.api.config.Settings;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.web.UserRole;
+import org.sonar.core.component.ComponentDto;
+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.source.index.SourceLineDoc;
+import org.sonar.server.source.index.SourceLineIndex;
+import org.sonar.server.source.index.SourceLineIndexDefinition;
+import org.sonar.server.user.MockUserSession;
+import org.sonar.server.ws.WsTester;
+
+import java.util.Date;
+
+public class ScmActionTest {
+
+  private static final String FILE_KEY = "FILE_KEY";
+  private static final String FILE_UUID = "FILE_A";
+  private static final String PROJECT_UUID = "PROJECT_A";
+
+  @ClassRule
+  public static DbTester dbTester = new DbTester();
+
+  @ClassRule
+  public static EsTester esTester = new EsTester().addDefinitions(new SourceLineIndexDefinition(new Settings()));
+
+  WsTester tester;
+
+  DbClient dbClient;
+
+  DbSession session;
+
+  @Before
+  public void setUp() throws Exception {
+    dbTester.truncateTables();
+    esTester.truncateIndices();
+    dbClient = new DbClient(dbTester.database(), dbTester.myBatis(), new ComponentDao());
+    session = dbClient.openSession(false);
+
+    tester = new WsTester(new SourcesWs(new ScmAction(dbClient, new SourceLineIndex(esTester.client()))));
+  }
+
+  @After
+  public void tearDown() throws Exception {
+    session.close();
+  }
+
+  @Test
+  public void show_scm() throws Exception {
+    initFile();
+    MockUserSession.set().addProjectUuidPermissions(UserRole.CODEVIEWER, PROJECT_UUID);
+
+    esTester.putDocuments(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE,
+      newSourceLine("julien", "123-456-789", DateUtils.parseDateTime("2015-03-30T12:34:56+0000"), 1)
+      );
+
+    WsTester.TestRequest request = tester.newGetRequest("api/sources", "scm").setParam("key", FILE_KEY);
+    request.execute().assertJson(getClass(), "show_scm.json");
+  }
+
+  @Test
+  public void show_scm_from_given_range_lines() throws Exception {
+    initFile();
+    MockUserSession.set().addProjectUuidPermissions(UserRole.CODEVIEWER, PROJECT_UUID);
+
+    esTester.putDocuments(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE,
+      newSourceLine("julien", "123-456-789", DateUtils.parseDateTime("2015-03-30T12:34:56+0000"), 1),
+      newSourceLine("julien", "123-456-789", DateUtils.parseDateTime("2015-03-30T12:34:56+0000"), 2),
+      newSourceLine("julien", "456-789-101", DateUtils.parseDateTime("2015-03-27T12:34:56+0000"), 3),
+      newSourceLine("simon", "789-101-112", DateUtils.parseDateTime("2015-03-31T12:34:56+0000"), 4)
+      );
+
+    WsTester.TestRequest request = tester.newGetRequest("api/sources", "scm").setParam("key", FILE_KEY).setParam("from", "2").setParam("to", "3");
+    request.execute().assertJson(getClass(), "show_scm_from_given_range_lines.json");
+  }
+
+  @Test
+  public void not_group_lines_by_commit() throws Exception {
+    initFile();
+    MockUserSession.set().addProjectUuidPermissions(UserRole.CODEVIEWER, PROJECT_UUID);
+
+    // lines 1 and 2 are the same commit, but not 3 (different date)
+    esTester.putDocuments(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE,
+      newSourceLine("julien", "123-456-789", DateUtils.parseDateTime("2015-03-30T12:34:56+0000"), 1),
+      newSourceLine("julien", "123-456-789", DateUtils.parseDateTime("2015-03-30T12:34:56+0000"), 2),
+      newSourceLine("julien", "456-789-101", DateUtils.parseDateTime("2015-03-27T12:34:56+0000"), 3),
+      newSourceLine("simon", "789-101-112", DateUtils.parseDateTime("2015-03-31T12:34:56+0000"), 4)
+      );
+
+    WsTester.TestRequest request = tester.newGetRequest("api/sources", "scm").setParam("key", FILE_KEY).setParam("commits_by_line", "true");
+    request.execute().assertJson(getClass(), "not_group_lines_by_commit.json");
+  }
+
+  @Test
+  public void group_lines_by_commit() throws Exception {
+    initFile();
+    MockUserSession.set().addProjectUuidPermissions(UserRole.CODEVIEWER, PROJECT_UUID);
+
+    // lines 1 and 2 are the same commit, but not 3 (different date)
+    esTester.putDocuments(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE,
+      newSourceLine("julien", "123-456-789", DateUtils.parseDateTime("2015-03-30T12:34:56+0000"), 1),
+      newSourceLine("julien", "123-456-789", DateUtils.parseDateTime("2015-03-30T12:34:56+0000"), 2),
+      newSourceLine("julien", "456-789-101", DateUtils.parseDateTime("2015-03-27T12:34:56+0000"), 3),
+      newSourceLine("simon", "789-101-112", DateUtils.parseDateTime("2015-03-31T12:34:56+0000"), 4)
+      );
+
+    WsTester.TestRequest request = tester.newGetRequest("api/sources", "scm").setParam("key", FILE_KEY).setParam("commits_by_line", "false");
+    request.execute().assertJson(getClass(), "group_lines_by_commit.json");
+  }
+
+  @Test
+  public void accept_negative_value_in_from_parameter() throws Exception {
+    initFile();
+    MockUserSession.set().addProjectUuidPermissions(UserRole.CODEVIEWER, PROJECT_UUID);
+
+    esTester.putDocuments(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE,
+      newSourceLine("julien", "123-456-789", DateUtils.parseDateTime("2015-03-30T12:34:56+0000"), 1),
+      newSourceLine("julien", "123-456-710", DateUtils.parseDateTime("2015-03-29T12:34:56+0000"), 2),
+      newSourceLine("julien", "456-789-101", DateUtils.parseDateTime("2015-03-27T12:34:56+0000"), 3),
+      newSourceLine("simon", "789-101-112", DateUtils.parseDateTime("2015-03-31T12:34:56+0000"), 4)
+    );
+
+    WsTester.TestRequest request = tester.newGetRequest("api/sources", "scm").setParam("key", FILE_KEY).setParam("from", "-2").setParam("to", "3");
+    request.execute().assertJson(getClass(), "accept_negative_value_in_from_parameter.json");
+  }
+
+  @Test
+  public void return_empty_value_when_no_scm() throws Exception {
+    initFile();
+    MockUserSession.set().addProjectUuidPermissions(UserRole.CODEVIEWER, PROJECT_UUID);
+
+    esTester.putDocuments(SourceLineIndexDefinition.INDEX, SourceLineIndexDefinition.TYPE,
+      newSourceLine(null, null, null, 1)
+    );
+
+    WsTester.TestRequest request = tester.newGetRequest("api/sources", "scm").setParam("key", FILE_KEY);
+    request.execute().assertJson(getClass(), "return_empty_value_when_no_scm.json");
+  }
+
+  @Test(expected = ForbiddenException.class)
+  public void fail_without_code_viewer_permission() throws Exception {
+    initFile();
+    MockUserSession.set().addProjectUuidPermissions(UserRole.USER, PROJECT_UUID);
+
+    WsTester.TestRequest request = tester.newGetRequest("api/sources", "scm").setParam("key", FILE_KEY);
+    request.execute();
+  }
+
+  private void initFile() {
+    ComponentDto project = ComponentTesting.newProjectDto(PROJECT_UUID);
+    dbClient.componentDao().insert(session, project, ComponentTesting.newFileDto(project, FILE_UUID).setKey(FILE_KEY));
+    session.commit();
+  }
+
+  private SourceLineDoc newSourceLine(String author, String revision, Date date, int line) {
+    return new SourceLineDoc()
+      .setScmAuthor(author)
+      .setScmRevision(revision)
+      .setScmDate(date)
+      .setLine(line)
+      .setProjectUuid(PROJECT_UUID)
+      .setFileUuid(FILE_UUID);
+  }
+}
index 4763f2f91f6fd50155d77d147fc0b81bc5adfb45..b8534b387976c78cf8b43317ed53cad63b5b1e85 100644 (file)
@@ -38,9 +38,7 @@ import org.sonar.server.ws.WsTester;
 import static com.google.common.collect.Lists.newArrayList;
 import static org.mockito.Matchers.anyInt;
 import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.*;
 
 @RunWith(MockitoJUnitRunner.class)
 public class ShowActionTest {
@@ -65,10 +63,7 @@ public class ShowActionTest {
   public void setUp() throws Exception {
     when(dbClient.componentDao()).thenReturn(componentDao);
     when(dbClient.openSession(false)).thenReturn(session);
-    tester = new WsTester(new SourcesWs(new ShowAction(sourceService, dbClient), mock(RawAction.class),
-      mock(LinesAction.class),
-      mock(HashAction.class),
-      mock(IndexAction.class)));
+    tester = new WsTester(new SourcesWs(new ShowAction(sourceService, dbClient)));
   }
 
   @Test
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/accept_negative_value_in_from_parameter.json b/server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/accept_negative_value_in_from_parameter.json
new file mode 100644 (file)
index 0000000..d2f4964
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "scm": [
+    [1, "julien", "2015-03-30T12:34:56+0000", "123-456-789"],
+    [2, "julien", "2015-03-29T12:34:56+0000", "123-456-710"],
+    [3, "julien", "2015-03-27T12:34:56+0000", "456-789-101"]
+  ]
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/group_lines_by_commit.json b/server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/group_lines_by_commit.json
new file mode 100644 (file)
index 0000000..48f0fea
--- /dev/null
@@ -0,0 +1,7 @@
+{
+  "scm": [
+    [1, "julien", "2015-03-30T12:34:56+0000", "123-456-789"],
+    [3, "julien", "2015-03-27T12:34:56+0000", "456-789-101"],
+    [4, "simon", "2015-03-31T12:34:56+0000", "789-101-112"]
+  ]
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/not_group_lines_by_commit.json b/server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/not_group_lines_by_commit.json
new file mode 100644 (file)
index 0000000..08ebdd2
--- /dev/null
@@ -0,0 +1,8 @@
+{
+  "scm": [
+    [1, "julien", "2015-03-30T12:34:56+0000", "123-456-789"],
+    [2, "julien", "2015-03-30T12:34:56+0000", "123-456-789"],
+    [3, "julien", "2015-03-27T12:34:56+0000", "456-789-101"],
+    [4, "simon", "2015-03-31T12:34:56+0000", "789-101-112"]
+  ]
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/return_empty_value_when_no_scm.json b/server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/return_empty_value_when_no_scm.json
new file mode 100644 (file)
index 0000000..6231167
--- /dev/null
@@ -0,0 +1,3 @@
+{
+  "scm": []
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/show_scm.json b/server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/show_scm.json
new file mode 100644 (file)
index 0000000..47f79ad
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "scm": [
+    [1, "julien", "2015-03-30T12:34:56+0000", "123-456-789"]
+  ]
+}
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/show_scm_from_given_range_lines.json b/server/sonar-server/src/test/resources/org/sonar/server/source/ws/ScmActionTest/show_scm_from_given_range_lines.json
new file mode 100644 (file)
index 0000000..737dda5
--- /dev/null
@@ -0,0 +1,6 @@
+{
+  "scm": [
+    [2, "julien", "2015-03-30T12:34:56+0000", "123-456-789"],
+    [3, "julien", "2015-03-27T12:34:56+0000", "456-789-101"]
+  ]
+}