diff options
author | Jacek <jacek.poreda@sonarsource.com> | 2020-04-02 11:52:09 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2020-04-15 20:03:38 +0000 |
commit | 613156f73891c1348fe01065ea0b6694e82d3f41 (patch) | |
tree | 8361e64d66bb89d22f808a89dd6c33d396ebb4f7 /server/sonar-webserver-webapi | |
parent | afa5ca444414f8c4352afb53528b9514d1c22c77 (diff) | |
download | sonarqube-613156f73891c1348fe01065ea0b6694e82d3f41.tar.gz sonarqube-613156f73891c1348fe01065ea0b6694e82d3f41.zip |
SONAR-13190 Support tags to application
Diffstat (limited to 'server/sonar-webserver-webapi')
6 files changed, 137 insertions, 85 deletions
diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java index a41c3857107..aeaf1cbe1cb 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java @@ -20,6 +20,7 @@ package org.sonar.server.component.ws; import com.google.common.collect.ImmutableSet; +import java.util.Arrays; import java.util.Objects; import java.util.Set; import javax.annotation.Nullable; @@ -69,9 +70,7 @@ class ComponentDtoToWsComponent { }); if (QUALIFIERS_WITH_VISIBILITY.contains(project.getQualifier())) { wsComponent.setVisibility(Visibility.getLabel(project.isPrivate())); - if (Qualifiers.PROJECT.equals(project.getQualifier())) { - wsComponent.getTagsBuilder().addAllTags(project.getTags()); - } + wsComponent.getTagsBuilder().addAllTags(project.getTags()); } return wsComponent; @@ -106,7 +105,7 @@ class ComponentDtoToWsComponent { }); if (QUALIFIERS_WITH_VISIBILITY.contains(dto.qualifier())) { wsComponent.setVisibility(Visibility.getLabel(dto.isPrivate())); - if (Qualifiers.PROJECT.equals(dto.qualifier()) && dto.getBranch() != null && parentProjectDto != null) { + if (Arrays.asList(Qualifiers.PROJECT, Qualifiers.APP).contains(dto.qualifier()) && dto.getBranch() != null && parentProjectDto != null) { wsComponent.getTagsBuilder().addAllTags(parentProjectDto.getTags()); } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/projecttag/TagsWsSupport.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/projecttag/TagsWsSupport.java new file mode 100644 index 00000000000..7177286749f --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/projecttag/TagsWsSupport.java @@ -0,0 +1,96 @@ +/* + * SonarQube + * Copyright (C) 2009-2020 SonarSource SA + * mailto:info 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.projecttag; + +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; +import org.apache.commons.lang.StringUtils; +import org.sonar.api.utils.System2; +import org.sonar.api.web.UserRole; +import org.sonar.core.util.stream.MoreCollectors; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.project.ProjectDto; +import org.sonar.server.component.ComponentFinder; +import org.sonar.server.es.ProjectIndexers; +import org.sonar.server.user.UserSession; + +import static java.util.Collections.singletonList; +import static org.sonar.server.es.ProjectIndexer.Cause.PROJECT_TAGS_UPDATE; +import static org.sonar.server.exceptions.BadRequestException.checkRequest; + +public class TagsWsSupport { + + private final DbClient dbClient; + private final ComponentFinder componentFinder; + private final UserSession userSession; + private final ProjectIndexers projectIndexers; + private final System2 system2; + + /** + * The characters allowed in project tags are lower-case + * letters, digits, plus (+), sharp (#), dash (-) and dot (.) + */ + private static final Pattern VALID_TAG_REGEXP = Pattern.compile("[a-z0-9+#\\-.]+$"); + + public TagsWsSupport(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, ProjectIndexers projectIndexers, System2 system2) { + this.dbClient = dbClient; + this.componentFinder = componentFinder; + this.userSession = userSession; + this.projectIndexers = projectIndexers; + this.system2 = system2; + } + + public void updateProjectTags(DbSession dbSession, String projectKey, List<String> providedTags) { + List<String> validatedTags = checkAndUnifyTags(providedTags); + ProjectDto project = componentFinder.getProjectByKey(dbSession, projectKey); + updateTagsForProjectsOrApplication(dbSession, validatedTags, project); + } + + public void updateApplicationTags(DbSession dbSession, String applicationKey, List<String> providedTags) { + List<String> validatedTags = checkAndUnifyTags(providedTags); + ProjectDto application = componentFinder.getApplicationByKey(dbSession, applicationKey); + updateTagsForProjectsOrApplication(dbSession, validatedTags, application); + } + + private void updateTagsForProjectsOrApplication(DbSession dbSession, List<String> tags, ProjectDto projectOrApplication) { + userSession.checkProjectPermission(UserRole.ADMIN, projectOrApplication); + projectOrApplication.setTags(tags); + projectOrApplication.setUpdatedAt(system2.now()); + dbClient.projectDao().updateTags(dbSession, projectOrApplication); + + projectIndexers.commitAndIndexProjects(dbSession, singletonList(projectOrApplication), PROJECT_TAGS_UPDATE); + } + + public static List<String> checkAndUnifyTags(List<String> tags) { + return tags.stream() + .filter(StringUtils::isNotBlank) + .map(t -> t.toLowerCase(Locale.ENGLISH)) + .map(TagsWsSupport::checkTag) + .distinct() + .collect(MoreCollectors.toList()); + } + + private static String checkTag(String tag) { + checkRequest(VALID_TAG_REGEXP.matcher(tag).matches(), "Tag '%s' is invalid. Tags accept only the characters: a-z, 0-9, '+', '-', '#', '.'", tag); + return tag; + } +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/projecttag/ws/ProjectTagsWsModule.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/projecttag/ws/ProjectTagsWsModule.java index 7e8a9b74093..4e3039f20b5 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/projecttag/ws/ProjectTagsWsModule.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/projecttag/ws/ProjectTagsWsModule.java @@ -20,11 +20,13 @@ package org.sonar.server.projecttag.ws; import org.sonar.core.platform.Module; +import org.sonar.server.projecttag.TagsWsSupport; public class ProjectTagsWsModule extends Module { @Override protected void configureModule() { add( + TagsWsSupport.class, ProjectTagsWs.class, SetAction.class, SearchAction.class diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/projecttag/ws/SetAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/projecttag/ws/SetAction.java index e1f71885ac2..7ab3ff39ff5 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/projecttag/ws/SetAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/projecttag/ws/SetAction.java @@ -20,48 +20,26 @@ package org.sonar.server.projecttag.ws; import java.util.List; -import java.util.Locale; -import org.apache.commons.lang.StringUtils; 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.System2; -import org.sonar.api.web.UserRole; -import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.DbClient; import org.sonar.db.DbSession; -import org.sonar.db.component.ComponentDto; -import org.sonar.db.project.ProjectDto; -import org.sonar.server.component.ComponentFinder; -import org.sonar.server.es.ProjectIndexers; -import org.sonar.server.user.UserSession; +import org.sonar.server.projecttag.TagsWsSupport; -import static java.util.Collections.singletonList; -import static org.sonar.server.es.ProjectIndexer.Cause.PROJECT_TAGS_UPDATE; -import static org.sonar.server.exceptions.BadRequestException.checkRequest; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; public class SetAction implements ProjectTagsWsAction { - /** - * The characters allowed in project tags are lower-case - * letters, digits, plus (+), sharp (#), dash (-) and dot (.) - */ - private static final String VALID_TAG_REGEXP = "[a-z0-9+#\\-.]+$"; + private static final String PARAM_PROJECT = "project"; private static final String PARAM_TAGS = "tags"; private final DbClient dbClient; - private final ComponentFinder componentFinder; - private final UserSession userSession; - private final ProjectIndexers projectIndexers; - private final System2 system2; + private final TagsWsSupport tagsWsSupport; - public SetAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, ProjectIndexers projectIndexers, System2 system2) { + public SetAction(DbClient dbClient, TagsWsSupport tagsWsSupport) { this.dbClient = dbClient; - this.componentFinder = componentFinder; - this.userSession = userSession; - this.projectIndexers = projectIndexers; - this.system2 = system2; + this.tagsWsSupport = tagsWsSupport; } @Override @@ -87,29 +65,12 @@ public class SetAction implements ProjectTagsWsAction { @Override public void handle(Request request, Response response) throws Exception { String projectKey = request.mandatoryParam(PARAM_PROJECT); - List<String> tags = request.mandatoryParamAsStrings(PARAM_TAGS).stream() - .filter(StringUtils::isNotBlank) - .map(t -> t.toLowerCase(Locale.ENGLISH)) - .map(SetAction::checkTag) - .distinct() - .collect(MoreCollectors.toList()); + List<String> tags = request.mandatoryParamAsStrings(PARAM_TAGS); try (DbSession dbSession = dbClient.openSession(false)) { - ProjectDto project = componentFinder.getProjectByKey(dbSession, projectKey); - userSession.checkProjectPermission(UserRole.ADMIN, project); - - project.setTags(tags); - project.setUpdatedAt(system2.now()); - dbClient.projectDao().updateTags(dbSession, project); - - projectIndexers.commitAndIndexProjects(dbSession, singletonList(project), PROJECT_TAGS_UPDATE); + tagsWsSupport.updateProjectTags(dbSession, projectKey, tags); } response.noContent(); } - - private static String checkTag(String tag) { - checkRequest(tag.matches(VALID_TAG_REGEXP), "Tag '%s' is invalid. Project tags accept only the characters: a-z, 0-9, '+', '-', '#', '.'", tag); - return tag; - } } diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/projecttag/ws/ProjectTagsWsModuleTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/projecttag/ws/ProjectTagsWsModuleTest.java index bba0d6dd7a6..b699135f0a5 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/projecttag/ws/ProjectTagsWsModuleTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/projecttag/ws/ProjectTagsWsModuleTest.java @@ -30,6 +30,6 @@ public class ProjectTagsWsModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new ProjectTagsWsModule().configure(container); - assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 3); + assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 4); } } diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/projecttag/ws/SetActionTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/projecttag/ws/SetActionTest.java index 0c1df5d6d79..3699e6ea46c 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/projecttag/ws/SetActionTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/projecttag/ws/SetActionTest.java @@ -23,7 +23,6 @@ import javax.annotation.Nullable; import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.rules.ExpectedException; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.System2; import org.sonar.api.web.UserRole; @@ -38,6 +37,7 @@ import org.sonar.server.es.TestProjectIndexers; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; +import org.sonar.server.projecttag.TagsWsSupport; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.TestResponse; @@ -47,13 +47,12 @@ import static java.lang.String.format; import static java.net.HttpURLConnection.HTTP_NO_CONTENT; import static java.util.Optional.ofNullable; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.component.ComponentTesting.newModuleDto; public class SetActionTest { @Rule - public ExpectedException expectedException = ExpectedException.none(); - @Rule public UserSessionRule userSession = UserSessionRule.standalone().logIn().setRoot(); @Rule public DbTester db = DbTester.create(); @@ -64,7 +63,9 @@ public class SetActionTest { private TestProjectIndexers projectIndexers = new TestProjectIndexers(); - private WsActionTester ws = new WsActionTester(new SetAction(dbClient, TestComponentFinder.from(db), userSession, projectIndexers, System2.INSTANCE)); + private TagsWsSupport tagsWsSupport = new TagsWsSupport(dbClient, TestComponentFinder.from(db), userSession, projectIndexers, System2.INSTANCE); + + private WsActionTester ws = new WsActionTester(new SetAction(dbClient, tagsWsSupport)); @Before public void setUp() { @@ -93,7 +94,8 @@ public class SetActionTest { @Test public void override_existing_tags() { - project = db.components().insertPrivateProjectDto(c -> {}, p -> p.setTagsString("marketing,languages")); + project = db.components().insertPrivateProjectDto(c -> { + }, p -> p.setTagsString("marketing,languages")); call(project.getKey(), "finance,offshore,platform"); @@ -118,43 +120,38 @@ public class SetActionTest { @Test public void fail_if_tag_does_not_respect_format() { - expectedException.expect(BadRequestException.class); - expectedException.expectMessage("_finance_' is invalid. Project tags accept only the characters: a-z, 0-9, '+', '-', '#', '.'"); - - call(project.getKey(), "_finance_"); + assertThatThrownBy(() -> call(project.getKey(), "_finance_")) + .isInstanceOf(BadRequestException.class) + .hasMessage("Tag '_finance_' is invalid. Tags accept only the characters: a-z, 0-9, '+', '-', '#', '.'"); } @Test public void fail_if_not_project_admin() { userSession.logIn().addProjectPermission(UserRole.USER, project); - expectedException.expect(ForbiddenException.class); - - call(project.getKey(), "platform"); + assertThatThrownBy(() -> call(project.getKey(), "platform")) + .isInstanceOf(ForbiddenException.class); } @Test public void fail_if_no_project() { - expectedException.expect(IllegalArgumentException.class); - - call(null, "platform"); + assertThatThrownBy(() -> call(null, "platform")) + .isInstanceOf(IllegalArgumentException.class); } @Test public void fail_if_no_tags() { - expectedException.expect(IllegalArgumentException.class); - - call(project.getKey(), null); + assertThatThrownBy(() -> call(project.getKey(), null)) + .isInstanceOf(IllegalArgumentException.class); } @Test public void fail_if_component_is_a_view() { ComponentDto view = db.components().insertView(v -> v.setDbKey("VIEW_KEY")); - expectedException.expect(NotFoundException.class); - expectedException.expectMessage("Project 'VIEW_KEY' not found"); - - call(view.getKey(), "point-of-view"); + assertThatThrownBy(() -> call(view.getKey(), "point-of-view")) + .isInstanceOf(NotFoundException.class) + .hasMessage("Project 'VIEW_KEY' not found"); } @Test @@ -162,10 +159,9 @@ public class SetActionTest { ComponentDto projectComponent = dbClient.componentDao().selectByUuid(dbSession, project.getUuid()).get(); ComponentDto module = db.components().insertComponent(newModuleDto(projectComponent).setDbKey("MODULE_KEY")); - expectedException.expect(NotFoundException.class); - expectedException.expectMessage("Project 'MODULE_KEY' not found"); - - call(module.getKey(), "modz"); + assertThatThrownBy(() -> call(module.getKey(), "modz")) + .isInstanceOf(NotFoundException.class) + .hasMessage("Project 'MODULE_KEY' not found"); } @Test @@ -173,10 +169,9 @@ public class SetActionTest { ComponentDto projectComponent = dbClient.componentDao().selectByUuid(dbSession, project.getUuid()).get(); ComponentDto file = db.components().insertComponent(newFileDto(projectComponent).setDbKey("FILE_KEY")); - expectedException.expect(NotFoundException.class); - expectedException.expectMessage("Project 'FILE_KEY' not found"); - - call(file.getKey(), "secret"); + assertThatThrownBy(() -> call(file.getKey(), "secret")) + .isInstanceOf(NotFoundException.class) + .hasMessage("Project 'FILE_KEY' not found"); } @Test @@ -186,10 +181,9 @@ public class SetActionTest { userSession.logIn().addProjectPermission(UserRole.USER, project); ComponentDto branch = db.components().insertProjectBranch(project); - expectedException.expect(NotFoundException.class); - expectedException.expectMessage(format("Project '%s' not found", branch.getDbKey())); - - call(branch.getDbKey(), "secret"); + assertThatThrownBy(() -> call(branch.getDbKey(), "secret")) + .isInstanceOf(NotFoundException.class) + .hasMessage(format("Project '%s' not found", branch.getDbKey())); } @Test |