From bddfadbad4375d8ce17ff76f0892290c80853f5b Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Thu, 3 Sep 2015 09:43:03 +0200 Subject: [PATCH] Fix api/sources/index --- .../platformlevel/PlatformLevel4.java | 2 + .../sonar/server/source/ws/IndexAction.java | 96 ++++++++++++++ .../server/source/ws/IndexActionTest.java | 124 ++++++++++++++++++ 3 files changed, 222 insertions(+) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/source/ws/IndexAction.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/source/ws/IndexActionTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 74deea4d98d..62a58ce92a4 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -272,6 +272,7 @@ import org.sonar.server.rule.ws.TagsAction; import org.sonar.server.source.HtmlSourceDecorator; import org.sonar.server.source.SourceService; import org.sonar.server.source.ws.HashAction; +import org.sonar.server.source.ws.IndexAction; import org.sonar.server.source.ws.LinesAction; import org.sonar.server.source.ws.RawAction; import org.sonar.server.source.ws.ScmAction; @@ -644,6 +645,7 @@ public class PlatformLevel4 extends PlatformLevel { LinesAction.class, HashAction.class, RawAction.class, + IndexAction.class, ScmAction.class, // Duplications diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/ws/IndexAction.java b/server/sonar-server/src/main/java/org/sonar/server/source/ws/IndexAction.java new file mode 100644 index 00000000000..6454f90f5a6 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/source/ws/IndexAction.java @@ -0,0 +1,96 @@ +/* + * 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.Optional; +import com.google.common.io.Resources; +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.text.JsonWriter; +import org.sonar.api.web.UserRole; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDto; +import org.sonar.server.component.ComponentFinder; +import org.sonar.server.source.SourceService; +import org.sonar.server.user.UserSession; + +public class IndexAction implements SourcesWsAction { + + private final DbClient dbClient; + private final SourceService sourceService; + private final UserSession userSession; + private final ComponentFinder componentFinder; + + public IndexAction(DbClient dbClient, SourceService sourceService, UserSession userSession, ComponentFinder componentFinder) { + this.dbClient = dbClient; + this.sourceService = sourceService; + this.userSession = userSession; + this.componentFinder = componentFinder; + } + + @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") + .setResponseExample(Resources.getResource(getClass(), "example-index.json")) + .setInternal(true) + .setHandler(this); + + action + .createParam("resource") + .setRequired(true) + .setDescription("File key") + .setExampleValue("my_project:/src/foo/Bar.php"); + + action + .createParam("from") + .setDefaultValue(1) + .setDescription("First line"); + + action + .createParam("to") + .setDescription("Last line (excluded). If not specified, all lines are returned until end of file"); + } + + @Override + public void handle(Request request, Response response) { + String fileKey = request.mandatoryParam("resource"); + userSession.checkComponentPermission(UserRole.CODEVIEWER, fileKey); + int from = request.mandatoryParamAsInt("from"); + Integer to = request.paramAsInt("to"); + try (DbSession session = dbClient.openSession(false)) { + ComponentDto componentDto = componentFinder.getByKey(session, fileKey); + Optional> lines = sourceService.getLinesAsRawText(session, componentDto.uuid(), from, to == null ? Integer.MAX_VALUE : to - 1); + JsonWriter json = response.newJsonWriter().beginArray().beginObject(); + if (lines.isPresent()) { + int lineCounter = from; + for (String line : lines.get()) { + json.prop(String.valueOf(lineCounter), line); + lineCounter++; + } + } + json.endObject().endArray().close(); + } + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/source/ws/IndexActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/source/ws/IndexActionTest.java new file mode 100644 index 00000000000..af90fc3aa5e --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/source/ws/IndexActionTest.java @@ -0,0 +1,124 @@ +/* + * 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.Optional; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.sonar.api.web.UserRole; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDao; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.ComponentTesting; +import org.sonar.server.component.ComponentFinder; +import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.source.SourceService; +import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.ws.WsTester; + +import static com.google.common.collect.Lists.newArrayList; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class IndexActionTest { + + @Rule + public UserSessionRule userSessionRule = UserSessionRule.standalone(); + + @Mock + DbClient dbClient; + + @Mock + DbSession session; + + @Mock + ComponentDao componentDao; + + @Mock + SourceService sourceService; + + WsTester tester; + + ComponentDto project = ComponentTesting.newProjectDto(); + ComponentDto file = ComponentTesting.newFileDto(project); + + @Before + public void setUp() { + when(dbClient.componentDao()).thenReturn(componentDao); + when(dbClient.openSession(false)).thenReturn(session); + tester = new WsTester(new SourcesWs(new IndexAction(dbClient, sourceService, userSessionRule, new ComponentFinder(dbClient)))); + } + + @Test + public void get_json() throws Exception { + String fileKey = "src/Foo.java"; + userSessionRule.addComponentPermission(UserRole.CODEVIEWER, "polop", fileKey); + when(componentDao.selectByKey(session, fileKey)).thenReturn(Optional.of(file)); + + when(sourceService.getLinesAsRawText(session, file.uuid(), 1, Integer.MAX_VALUE)).thenReturn(Optional.of((Iterable) newArrayList( + "public class HelloWorld {", + "}"))); + + WsTester.TestRequest request = tester.newGetRequest("api/sources", "index").setParam("resource", fileKey); + request.execute().assertJson(this.getClass(), "index-result.json"); + } + + @Test + public void limit_range() throws Exception { + String fileKey = "src/Foo.java"; + userSessionRule.addComponentPermission(UserRole.CODEVIEWER, "polop", fileKey); + when(componentDao.selectByKey(session, fileKey)).thenReturn(Optional.of(file)); + + when(sourceService.getLinesAsRawText(session, file.uuid(), 1, 2)).thenReturn(Optional.of((Iterable) newArrayList( + "public class HelloWorld {", + "}"))); + + WsTester.TestRequest request = tester.newGetRequest("api/sources", "index") + .setParam("resource", fileKey).setParam("from", "1").setParam("to", "3"); + request.execute().assertJson(this.getClass(), "index-result.json"); + } + + @Test(expected = ForbiddenException.class) + public void requires_code_viewer_permission() throws Exception { + tester.newGetRequest("api/sources", "index").setParam("resource", "any").execute(); + } + + @Test + public void close_db_session() throws Exception { + String fileKey = "src/Foo.java"; + userSessionRule.addComponentPermission(UserRole.CODEVIEWER, "polop", fileKey); + when(componentDao.selectByKey(session, fileKey)).thenReturn(Optional.absent()); + + WsTester.TestRequest request = tester.newGetRequest("api/sources", "index").setParam("resource", fileKey); + try { + request.execute(); + } catch (NotFoundException nfe) { + verify(session).close(); + } + } +} -- 2.39.5