aboutsummaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2016-07-26 17:21:33 +0200
committerStas Vilchik <vilchiks@gmail.com>2016-07-28 17:31:14 +0200
commit1d702b6b507bd7c346381cf371d16fd8c556b99e (patch)
tree85d9a4a21dee50a8410477ea5eeb52157808737f /server
parentfa27bf616fad3cb743994ea8407feabd96a22ae9 (diff)
downloadsonarqube-1d702b6b507bd7c346381cf371d16fd8c556b99e.tar.gz
sonarqube-1d702b6b507bd7c346381cf371d16fd8c556b99e.zip
SONAR-7926 Create WS to add project link
Diffstat (limited to 'server')
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/CreateAction.java155
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksModule.java3
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/create-example.json7
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java208
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsModuleTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java15
6 files changed, 386 insertions, 4 deletions
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/CreateAction.java
new file mode 100644
index 00000000000..12186285a0d
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/CreateAction.java
@@ -0,0 +1,155 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.projectlink.ws;
+
+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.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentLinkDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.user.UserSession;
+import org.sonarqube.ws.WsProjectLinks;
+import org.sonarqube.ws.WsProjectLinks.CreateWsResponse;
+import org.sonarqube.ws.client.projectlinks.CreateWsRequest;
+
+import static org.sonar.core.util.Slug.slugify;
+import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import static org.sonarqube.ws.client.projectlinks.ProjectLinksWsParameters.ACTION_CREATE;
+import static org.sonarqube.ws.client.projectlinks.ProjectLinksWsParameters.PARAM_NAME;
+import static org.sonarqube.ws.client.projectlinks.ProjectLinksWsParameters.PARAM_PROJECT_ID;
+import static org.sonarqube.ws.client.projectlinks.ProjectLinksWsParameters.PARAM_PROJECT_KEY;
+import static org.sonarqube.ws.client.projectlinks.ProjectLinksWsParameters.PARAM_URL;
+
+public class CreateAction implements ProjectLinksWsAction {
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final ComponentFinder componentFinder;
+
+ public CreateAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder) {
+ this.dbClient = dbClient;
+ this.userSession = userSession;
+ this.componentFinder = componentFinder;
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context.createAction(ACTION_CREATE)
+ .setDescription("Create a new project link.<br>" +
+ "Requires 'Administer' permission on the specified project, " +
+ "or global 'Administer' permission.")
+ .setHandler(this)
+ .setPost(true)
+ .setResponseExample(getClass().getResource("create-example.json"))
+ .setSince("6.1");
+
+ action.createParam(PARAM_PROJECT_ID)
+ .setDescription("Project id")
+ .setExampleValue(UUID_EXAMPLE_01);
+
+ action.createParam(PARAM_PROJECT_KEY)
+ .setDescription("Project key")
+ .setExampleValue(KEY_PROJECT_EXAMPLE_001);
+
+ action.createParam(PARAM_NAME)
+ .setRequired(true)
+ .setDescription("Link name")
+ .setExampleValue("Custom");
+
+ action.createParam(PARAM_URL)
+ .setRequired(true)
+ .setDescription("Link url")
+ .setExampleValue("http://example.com");
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ CreateWsRequest searchWsRequest = toCreateWsRequest(request);
+ CreateWsResponse createWsResponse = doHandle(searchWsRequest);
+ writeProtobuf(createWsResponse, request, response);
+ }
+
+ private CreateWsResponse doHandle(CreateWsRequest createWsRequest) {
+ String name = createWsRequest.getName();
+ String url = createWsRequest.getUrl();
+
+ DbSession dbSession = dbClient.openSession(false);
+ try {
+ ComponentDto component = getComponentByUuidOrKey(dbSession, createWsRequest);
+
+ userSession.checkComponentUuidPermission(UserRole.ADMIN, component.uuid());
+
+ ComponentLinkDto link = new ComponentLinkDto()
+ .setComponentUuid(component.uuid())
+ .setName(name)
+ .setHref(url)
+ .setType(nameToType(name));
+ dbClient.componentLinkDao().insert(dbSession, link);
+
+ dbSession.commit();
+ return buildResponse(link);
+ } finally {
+ dbClient.closeSession(dbSession);
+ }
+ }
+
+ private static CreateWsResponse buildResponse(ComponentLinkDto link) {
+ CreateWsResponse.Builder response = CreateWsResponse.newBuilder();
+
+ WsProjectLinks.Link.Builder linkBuilder = WsProjectLinks.Link.newBuilder()
+ .setId(String.valueOf(link.getId()))
+ .setName(link.getName())
+ .setUrl(link.getHref());
+
+ String type = link.getType();
+ if (type != null) {
+ linkBuilder.setType(type);
+ }
+
+ response.setLink(linkBuilder);
+
+ return response.build();
+ }
+
+ private ComponentDto getComponentByUuidOrKey(DbSession dbSession, CreateWsRequest request) {
+ return componentFinder.getByUuidOrKey(
+ dbSession,
+ request.getProjectId(),
+ request.getProjectKey(),
+ ComponentFinder.ParamNames.PROJECT_ID_AND_KEY);
+ }
+
+ private static CreateWsRequest toCreateWsRequest(Request request) {
+ return new CreateWsRequest()
+ .setProjectId(request.param(PARAM_PROJECT_ID))
+ .setProjectKey(request.param(PARAM_PROJECT_KEY))
+ .setName(request.mandatoryParam(PARAM_NAME))
+ .setUrl(request.mandatoryParam(PARAM_URL));
+ }
+
+ private static String nameToType(String name) {
+ return slugify(name);
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksModule.java b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksModule.java
index 9a034e3729b..34bfa5a358d 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksModule.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksModule.java
@@ -28,7 +28,8 @@ public class ProjectLinksModule extends Module {
add(
ProjectLinksWs.class,
// actions
- SearchAction.class);
+ SearchAction.class,
+ CreateAction.class);
}
}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/create-example.json b/server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/create-example.json
new file mode 100644
index 00000000000..afe211f0fb9
--- /dev/null
+++ b/server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/create-example.json
@@ -0,0 +1,7 @@
+{
+ "link": {
+ "id": "18",
+ "name": "Custom",
+ "url": "http://example.org"
+ }
+} \ No newline at end of file
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java
new file mode 100644
index 00000000000..e68caa5acde
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java
@@ -0,0 +1,208 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program 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.
+ *
+ * This program 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.projectlink.ws;
+
+import java.io.IOException;
+import java.io.InputStream;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentLinkDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.ForbiddenException;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.WsProjectLinks;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.permission.GlobalPermissions.SYSTEM_ADMIN;
+import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.MediaTypes.PROTOBUF;
+import static org.sonarqube.ws.client.projectlinks.ProjectLinksWsParameters.PARAM_NAME;
+import static org.sonarqube.ws.client.projectlinks.ProjectLinksWsParameters.PARAM_PROJECT_ID;
+import static org.sonarqube.ws.client.projectlinks.ProjectLinksWsParameters.PARAM_PROJECT_KEY;
+import static org.sonarqube.ws.client.projectlinks.ProjectLinksWsParameters.PARAM_URL;
+
+public class CreateActionTest {
+
+ private final String PROJECT_KEY = KEY_PROJECT_EXAMPLE_001;
+ private final String PROJECT_UUID = UUID_EXAMPLE_01;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+ DbClient dbClient = db.getDbClient();
+ DbSession dbSession = db.getSession();
+
+ WsActionTester ws;
+
+ CreateAction underTest;
+
+ @Before
+ public void setUp() {
+ ComponentFinder componentFinder = new ComponentFinder(dbClient);
+ underTest = new CreateAction(dbClient, userSession, componentFinder);
+ ws = new WsActionTester(underTest);
+
+ userSession.login("login").setGlobalPermissions(SYSTEM_ADMIN);
+ }
+
+ @Test
+ public void example_with_key() {
+ insertProject();
+
+ String result = ws.newRequest()
+ .setMethod("POST")
+ .setParam(PARAM_PROJECT_KEY, PROJECT_KEY)
+ .setParam(PARAM_NAME, "Custom")
+ .setParam(PARAM_URL, "http://example.org")
+ .execute().getInput();
+
+ assertJson(result).ignoreFields("id").isSimilarTo(getClass().getResource("create-example.json"));
+ }
+
+ @Test
+ public void example_with_id() {
+ insertProject();
+
+ String result = ws.newRequest()
+ .setMethod("POST")
+ .setParam(PARAM_PROJECT_ID, PROJECT_UUID)
+ .setParam(PARAM_NAME, "Custom")
+ .setParam(PARAM_URL, "http://example.org")
+ .execute().getInput();
+
+ assertJson(result).ignoreFields("id").isSimilarTo(getClass().getResource("create-example.json"));
+ }
+
+ @Test
+ public void global_admin() throws IOException {
+ userSession.login("login").setGlobalPermissions(SYSTEM_ADMIN);
+ insertProject();
+ createAndTest();
+ }
+
+ @Test
+ public void project_admin() throws IOException {
+ userSession.login("login");
+ ComponentDto project = insertProject();
+ userSession.addProjectUuidPermissions(UserRole.ADMIN, project.uuid());
+ createAndTest();
+ }
+
+ @Test
+ public void fail_if_no_name() {
+ expectedException.expect(IllegalArgumentException.class);
+ ws.newRequest()
+ .setParam(PARAM_PROJECT_KEY, "unknown")
+ .setParam(PARAM_URL, "http://example.org")
+ .execute();
+ }
+
+ @Test
+ public void fail_if_no_url() {
+ expectedException.expect(IllegalArgumentException.class);
+ ws.newRequest()
+ .setParam(PARAM_PROJECT_KEY, "unknown")
+ .setParam(PARAM_NAME, "Custom")
+ .execute();
+ }
+
+ @Test
+ public void fail_when_no_project() {
+ expectedException.expect(NotFoundException.class);
+ ws.newRequest()
+ .setParam(PARAM_PROJECT_KEY, "unknown")
+ .setParam(PARAM_NAME, "Custom")
+ .setParam(PARAM_URL, "http://example.org")
+ .execute();
+ }
+
+ @Test
+ public void fail_if_anonymous() {
+ userSession.anonymous();
+ insertProject();
+
+ expectedException.expect(ForbiddenException.class);
+ ws.newRequest()
+ .setParam(PARAM_PROJECT_KEY, PROJECT_KEY)
+ .setParam(PARAM_NAME, "Custom")
+ .setParam(PARAM_URL, "http://example.org")
+ .execute();
+ }
+
+ @Test
+ public void fail_if_not_project_admin() {
+ userSession.login("login");
+ insertProject();
+
+ expectedException.expect(ForbiddenException.class);
+ ws.newRequest()
+ .setParam(PARAM_PROJECT_KEY, PROJECT_KEY)
+ .setParam(PARAM_NAME, "Custom")
+ .setParam(PARAM_URL, "http://example.org")
+ .execute();
+ }
+
+ private ComponentDto insertProject() {
+ ComponentDto project = new ComponentDto()
+ .setUuid(PROJECT_UUID)
+ .setKey(PROJECT_KEY)
+ .setUuidPath("")
+ .setRootUuid("");
+ dbClient.componentDao().insert(dbSession, project);
+ dbSession.commit();
+ return project;
+ }
+
+ private void createAndTest() throws IOException {
+ InputStream responseStream = ws.newRequest()
+ .setMethod("POST")
+ .setParam(PARAM_PROJECT_KEY, PROJECT_KEY)
+ .setParam(PARAM_NAME, "Custom")
+ .setParam(PARAM_URL, "http://example.org")
+ .setMediaType(PROTOBUF)
+ .execute().getInputStream();
+
+ WsProjectLinks.CreateWsResponse response = WsProjectLinks.CreateWsResponse.parseFrom(responseStream);
+
+ String newId = response.getLink().getId();
+
+ ComponentLinkDto link = dbClient.componentLinkDao().selectById(dbSession, newId);
+ assertThat(link.getName()).isEqualTo("Custom");
+ assertThat(link.getHref()).isEqualTo("http://example.org");
+ assertThat(link.getType()).isEqualTo("custom");
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsModuleTest.java
index 7514fbe35df..e63a38e15f3 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsModuleTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsModuleTest.java
@@ -30,6 +30,6 @@ public class ProjectLinksWsModuleTest {
public void verify_count_of_added_components() {
ComponentContainer container = new ComponentContainer();
new ProjectLinksModule().configure(container);
- assertThat(container.size()).isEqualTo(2 + 2);
+ assertThat(container.size()).isEqualTo(2 + 3);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java
index c35ba646fdf..edafecf8c89 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java
@@ -41,7 +41,8 @@ public class ProjectLinksWsTest {
@Before
public void setUp() {
WsTester tester = new WsTester(new ProjectLinksWs(
- new SearchAction(mock(DbClient.class), userSessionRule, mock(ComponentFinder.class))
+ new SearchAction(mock(DbClient.class), userSessionRule, mock(ComponentFinder.class)),
+ new CreateAction(mock(DbClient.class), userSessionRule, mock(ComponentFinder.class))
));
controller = tester.controller("api/project_links");
}
@@ -51,7 +52,7 @@ public class ProjectLinksWsTest {
assertThat(controller).isNotNull();
assertThat(controller.description()).isNotEmpty();
assertThat(controller.since()).isEqualTo("6.1");
- assertThat(controller.actions()).hasSize(1);
+ assertThat(controller.actions()).hasSize(2);
}
@Test
@@ -63,4 +64,14 @@ public class ProjectLinksWsTest {
assertThat(action.responseExampleAsString()).isNotEmpty();
assertThat(action.params()).hasSize(2);
}
+
+ @Test
+ public void define_create_action() {
+ WebService.Action action = controller.action("create");
+ assertThat(action).isNotNull();
+ assertThat(action.isPost()).isTrue();
+ assertThat(action.handler()).isNotNull();
+ assertThat(action.responseExampleAsString()).isNotEmpty();
+ assertThat(action.params()).hasSize(4);
+ }
}