From 1d702b6b507bd7c346381cf371d16fd8c556b99e Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 26 Jul 2016 17:21:33 +0200 Subject: [PATCH] SONAR-7926 Create WS to add project link --- .../server/projectlink/ws/CreateAction.java | 155 +++++++++++++ .../projectlink/ws/ProjectLinksModule.java | 3 +- .../server/projectlink/ws/create-example.json | 7 + .../projectlink/ws/CreateActionTest.java | 208 ++++++++++++++++++ .../ws/ProjectLinksWsModuleTest.java | 2 +- .../projectlink/ws/ProjectLinksWsTest.java | 15 +- .../sonar/db/component/ComponentLinkDao.java | 9 +- .../db/component/ComponentLinkMapper.java | 2 + .../db/component/ComponentLinkMapper.xml | 8 + .../db/component/ComponentLinkDaoTest.java | 9 + .../client/projectlinks/CreateWsRequest.java | 68 ++++++ .../ProjectLinksWsParameters.java | 3 + .../src/main/protobuf/ws-projectlink.proto | 4 + 13 files changed, 488 insertions(+), 5 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/CreateAction.java create mode 100644 server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/create-example.json create mode 100644 server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/projectlinks/CreateWsRequest.java 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.
" + + "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 selectByComponentUuids(@Param("componentUuids") List 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 + 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 @@ -84,6 +84,15 @@ public class ComponentLinkDaoTest { assertThat(result).isEmpty(); } + @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; -- 2.39.5