Skip to content

Commit

Permalink
SONAR-6350 /api/sources/scm should read information from index
Browse files Browse the repository at this point in the history
  • Loading branch information
julienlancelot committed Mar 30, 2015
1 parent 97371fc commit ed7971e
Show file tree
Hide file tree
Showing 22 changed files with 445 additions and 72 deletions.
Expand Up @@ -601,6 +601,7 @@ void startLevel4Components(ComponentContainer pico) {
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);
Expand Down
Expand Up @@ -24,7 +24,6 @@
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;
Expand All @@ -37,15 +36,16 @@
import java.io.OutputStreamWriter;
import java.io.Reader;

public class HashAction implements RequestHandler {
public class HashAction implements SourcesAction {

private final DbClient dbClient;

public HashAction(DbClient dbClient) {
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")
Expand Down
Expand Up @@ -22,7 +22,6 @@

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;
Expand All @@ -35,7 +34,7 @@

import java.util.List;

public class IndexAction implements RequestHandler {
public class IndexAction implements SourcesAction {

private final DbClient dbClient;
private final SourceService sourceService;
Expand All @@ -45,7 +44,8 @@ public IndexAction(DbClient dbClient, SourceService sourceService) {
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")
Expand Down
Expand Up @@ -22,7 +22,6 @@
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;
Expand All @@ -39,7 +38,7 @@
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;
Expand All @@ -51,7 +50,8 @@ public LinesAction(SourceLineIndex sourceLineIndex, HtmlSourceDecorator htmlSour
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:" +
Expand Down Expand Up @@ -106,12 +106,12 @@ public void handle(Request request, Response response) {
}

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()
Expand Down
Expand Up @@ -24,7 +24,6 @@
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;
Expand All @@ -37,7 +36,7 @@
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;
Expand All @@ -47,7 +46,8 @@ public RawAction(DbClient dbClient, SourceService sourceService) {
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")
Expand Down
@@ -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());
}
}
Expand Up @@ -22,7 +22,6 @@
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;
Expand All @@ -35,7 +34,7 @@

import java.util.List;

public class ShowAction implements RequestHandler {
public class ShowAction implements SourcesAction {

private final SourceService sourceService;
private final DbClient dbClient;
Expand All @@ -45,7 +44,8 @@ public ShowAction(SourceService sourceService, DbClient dbClient) {
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:" +
Expand Down
@@ -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);

}
Expand Up @@ -24,30 +24,20 @@

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
public void define(Context context) {
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();
}
}
@@ -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"]
]
}

0 comments on commit ed7971e

Please sign in to comment.