pico.addSingleton(SourceService.class);
pico.addSingleton(SourcesWs.class);
pico.addSingleton(ShowAction.class);
+ pico.addSingleton(IndexAction.class);
pico.addSingleton(ScmWriter.class);
pico.addSingleton(RawAction.class);
pico.addSingleton(ScmAction.class);
package org.sonar.server.source;
import com.google.common.base.Splitter;
+import org.elasticsearch.common.collect.Lists;
import org.sonar.api.ServerComponent;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.web.UserRole;
import org.sonar.core.persistence.MyBatis;
import org.sonar.core.source.db.SnapshotSourceDao;
import org.sonar.server.db.DbClient;
+import org.sonar.server.source.index.SourceLineDoc;
+import org.sonar.server.source.index.SourceLineIndex;
import org.sonar.server.user.UserSession;
import javax.annotation.CheckForNull;
private final DbClient dbClient;
private final HtmlSourceDecorator sourceDecorator;
private final SnapshotSourceDao snapshotSourceDao;
+ private final SourceLineIndex sourceLineIndex;
/**
* Old service to colorize code
*/
private final DeprecatedSourceDecorator deprecatedSourceDecorator;
- public SourceService(DbClient dbClient, HtmlSourceDecorator sourceDecorator, SnapshotSourceDao snapshotSourceDao, DeprecatedSourceDecorator deprecatedSourceDecorator) {
+ public SourceService(DbClient dbClient, HtmlSourceDecorator sourceDecorator, SnapshotSourceDao snapshotSourceDao, DeprecatedSourceDecorator deprecatedSourceDecorator,
+ SourceLineIndex sourceLineIndex) {
this.dbClient = dbClient;
this.sourceDecorator = sourceDecorator;
this.snapshotSourceDao = snapshotSourceDao;
this.deprecatedSourceDecorator = deprecatedSourceDecorator;
+ this.sourceLineIndex = sourceLineIndex;
}
@CheckForNull
return null;
}
+ /**
+ * Raw lines of source file.
+ */
+ public List<String> getLinesAsTxt(String fileUuid) {
+ List<String> lines = Lists.newArrayList();
+ for (SourceLineDoc lineDoc: sourceLineIndex.getLines(fileUuid, 1, Integer.MAX_VALUE)) {
+ lines.add(lineDoc.source());
+ }
+ return lines;
+ }
+
@CheckForNull
public String getScmAuthorData(String fileKey) {
checkPermission(fileKey);
import org.sonar.server.search.BaseDoc;
import org.sonar.server.search.BaseNormalizer;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
import java.util.Date;
import java.util.Map;
setField(SourceLineIndexDefinition.FIELD_LINE, line);
}
+ @CheckForNull
public String scmRevision() {
- return getField(SourceLineIndexDefinition.FIELD_SCM_REVISION);
+ return getNullableField(SourceLineIndexDefinition.FIELD_SCM_REVISION);
}
public void setScmRevision(String scmRevision) {
setField(SourceLineIndexDefinition.FIELD_SCM_REVISION, scmRevision);
}
+ @CheckForNull
public String scmAuthor() {
- return getField(SourceLineIndexDefinition.FIELD_SCM_AUTHOR);
+ return getNullableField(SourceLineIndexDefinition.FIELD_SCM_AUTHOR);
}
public void setScmAuthor(String scmAuthor) {
setField(SourceLineIndexDefinition.FIELD_SCM_AUTHOR, scmAuthor);
}
+ @CheckForNull
public Date scmDate() {
- return getField(SourceLineIndexDefinition.FIELD_SCM_DATE);
+ return getNullableField(SourceLineIndexDefinition.FIELD_SCM_DATE);
}
- public void setScmDate(Date scmDate) {
+ public void setScmDate(@Nullable Date scmDate) {
setField(SourceLineIndexDefinition.FIELD_SCM_DATE, scmDate);
}
*/
package org.sonar.server.source.index;
+import com.google.common.base.Preconditions;
+import org.elasticsearch.common.collect.Lists;
import org.elasticsearch.index.query.QueryBuilders;
+import org.elasticsearch.search.SearchHit;
+import org.elasticsearch.search.sort.SortOrder;
import org.sonar.api.ServerComponent;
import org.sonar.server.es.EsClient;
+import java.util.List;
+
public class SourceLineIndex implements ServerComponent {
private final EsClient esClient;
this.esClient = esClient;
}
+ /**
+ * Unindex all lines in file with UUID <code>fileUuid</code> above line <code>lastLine</code>
+ */
public void deleteLinesFromFileAbove(String fileUuid, int lastLine) {
esClient.prepareDeleteByQuery(SourceLineIndexDefinition.INDEX_SOURCE_LINES)
.setTypes(SourceLineIndexDefinition.TYPE_SOURCE_LINE)
.must(QueryBuilders.rangeQuery(SourceLineIndexDefinition.FIELD_LINE).gt(lastLine))
).get();
}
+
+ /**
+ * Get lines of code for file with UUID <code>fileUuid</code> with line numbers
+ * between <code>from</code> and <code>to</code> (both inclusive). Line numbers
+ * start at 1.
+ * @param fileUuid the UUID of the file for which to get source code
+ * @param from starting line; must be strictly positive
+ * @param to ending line; must be greater than or equal to <code>to</code>
+ */
+ public List<SourceLineDoc> getLines(String fileUuid, int from, int to) {
+ Preconditions.checkArgument(from > 0, "Minimum value for 'from' is 1");
+ Preconditions.checkArgument(to >= from, "'to' must be larger than or equal to 'from'");
+ List<SourceLineDoc> lines = Lists.newArrayList();
+
+ for (SearchHit hit: esClient.prepareSearch(SourceLineIndexDefinition.INDEX_SOURCE_LINES)
+ .setTypes(SourceLineIndexDefinition.TYPE_SOURCE_LINE)
+ .setSize(1 + to - from)
+ .setQuery(QueryBuilders.boolQuery()
+ .must(QueryBuilders.termQuery(SourceLineIndexDefinition.FIELD_FILE_UUID, fileUuid))
+ .must(QueryBuilders.rangeQuery(SourceLineIndexDefinition.FIELD_LINE)
+ .gte(from)
+ .lte(to)))
+ .addSort(SourceLineIndexDefinition.FIELD_LINE, SortOrder.ASC)
+ .get().getHits().getHits()) {
+ lines.add(new SourceLineDoc(hit.sourceAsMap()));
+ }
+
+ return lines;
+ }
}
--- /dev/null
+/*
+ * 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.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;
+import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.source.index.SourceLineDoc;
+import org.sonar.server.source.index.SourceLineIndex;
+
+import java.util.Date;
+import java.util.List;
+
+public class IndexAction implements RequestHandler {
+
+ private final SourceLineIndex sourceLineIndex;
+
+ public IndexAction(SourceLineIndex sourceLineIndex) {
+ this.sourceLineIndex = sourceLineIndex;
+ }
+
+ void define(WebService.NewController controller) {
+ WebService.NewAction action = controller.createAction("index")
+ .setDescription("Show source code with line oriented info. Require Browse permission on file's project<br/>" +
+ "Each element of the result array is an object which contains:" +
+ "<ol>" +
+ "<li>Line number</li>" +
+ "<li>Content of the line</li>" +
+ "<li>Author of the line (from SCM information)</li>" +
+ "<li>Revision of the line (from SCM information)</li>" +
+ "<li>Last commit date of the line (from SCM information)</li>" +
+ "</ol>")
+ .setSince("5.0")
+ .setInternal(true)
+ .setResponseExample(Resources.getResource(getClass(), "example-index.json"))
+ .setHandler(this);
+
+ action
+ .createParam("uuid")
+ .setRequired(true)
+ .setDescription("File uuid")
+ .setExampleValue("f333aab4-7e3a-4d70-87e1-f4c491f05e5c");
+
+ 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");
+ }
+
+ @Override
+ public void handle(Request request, Response response) {
+ String fileUuid = request.mandatoryParam("uuid");
+ 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);
+ if (sourceLines.isEmpty()) {
+ throw new NotFoundException("File '" + fileUuid + "' has no sources");
+ }
+
+ JsonWriter json = response.newJsonWriter().beginObject();
+ writeSource(sourceLines, from, json);
+
+ json.endObject().close();
+ }
+
+ private void writeSource(List<SourceLineDoc> lines, int from, JsonWriter json) {
+ json.name("sources").beginArray();
+ for (SourceLineDoc line: lines) {
+ json.beginObject()
+ .prop("line", line.line())
+ .prop("code", line.source())
+ .prop("scmAuthor", line.scmAuthor())
+ .prop("scmRevision", line.scmRevision());
+ Date scmDate = line.scmDate();
+ json.prop("scmDate", scmDate == null ? null : DateUtils.formatDateTime(scmDate))
+ .endObject();
+ }
+ json.endArray();
+ }
+}
public class SourcesWs implements WebService {
private final ShowAction showAction;
+ private final IndexAction show2Action;
private final RawAction rawAction;
private final ScmAction scmAction;
- public SourcesWs(ShowAction showAction, RawAction rawAction, ScmAction scmAction) {
+ public SourcesWs(ShowAction showAction, RawAction rawAction, ScmAction scmAction, IndexAction show2Action) {
this.showAction = showAction;
+ this.show2Action = show2Action;
this.rawAction = rawAction;
this.scmAction = scmAction;
}
.setSince("4.2")
.setDescription("Display sources information");
showAction.define(controller);
+ show2Action.define(controller);
rawAction.define(controller);
scmAction.define(controller);
controller.done();
--- /dev/null
+{
+ "sources": [
+ {"line": 20, "code": "<span class=\"k\">package </span>org.sonar.check;", "scmAuthor": "simon.brandhof@sonarsource.com", "scmDate": "2011-01-02T12:34:56+0100", "scmRevision":"a1e2b3e5d6f5"],
+ {"line": 21, "code":"", "scmAuthor": "simon.brandhof@sonarsource.com", "scmDate": "2011-01-02T12:34:56+0100", "scmRevision":"a1e2b3e5d6f5"],
+ {"line": 22, "code":"<span class=\"k\">public </span><span class=\"k\">enum </span><span class=\"sym-922 sym\">Priority</span> {", "scmAuthor": "simon.brandhof@sonarsource.com", "scmDate": "2011-01-02T12:34:56+0100", "scmRevision":"a1e2b3e5d6f5"],
+ {"line": 23, "code":" <span class=\"cppd\">/**</span>", "scmAuthor": "simon.brandhof@sonarsource.com", "scmDate": "2011-01-02T12:34:56+0100", "scmRevision":"a1e2b3e5d6f5"],
+ {"line": 24, "code":"<span class=\"cppd\"> * WARNING : DO NOT CHANGE THE ENUMERATION ORDER</span>", "scmAuthor": "simon.brandhof@sonarsource.com", "scmDate": "2011-01-02T12:34:56+0100", "scmRevision":"a1e2b3e5d6f5"],
+ {"line": 25, "code":"<span class=\"cppd\"> * the enum ordinal is used for db persistence</span>", "scmAuthor": "simon.brandhof@sonarsource.com", "scmDate": "2011-01-02T12:34:56+0100", "scmRevision":"a1e2b3e5d6f5"]
+ ]
+}
{
"sources": [
- [20, "<span class=\"k\">package </span>org.sonar.check;"],
- [21, ""],
- [22, "<span class=\"k\">public </span><span class=\"k\">enum </span><span class=\"sym-922 sym\">Priority</span> {"],
- [23, " <span class=\"cppd\">/**</span>"],
- [24, "<span class=\"cppd\"> * WARNING : DO NOT CHANGE THE ENUMERATION ORDER</span>"],
- [25, "<span class=\"cppd\"> * the enum ordinal is used for db persistence</span>"]
+ {"line": 20, "code": "<span class=\"k\">package </span>org.sonar.check;", "scmAuthor": "simon.brandhof@sonarsource.com", "scmDate": "2011-01-02T12:34:56+0100", ""],
+ {"line": 21, "code":""],
+ {"line": 22, "code":"<span class=\"k\">public </span><span class=\"k\">enum </span><span class=\"sym-922 sym\">Priority</span> {"],
+ {"line": 23, "code":" <span class=\"cppd\">/**</span>"],
+ {"line": 24, "code":"<span class=\"cppd\"> * WARNING : DO NOT CHANGE THE ENUMERATION ORDER</span>"],
+ {"line": 25, "code":"<span class=\"cppd\"> * the enum ordinal is used for db persistence</span>"]
]
}
package org.sonar.server.es;
import com.google.common.collect.Lists;
+import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.math.RandomUtils;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexResponse;
+import org.elasticsearch.action.bulk.BulkRequestBuilder;
+import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.sonar.api.config.Settings;
import org.sonar.api.platform.ComponentContainer;
import org.sonar.core.profiling.Profiling;
+import org.sonar.test.TestUtils;
+import java.io.FileInputStream;
import java.util.Collections;
import java.util.List;
.get();
}
+ public void putDocuments(String index, String type, Class<?> testClass, String... jsonPaths) throws Exception {
+ BulkRequestBuilder bulk = client.prepareBulk().setRefresh(true);
+ for (String path: jsonPaths) {
+ bulk.add(new IndexRequest(index, type).source(IOUtils.toString(
+ new FileInputStream(TestUtils.getResource(testClass, path)))));
+ }
+ bulk.get();
+ }
+
public Node node() {
return node;
}
import org.sonar.server.db.DbClient;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.measure.persistence.MeasureDao;
+import org.sonar.server.source.index.SourceLineIndex;
import org.sonar.server.user.MockUserSession;
import java.util.List;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
-import static org.mockito.Mockito.*;
+import static org.mockito.Matchers.any;
+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.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class SourceServiceTest {
@Mock
MeasureDao measureDao;
+ @Mock
+ SourceLineIndex sourceLineIndex;
+
static final String PROJECT_KEY = "org.sonar.sample";
static final String COMPONENT_KEY = "org.sonar.sample:Sample";
SourceService service;
+
@Before
public void setUp() throws Exception {
DbClient dbClient = mock(DbClient.class);
when(dbClient.openSession(false)).thenReturn(session);
when(dbClient.measureDao()).thenReturn(measureDao);
- service = new SourceService(dbClient, sourceDecorator, snapshotSourceDao, deprecatedSourceDecorator);
+ service = new SourceService(dbClient, sourceDecorator, snapshotSourceDao, deprecatedSourceDecorator, sourceLineIndex);
}
@Test
--- /dev/null
+/*
+ * 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.index;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.config.Settings;
+import org.sonar.server.es.EsTester;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class SourceLineIndexTest {
+
+ @Rule
+ public EsTester es = new EsTester().addDefinitions(new SourceLineIndexDefinition(new Settings()));
+
+ private SourceLineIndex index;
+
+ @Before
+ public void setUp() {
+ index = new SourceLineIndex(es.client());
+ }
+
+ @Test
+ public void should_retrieve_line_range() throws Exception {
+ es.putDocuments(SourceLineIndexDefinition.INDEX_SOURCE_LINES, SourceLineIndexDefinition.TYPE_SOURCE_LINE,
+ this.getClass(),
+ "file1_line1.json",
+ "file1_line2.json",
+ "file1_line3.json",
+ "file2_line1.json",
+ "file2_line2.json",
+ "file2_line3.json");
+ assertThat(index.getLines("file1", 1, 3)).hasSize(3);
+ assertThat(index.getLines("file1", 1, Integer.MAX_VALUE)).hasSize(3);
+ assertThat(index.getLines("file1", 2, 2)).hasSize(1);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void should_reject_from_less_than_1() {
+ index.getLines("polop", 0, 0);
+ }
+
+ @Test(expected = IllegalArgumentException.class)
+ public void should_reject_to_less_than_from() {
+ index.getLines("polop", 2, 1);
+ }
+}
--- /dev/null
+/*
+ * 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.collect.ImmutableMap;
+import org.elasticsearch.common.collect.Lists;
+import org.elasticsearch.common.collect.Maps;
+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.utils.DateUtils;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.search.BaseNormalizer;
+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.ws.WsTester;
+
+import java.util.Date;
+import java.util.Map;
+
+import static com.google.common.collect.Lists.newArrayList;
+import static org.fest.assertions.Assertions.assertThat;
+import static org.fest.assertions.Fail.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.Mockito.when;
+
+@RunWith(MockitoJUnitRunner.class)
+public class IndexActionTest {
+
+ @Mock
+ SourceLineIndex sourceLineIndex;
+
+ WsTester tester;
+
+ @Before
+ public void setUp() throws Exception {
+ tester = new WsTester(
+ new SourcesWs(
+ mock(ShowAction.class),
+ mock(RawAction.class),
+ mock(ScmAction.class),
+ new IndexAction(sourceLineIndex)
+ )
+ );
+ }
+
+ @Test
+ public void show_source() throws Exception {
+ String componentUuid = "efgh";
+ Date updatedAt = new Date();
+ Date scmDate = DateUtils.parseDateTime("2014-01-01T12:34:56+0100");
+ SourceLineDoc line1 = new SourceLineDoc(ImmutableMap.<String, Object>builder()
+ .put(SourceLineIndexDefinition.FIELD_PROJECT_UUID, "abcd")
+ .put(SourceLineIndexDefinition.FIELD_FILE_UUID, "efgh")
+ .put(SourceLineIndexDefinition.FIELD_LINE, 1)
+ .put(SourceLineIndexDefinition.FIELD_SCM_REVISION, "cafebabe")
+ .put(SourceLineIndexDefinition.FIELD_SCM_DATE, scmDate)
+ .put(SourceLineIndexDefinition.FIELD_SCM_AUTHOR, "polop")
+ .put(SourceLineIndexDefinition.FIELD_SOURCE, "class Polop {")
+ .put(BaseNormalizer.UPDATED_AT_FIELD, updatedAt)
+ .build());
+ SourceLineDoc line2 = new SourceLineDoc(ImmutableMap.<String, Object>builder()
+ .put(SourceLineIndexDefinition.FIELD_PROJECT_UUID, "abcd")
+ .put(SourceLineIndexDefinition.FIELD_FILE_UUID, "efgh")
+ .put(SourceLineIndexDefinition.FIELD_LINE, 2)
+ .put(SourceLineIndexDefinition.FIELD_SCM_REVISION, "cafebabe")
+ .put(SourceLineIndexDefinition.FIELD_SCM_DATE, scmDate)
+ .put(SourceLineIndexDefinition.FIELD_SCM_AUTHOR, "polop")
+ .put(SourceLineIndexDefinition.FIELD_SOURCE, " // Empty")
+ .put(BaseNormalizer.UPDATED_AT_FIELD, updatedAt)
+ .build());
+ SourceLineDoc line3 = new SourceLineDoc(ImmutableMap.<String, Object>builder()
+ .put(SourceLineIndexDefinition.FIELD_PROJECT_UUID, "abcd")
+ .put(SourceLineIndexDefinition.FIELD_FILE_UUID, "efgh")
+ .put(SourceLineIndexDefinition.FIELD_LINE, 3)
+ .put(SourceLineIndexDefinition.FIELD_SCM_REVISION, "cafebabe")
+ .put(SourceLineIndexDefinition.FIELD_SCM_DATE, scmDate)
+ .put(SourceLineIndexDefinition.FIELD_SCM_AUTHOR, "polop")
+ .put(SourceLineIndexDefinition.FIELD_SOURCE, "}")
+ .put(BaseNormalizer.UPDATED_AT_FIELD, updatedAt)
+ .build());
+ when(sourceLineIndex.getLines(eq(componentUuid), anyInt(), anyInt())).thenReturn(newArrayList(
+ line1,
+ line2,
+ line3
+ ));
+
+ WsTester.TestRequest request = tester.newGetRequest("api/sources", "index").setParam("uuid", componentUuid);
+ // Using non-strict match b/c of dates
+ request.execute().assertJson(getClass(), "show_source.json", false);
+ }
+
+ @Test
+ public void fail_to_show_source_if_no_source_found() throws Exception {
+ String componentKey = "src/Foo.java";
+ when(sourceLineIndex.getLines(anyString(), anyInt(), anyInt())).thenReturn(Lists.<SourceLineDoc>newArrayList());
+
+ try {
+ WsTester.TestRequest request = tester.newGetRequest("api/sources", "index").setParam("uuid", componentKey);
+ request.execute();
+ fail();
+ } catch (Exception e) {
+ assertThat(e).isInstanceOf(NotFoundException.class);
+ }
+ }
+
+ @Test
+ public void show_source_with_from_and_to_params() throws Exception {
+ String fileKey = "src/Foo.java";
+ Map<String, Object> fieldMap = Maps.newHashMap();
+ fieldMap.put(SourceLineIndexDefinition.FIELD_PROJECT_UUID, "abcd");
+ fieldMap.put(SourceLineIndexDefinition.FIELD_FILE_UUID, "efgh");
+ fieldMap.put(SourceLineIndexDefinition.FIELD_LINE, 3);
+ fieldMap.put(SourceLineIndexDefinition.FIELD_SCM_REVISION, "cafebabe");
+ fieldMap.put(SourceLineIndexDefinition.FIELD_SCM_DATE, null);
+ fieldMap.put(SourceLineIndexDefinition.FIELD_SCM_AUTHOR, "polop");
+ fieldMap.put(SourceLineIndexDefinition.FIELD_SOURCE, "}");
+ fieldMap.put(BaseNormalizer.UPDATED_AT_FIELD, new Date());
+ when(sourceLineIndex.getLines(fileKey, 3, 3)).thenReturn(newArrayList(
+ new SourceLineDoc(fieldMap)
+ ));
+ WsTester.TestRequest request = tester
+ .newGetRequest("api/sources", "index")
+ .setParam("uuid", fileKey)
+ .setParam("from", "3")
+ .setParam("to", "3");
+ request.execute().assertJson(getClass(), "show_source_with_params_from_and_to.json");
+ }
+}
import org.sonar.server.source.SourceService;
import org.sonar.server.ws.WsTester;
-import java.util.Collections;
-
import static com.google.common.collect.Lists.newArrayList;
import static org.fest.assertions.Assertions.assertThat;
import static org.fest.assertions.Fail.fail;
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(ScmAction.class)));
+ tester = new WsTester(new SourcesWs(mock(ShowAction.class), new RawAction(dbClient, sourceService), mock(ScmAction.class), mock(IndexAction.class)));
}
@Test
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
public class ScmActionTest {
SourceService sourceService = mock(SourceService.class);
ScmWriter scmWriter = mock(ScmWriter.class);
- WsTester tester = new WsTester(new SourcesWs(mock(ShowAction.class), mock(RawAction.class), new ScmAction(sourceService, scmWriter)));
+ WsTester tester = new WsTester(new SourcesWs(mock(ShowAction.class), mock(RawAction.class), new ScmAction(sourceService, scmWriter), mock(IndexAction.class)));
@Test
public void get_scm() throws Exception {
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.*;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
public class ShowActionTest {
@Before
public void setUp() throws Exception {
- tester = new WsTester(new SourcesWs(new ShowAction(sourceService), mock(RawAction.class), new ScmAction(sourceService, mock(ScmWriter.class))));
+ tester = new WsTester(new SourcesWs(new ShowAction(sourceService), mock(RawAction.class), new ScmAction(sourceService, mock(ScmWriter.class)), mock(IndexAction.class)));
}
@Test
import org.sonar.api.server.ws.WebService;
import org.sonar.server.db.DbClient;
import org.sonar.server.source.SourceService;
+import org.sonar.server.source.index.SourceLineIndex;
import org.sonar.server.ws.WsTester;
import static org.fest.assertions.Assertions.assertThat;
ShowAction showAction = new ShowAction(mock(SourceService.class));
RawAction rawAction = new RawAction(mock(DbClient.class), mock(SourceService.class));
ScmAction scmAction = new ScmAction(mock(SourceService.class), new ScmWriter());
- WsTester tester = new WsTester(new SourcesWs(showAction, rawAction, scmAction));
+ IndexAction indexAction = new IndexAction(mock(SourceLineIndex.class));
+ WsTester tester = new WsTester(new SourcesWs(showAction, rawAction, scmAction, indexAction));
@Test
public void define_ws() throws Exception {
assertThat(controller).isNotNull();
assertThat(controller.since()).isEqualTo("4.2");
assertThat(controller.description()).isNotEmpty();
- assertThat(controller.actions()).hasSize(3);
+ assertThat(controller.actions()).hasSize(4);
WebService.Action show = controller.action("show");
assertThat(show).isNotNull();
assertThat(scm.isInternal()).isFalse();
assertThat(scm.responseExampleAsString()).isNotEmpty();
assertThat(scm.params()).hasSize(4);
+
+ WebService.Action index = controller.action("index");
+ assertThat(index).isNotNull();
+ assertThat(index.handler()).isSameAs(indexAction);
+ assertThat(index.since()).isEqualTo("5.0");
+ assertThat(index.isInternal()).isTrue();
+ assertThat(index.responseExampleAsString()).isNotEmpty();
+ assertThat(index.params()).hasSize(3);
}
}
--- /dev/null
+{
+ "projectUuid": "abcd",
+ "fileUuid": "file1",
+ "line": 1,
+ "scmAuthor": "polop",
+ "scmDate": "2014-01-01T12:34:56.7+01:00",
+ "scmRevision": "cafebabe",
+ "source": "// Empty",
+ "updatedAt": "2014-01-01T23:45:01.8+01:00"
+}
\ No newline at end of file
--- /dev/null
+{
+ "projectUuid": "abcd",
+ "fileUuid": "file1",
+ "line": 2,
+ "scmAuthor": "polop",
+ "scmDate": "2014-01-01T12:34:56.7+01:00",
+ "scmRevision": "cafebabe",
+ "source": "// Empty",
+ "updatedAt": "2014-01-01T23:45:01.8+01:00"
+}
\ No newline at end of file
--- /dev/null
+{
+ "projectUuid": "abcd",
+ "fileUuid": "file1",
+ "line": 3,
+ "scmAuthor": "polop",
+ "scmDate": "2014-01-01T12:34:56.7+01:00",
+ "scmRevision": "cafebabe",
+ "source": "// Empty",
+ "updatedAt": "2014-01-01T23:45:01.8+01:00"
+}
\ No newline at end of file
--- /dev/null
+{
+ "projectUuid": "abcd",
+ "fileUuid": "file2",
+ "line": 1,
+ "scmAuthor": "polop",
+ "scmDate": "2014-01-01T12:34:56.7+01:00",
+ "scmRevision": "cafebabe",
+ "source": "// Empty",
+ "updatedAt": "2014-01-01T23:45:01.8+01:00"
+}
\ No newline at end of file
--- /dev/null
+{
+ "projectUuid": "abcd",
+ "fileUuid": "file2",
+ "line": 2,
+ "scmAuthor": "polop",
+ "scmDate": "2014-01-01T12:34:56.7+01:00",
+ "scmRevision": "cafebabe",
+ "source": "// Empty",
+ "updatedAt": "2014-01-01T23:45:01.8+01:00"
+}
\ No newline at end of file
--- /dev/null
+{
+ "projectUuid": "abcd",
+ "fileUuid": "file2",
+ "line": 3,
+ "scmAuthor": "polop",
+ "scmDate": "2014-01-01T12:34:56.7+01:00",
+ "scmRevision": "cafebabe",
+ "source": "// Empty",
+ "updatedAt": "2014-01-01T23:45:01.8+01:00"
+}
\ No newline at end of file
--- /dev/null
+{
+ "sources": [
+ {
+ "code": "class Polop {",
+ "line": 1,
+ "scmAuthor": "polop",
+ "scmRevision": "cafebabe"
+ },
+ {
+ "code": " // Empty",
+ "line": 2,
+ "scmAuthor": "polop",
+ "scmRevision": "cafebabe"
+ },
+ {
+ "code": "}",
+ "line": 3,
+ "scmAuthor": "polop",
+ "scmRevision": "cafebabe"
+ }
+ ]
+}
+
+
+
+
+
+
+
+
--- /dev/null
+{
+ "sources": [
+ {
+ "code": "}",
+ "line": 3,
+ "scmAuthor": "polop",
+ "scmRevision": "cafebabe"
+ }
+ ]
+}