aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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
-rw-r--r--sonar-db/src/main/java/org/sonar/db/component/ComponentLinkDao.java9
-rw-r--r--sonar-db/src/main/java/org/sonar/db/component/ComponentLinkMapper.java2
-rw-r--r--sonar-db/src/main/resources/org/sonar/db/component/ComponentLinkMapper.xml8
-rw-r--r--sonar-db/src/test/java/org/sonar/db/component/ComponentLinkDaoTest.java9
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/projectlinks/CreateWsRequest.java68
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/projectlinks/ProjectLinksWsParameters.java3
-rw-r--r--sonar-ws/src/main/protobuf/ws-projectlink.proto4
13 files changed, 488 insertions, 5 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);
+ }
}
diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentLinkDao.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentLinkDao.java
index ec7d9e33bf5..28fc47ee364 100644
--- a/sonar-db/src/main/java/org/sonar/db/component/ComponentLinkDao.java
+++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentLinkDao.java
@@ -20,6 +20,7 @@
package org.sonar.db.component;
import java.util.List;
+import javax.annotation.CheckForNull;
import org.sonar.db.Dao;
import org.sonar.db.DbSession;
@@ -35,8 +36,14 @@ public class ComponentLinkDao implements Dao {
return componentUuids.isEmpty() ? emptyList() : mapper(dbSession).selectByComponentUuids(componentUuids);
}
- public void insert(DbSession session, ComponentLinkDto dto) {
+ @CheckForNull
+ public ComponentLinkDto selectById(DbSession session, long id) {
+ return session.getMapper(ComponentLinkMapper.class).selectById(id);
+ }
+
+ public ComponentLinkDto insert(DbSession session, ComponentLinkDto dto) {
session.getMapper(ComponentLinkMapper.class).insert(dto);
+ return dto;
}
public void update(DbSession session, ComponentLinkDto dto) {
diff --git a/sonar-db/src/main/java/org/sonar/db/component/ComponentLinkMapper.java b/sonar-db/src/main/java/org/sonar/db/component/ComponentLinkMapper.java
index d8df30879d8..a7ff04b9508 100644
--- a/sonar-db/src/main/java/org/sonar/db/component/ComponentLinkMapper.java
+++ b/sonar-db/src/main/java/org/sonar/db/component/ComponentLinkMapper.java
@@ -28,6 +28,8 @@ public interface ComponentLinkMapper {
List<ComponentLinkDto> selectByComponentUuids(@Param("componentUuids") List<String> componentUuids);
+ ComponentLinkDto selectById(@Param("id") String id);
+
void insert(ComponentLinkDto dto);
void update(ComponentLinkDto dto);
diff --git a/sonar-db/src/main/resources/org/sonar/db/component/ComponentLinkMapper.xml b/sonar-db/src/main/resources/org/sonar/db/component/ComponentLinkMapper.xml
index bd1b90792dd..d74de88a667 100644
--- a/sonar-db/src/main/resources/org/sonar/db/component/ComponentLinkMapper.xml
+++ b/sonar-db/src/main/resources/org/sonar/db/component/ComponentLinkMapper.xml
@@ -33,6 +33,14 @@
order by p.id
</select>
+ <select id="selectById" parameterType="String" resultType="ComponentLink">
+ SELECT
+ <include refid="componentLinkColumns"/>
+ FROM project_links p
+ <where>
+ p.id=#{id}
+ </where>
+ </select>
<insert id="insert" parameterType="ComponentLink" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
INSERT INTO project_links (component_uuid, link_type, name, href)
diff --git a/sonar-db/src/test/java/org/sonar/db/component/ComponentLinkDaoTest.java b/sonar-db/src/test/java/org/sonar/db/component/ComponentLinkDaoTest.java
index 685079b6cfb..c24788ceabe 100644
--- a/sonar-db/src/test/java/org/sonar/db/component/ComponentLinkDaoTest.java
+++ b/sonar-db/src/test/java/org/sonar/db/component/ComponentLinkDaoTest.java
@@ -85,6 +85,15 @@ public class ComponentLinkDaoTest {
}
@Test
+ public void select_by_id() {
+ ComponentLinkDto link = underTest.insert(dbSession, newComponentLinkDto());
+ db.commit();
+
+ ComponentLinkDto candidate = underTest.selectById(dbSession, link.getId());
+ assertThat(candidate.getId()).isNotNull();
+ }
+
+ @Test
public void insert() {
db.prepareDbUnit(getClass(), "empty.xml");
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectlinks/CreateWsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectlinks/CreateWsRequest.java
new file mode 100644
index 00000000000..6d41e08e7a7
--- /dev/null
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectlinks/CreateWsRequest.java
@@ -0,0 +1,68 @@
+/*
+ * 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.sonarqube.ws.client.projectlinks;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+public class CreateWsRequest {
+ private String projectId;
+ private String projectKey;
+ private String name;
+ private String url;
+
+ @CheckForNull
+ public String getProjectId() {
+ return projectId;
+ }
+
+ public CreateWsRequest setProjectId(@Nullable String projectId) {
+ this.projectId = projectId;
+ return this;
+ }
+
+ @CheckForNull
+ public String getProjectKey() {
+ return projectKey;
+ }
+
+ public CreateWsRequest setProjectKey(@Nullable String projectKey) {
+ this.projectKey = projectKey;
+ return this;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public CreateWsRequest setName(String name) {
+ this.name = name;
+ return this;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public CreateWsRequest setUrl(String url) {
+ this.url = url;
+ return this;
+ }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectlinks/ProjectLinksWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectlinks/ProjectLinksWsParameters.java
index ea43c3fc466..2ae04134401 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectlinks/ProjectLinksWsParameters.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectlinks/ProjectLinksWsParameters.java
@@ -23,10 +23,13 @@ public class ProjectLinksWsParameters {
//actions
public static final String ACTION_SEARCH = "search";
+ public static final String ACTION_CREATE = "create";
// parameters
public static final String PARAM_PROJECT_ID = "projectId";
public static final String PARAM_PROJECT_KEY = "projectKey";
+ public static final String PARAM_NAME = "name";
+ public static final String PARAM_URL = "url";
private ProjectLinksWsParameters() {
// static utility class
diff --git a/sonar-ws/src/main/protobuf/ws-projectlink.proto b/sonar-ws/src/main/protobuf/ws-projectlink.proto
index 32e1d59d725..073b3e498b0 100644
--- a/sonar-ws/src/main/protobuf/ws-projectlink.proto
+++ b/sonar-ws/src/main/protobuf/ws-projectlink.proto
@@ -11,6 +11,10 @@ message SearchWsResponse {
repeated Link links = 1;
}
+message CreateWsResponse {
+ optional Link link = 1;
+}
+
message Link {
optional string id = 1;
optional string name = 2;