import org.sonar.server.source.DeprecatedSourceDecorator;
import org.sonar.server.source.HtmlSourceDecorator;
import org.sonar.server.source.SourceService;
-import org.sonar.server.source.ws.ScmAction;
-import org.sonar.server.source.ws.ScmWriter;
+import org.sonar.server.source.ws.*;
import org.sonar.server.source.ws.ShowAction;
-import org.sonar.server.source.ws.SourcesWs;
import org.sonar.server.startup.*;
import org.sonar.server.test.CoverageService;
import org.sonar.server.test.ws.*;
pico.addSingleton(SourcesWs.class);
pico.addSingleton(ShowAction.class);
pico.addSingleton(ScmWriter.class);
+ pico.addSingleton(RawAction.class);
pico.addSingleton(ScmAction.class);
// Duplications
public static final String JSON = "application/json";
public static final String XML = "application/xml";
+ public static final String TXT = "text/plain";
public static final String DEFAULT = "application/octet-stream";
private static final Map<String, String> MAP = new ImmutableMap.Builder<String, String>()
.put("png", "image/png")
.put("svg", "image/svg+xml")
.put("ico", "image/x-icon")
- .put("txt", "text/plain")
+ .put("txt", TXT)
.put("csv", "text/csv")
.put("properties", "text/plain")
.put("rtf", "text/rtf")
package org.sonar.server.source;
+import com.google.common.base.Splitter;
import org.sonar.api.ServerComponent;
import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.web.UserRole;
import org.sonar.core.measure.db.MeasureKey;
import org.sonar.core.persistence.DbSession;
import org.sonar.core.persistence.MyBatis;
+import org.sonar.core.source.db.SnapshotSourceDao;
import org.sonar.server.db.DbClient;
import org.sonar.server.user.UserSession;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import java.util.Collections;
import java.util.List;
+import static com.google.common.collect.Lists.newArrayList;
+
public class SourceService implements ServerComponent {
private final DbClient dbClient;
private final HtmlSourceDecorator sourceDecorator;
+ private final SnapshotSourceDao snapshotSourceDao;
/**
* Old service to colorize code
*/
private final DeprecatedSourceDecorator deprecatedSourceDecorator;
- public SourceService(DbClient dbClient, HtmlSourceDecorator sourceDecorator, DeprecatedSourceDecorator deprecatedSourceDecorator) {
+ public SourceService(DbClient dbClient, HtmlSourceDecorator sourceDecorator, SnapshotSourceDao snapshotSourceDao, DeprecatedSourceDecorator deprecatedSourceDecorator) {
this.dbClient = dbClient;
this.sourceDecorator = sourceDecorator;
+ this.snapshotSourceDao = snapshotSourceDao;
this.deprecatedSourceDecorator = deprecatedSourceDecorator;
}
return deprecatedSourceDecorator.getSourceAsHtml(fileKey, from, to);
}
+ public List<String> getLinesAsTxt(String fileKey) {
+ checkPermission(fileKey);
+
+ DbSession session = dbClient.openSession(false);
+ try {
+ String source = snapshotSourceDao.selectSnapshotSourceByComponentKey(fileKey, session);
+ if (source != null) {
+ return newArrayList(Splitter.onPattern("\r?\n|\r").split(source));
+ }
+ return Collections.emptyList();
+ } finally {
+ MyBatis.closeQuietly(session);
+ }
+ }
+
@CheckForNull
public String getScmAuthorData(String fileKey) {
checkPermission(fileKey);
MyBatis.closeQuietly(session);
}
}
-
}
--- /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.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.server.exceptions.NotFoundException;
+import org.sonar.server.source.SourceService;
+
+import java.util.List;
+
+public class RawAction implements RequestHandler {
+
+ private final SourceService sourceService;
+
+ public RawAction(SourceService sourceService) {
+ this.sourceService = sourceService;
+ }
+
+ void define(WebService.NewController controller) {
+ WebService.NewAction action = controller.createAction("raw")
+ .setDescription("Get source code as plain text. Require Browse permission on file's project")
+ .setSince("5.0")
+ .setResponseExample(Resources.getResource(getClass(), "example-raw.txt"))
+ .setHandler(this);
+
+ action
+ .createParam("key")
+ .setRequired(true)
+ .setDescription("File key")
+ .setExampleValue("my_project:/src/foo/Bar.php");
+ }
+
+ @Override
+ public void handle(Request request, Response response) {
+ String fileKey = request.mandatoryParam("key");
+ List<String> lines = sourceService.getLinesAsTxt(fileKey);
+ if (lines.isEmpty()) {
+ throw new NotFoundException("File '" + fileKey + "' has no sources");
+ }
+ response.newTxtWriter().values(lines).close();
+ }
+}
public class SourcesWs implements WebService {
private final ShowAction showAction;
+ private final RawAction rawAction;
private final ScmAction scmAction;
- public SourcesWs(ShowAction showAction, ScmAction scmAction) {
+ public SourcesWs(ShowAction showAction, RawAction rawAction, ScmAction scmAction) {
this.showAction = showAction;
+ this.rawAction = rawAction;
this.scmAction = scmAction;
}
.setSince("4.2")
.setDescription("Display sources information");
showAction.define(controller);
+ rawAction.define(controller);
scmAction.define(controller);
controller.done();
}
import org.sonar.api.server.ws.Response;
import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.utils.text.TxtWriter;
import org.sonar.api.utils.text.XmlWriter;
import org.sonar.server.plugins.MimeTypes;
import javax.annotation.CheckForNull;
+
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
return XmlWriter.of(new OutputStreamWriter(stream.output()));
}
+ @Override
+ public TxtWriter newTxtWriter() {
+ stream.setMediaType(MimeTypes.TXT);
+ return TxtWriter.of(new OutputStreamWriter(stream.output()));
+ }
+
@Override
public ServletStream stream() {
return stream;
--- /dev/null
+package org.sonar.check;
+
+public enum Priority {
+/**
+ * WARNING : DO NOT CHANGE THE ENUMERATION ORDER
+ * the enum ordinal is used for db persistence
+
import org.sonar.api.web.UserRole;
import org.sonar.core.measure.db.MeasureKey;
import org.sonar.core.persistence.DbSession;
+import org.sonar.core.source.db.SnapshotSourceDao;
import org.sonar.server.db.DbClient;
import org.sonar.server.exceptions.ForbiddenException;
import org.sonar.server.measure.persistence.MeasureDao;
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.*;
@Mock
HtmlSourceDecorator sourceDecorator;
+ @Mock
+ SnapshotSourceDao snapshotSourceDao;
+
@Mock
DeprecatedSourceDecorator deprecatedSourceDecorator;
DbClient dbClient = mock(DbClient.class);
when(dbClient.openSession(false)).thenReturn(session);
when(dbClient.measureDao()).thenReturn(measureDao);
- service = new SourceService(dbClient, sourceDecorator, deprecatedSourceDecorator);
+ service = new SourceService(dbClient, sourceDecorator, snapshotSourceDao, deprecatedSourceDecorator);
}
@Test
- public void get_lines() throws Exception {
+ public void get_html_lines() throws Exception {
MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY);
service.getLinesAsHtml(COMPONENT_KEY);
assertThat(service.getScmDateData(COMPONENT_KEY)).isNull();
}
+ @Test
+ public void get_txt_lines() throws Exception {
+ MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY);
+
+ when(snapshotSourceDao.selectSnapshotSourceByComponentKey(COMPONENT_KEY, session)).thenReturn("line1\nline2");
+
+ List<String> result = service.getLinesAsTxt(COMPONENT_KEY);
+ assertThat(result).contains("line1", "line2");
+ }
+
+ @Test
+ public void get_txt_lines_when_no_source() throws Exception {
+ MockUserSession.set().addComponentPermission(UserRole.CODEVIEWER, PROJECT_KEY, COMPONENT_KEY);
+
+ when(snapshotSourceDao.selectSnapshotSourceByComponentKey(COMPONENT_KEY, session)).thenReturn(null);
+
+ List<String> result = service.getLinesAsTxt(COMPONENT_KEY);
+ assertThat(result).isEmpty();
+ }
+
}
--- /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 org.junit.Test;
+import org.sonar.server.exceptions.NotFoundException;
+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.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class RawActionTest {
+
+ SourceService sourceService = mock(SourceService.class);
+ WsTester tester = new WsTester(new SourcesWs(mock(ShowAction.class), new RawAction(sourceService), mock(ScmAction.class)));
+
+ @Test
+ public void get_txt() throws Exception {
+ String fileKey = "src/Foo.java";
+ when(sourceService.getLinesAsTxt(fileKey)).thenReturn(newArrayList(
+ "public class HelloWorld {",
+ "}"
+ ));
+
+ WsTester.TestRequest request = tester.newGetRequest("api/sources", "raw").setParam("key", fileKey);
+ String result = request.execute().outputAsString();
+ assertThat(result).isEqualTo("public class HelloWorld {\r\n}\r\n");
+ }
+
+ @Test(expected = NotFoundException.class)
+ public void fail_to_get_txt_when_no_source() throws Exception {
+ String fileKey = "src/Foo.java";
+ when(sourceService.getLinesAsTxt(fileKey)).thenReturn(Collections.<String>emptyList());
+
+ WsTester.TestRequest request = tester.newGetRequest("api/sources", "raw").setParam("key", fileKey);
+ request.execute();
+ }
+
+}
import static org.mockito.Matchers.any;
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.*;
public class ScmActionTest {
SourceService sourceService = mock(SourceService.class);
ScmWriter scmWriter = mock(ScmWriter.class);
- WsTester tester = new WsTester(new SourcesWs(mock(ShowAction.class), new ScmAction(sourceService, scmWriter)));
+ WsTester tester = new WsTester(new SourcesWs(mock(ShowAction.class), mock(RawAction.class), new ScmAction(sourceService, scmWriter)));
@Test
public void get_scm() throws Exception {
@Before
public void setUp() throws Exception {
- tester = new WsTester(new SourcesWs(new ShowAction(sourceService), new ScmAction(sourceService, mock(ScmWriter.class))));
+ tester = new WsTester(new SourcesWs(new ShowAction(sourceService), mock(RawAction.class), new ScmAction(sourceService, mock(ScmWriter.class))));
}
@Test
public class SourcesWsTest {
ShowAction showAction = new ShowAction(mock(SourceService.class));
+ RawAction rawAction = new RawAction(mock(SourceService.class));
ScmAction scmAction = new ScmAction(mock(SourceService.class), new ScmWriter());
- WsTester tester = new WsTester(new SourcesWs(showAction, scmAction));
+ WsTester tester = new WsTester(new SourcesWs(showAction, rawAction, scmAction));
@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);
WebService.Action show = controller.action("show");
assertThat(show).isNotNull();
assertThat(show.responseExampleAsString()).isNotEmpty();
assertThat(show.params()).hasSize(3);
+ WebService.Action raw = controller.action("raw");
+ assertThat(raw).isNotNull();
+ assertThat(raw.handler()).isSameAs(rawAction);
+ assertThat(raw.since()).isEqualTo("5.0");
+ assertThat(raw.isInternal()).isFalse();
+ assertThat(raw.responseExampleAsString()).isNotEmpty();
+ assertThat(raw.params()).hasSize(1);
+
WebService.Action scm = controller.action("scm");
assertThat(scm).isNotNull();
assertThat(scm.handler()).isSameAs(scmAction);
import org.sonar.api.server.ws.WebService;
import org.sonar.api.server.ws.internal.ValidatingRequest;
import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.utils.text.TxtWriter;
import org.sonar.api.utils.text.XmlWriter;
import org.sonar.server.ws.WsTester.TestResponse.TestStream;
return XmlWriter.of(new OutputStreamWriter(output, Charsets.UTF_8));
}
+ @Override
+ public TxtWriter newTxtWriter() {
+ return TxtWriter.of(new OutputStreamWriter(output, Charsets.UTF_8));
+ }
+
@Override
public Stream stream() {
if (stream == null) {
package org.sonar.api.server.ws;
import org.sonar.api.utils.text.JsonWriter;
+import org.sonar.api.utils.text.TxtWriter;
import org.sonar.api.utils.text.XmlWriter;
import java.io.OutputStream;
XmlWriter newXmlWriter();
+ TxtWriter newTxtWriter();
+
Response noContent();
Stream stream();
--- /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.api.utils.text;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+
+import static com.google.common.collect.Lists.newArrayList;
+
+/**
+ * @since 5.0
+ */
+public class TxtWriter {
+
+ private final Writer writer;
+ private static final String LINE_SEPARATOR = "\r\n";
+
+ private TxtWriter(Writer writer) {
+ this.writer = writer;
+ }
+
+ public static TxtWriter of(Writer writer) {
+ return new TxtWriter(writer);
+ }
+
+ public TxtWriter values(List<String> values) {
+ for (String value : values) {
+ write(value);
+ write(LINE_SEPARATOR);
+ }
+ return this;
+ }
+
+ public TxtWriter values(String... values) {
+ return values(newArrayList(values));
+ }
+
+ public void close() {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ throw new WriterException("Fail to close TXT output", e);
+ }
+ }
+
+ private void write(String s) {
+ try {
+ writer.append(s);
+ } catch (IOException e) {
+ throw new WriterException("Fail to generate TXT with value: " + s, e);
+ }
+ }
+}
--- /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.api.utils.text;
+
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.StringReader;
+import java.io.StringWriter;
+
+import static org.fest.assertions.Assertions.assertThat;
+
+public class TxtWriterTest {
+
+ @Test
+ public void write_txt() throws Exception {
+ StringWriter output = new StringWriter();
+ TxtWriter writer = TxtWriter.of(output);
+
+ writer.values("France", "Paris");
+ writer.close();
+
+ BufferedReader reader = new BufferedReader(new StringReader(output.toString()));
+ String line1 = reader.readLine();
+ assertThat(line1).isEqualTo("France");
+
+ String line2 = reader.readLine();
+ assertThat(line2).isEqualTo("Paris");
+
+ assertThat(reader.readLine()).isNull();
+ }
+
+}