diff options
author | Zipeng WU <zipeng.wu@sonarsource.com> | 2023-05-08 10:09:52 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-05-09 20:10:37 +0000 |
commit | 2c5ee5384d9f3e7bccee6260112f3cbd20d0f9ae (patch) | |
tree | ccddf48d508826cf3615d3d2f32c1532ad184328 | |
parent | 9e906a6151503e38c17a30529407be8f820aa158 (diff) | |
download | sonarqube-2c5ee5384d9f3e7bccee6260112f3cbd20d0f9ae.tar.gz sonarqube-2c5ee5384d9f3e7bccee6260112f3cbd20d0f9ae.zip |
SONAR-19103 Open documentation links safely in new tabs
18 files changed, 119 insertions, 212 deletions
diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/ListActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/ListActionIT.java index 89d17bf76b2..040bf225dcd 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/ListActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/ListActionIT.java @@ -40,13 +40,14 @@ import java.time.Instant; import java.util.Optional; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.sonar.api.platform.Server; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.DateUtils; import org.sonar.api.utils.System2; import org.sonar.api.web.UserRole; +import org.sonar.core.documentation.DocumentationLinkGenerator; import org.sonar.core.util.UuidFactoryFast; import org.sonar.db.DbClient; import org.sonar.db.DbTester; @@ -70,6 +71,9 @@ import org.sonarqube.ws.NewCodePeriods.ShowWSResponse; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import static org.sonar.db.component.BranchDto.DEFAULT_MAIN_BRANCH_NAME; import static org.sonar.db.component.SnapshotTesting.newAnalysis; @@ -84,12 +88,18 @@ import static org.sonar.db.component.SnapshotTesting.newAnalysis; private ComponentFinder componentFinder = TestComponentFinder.from(db); private NewCodePeriodDao dao = new NewCodePeriodDao(System2.INSTANCE, UuidFactoryFast.getInstance()); private NewCodePeriodDbTester tester = new NewCodePeriodDbTester(db); - private Server server = new NewCodeTestServer("9.9.0.65466"); - private ListAction underTest = new ListAction(dbClient, userSession, componentFinder, dao, server); - private WsActionTester ws = new WsActionTester(underTest); + private DocumentationLinkGenerator documentationLinkGenerator = mock(DocumentationLinkGenerator.class); + private WsActionTester ws; + + @Before + public void setup(){ + when(documentationLinkGenerator.getDocumentationLink(any())).thenReturn("https://docs.sonarqube.org/9.9/project-administration/defining-new-code/"); + ws = new WsActionTester(new ListAction(dbClient, userSession, componentFinder, dao, documentationLinkGenerator)); + } @Test public void test_definition() { + WebService.Action definition = ws.getDef(); assertThat(definition.description()).contains("https://docs.sonarqube.org/9.9/project-administration/defining-new-code/"); diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/NewCodeTestServer.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/NewCodeTestServer.java deleted file mode 100644 index 789098ae862..00000000000 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/NewCodeTestServer.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 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.newcodeperiod.ws; - -import java.util.Date; -import org.sonar.api.platform.Server; - -class NewCodeTestServer extends Server { - - private final String version; - - NewCodeTestServer(String version) { - this.version = version; - } - - @Override - public String getId() { - return null; - } - - @Override - public String getPermanentServerId() { - return null; - } - - @Override - public String getVersion() { - return version; - } - - @Override - public Date getStartedAt() { - return null; - } - - @Override - public String getContextPath() { - return null; - } - - @Override - public String getPublicRootUrl() { - return null; - } - - @Override - public boolean isSecured() { - return false; - } -} diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/SetActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/SetActionIT.java index 59cb2e96d01..20baefcfb20 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/SetActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/SetActionIT.java @@ -24,13 +24,14 @@ import com.tngtech.java.junit.dataprovider.DataProviderRunner; import com.tngtech.java.junit.dataprovider.UseDataProvider; import java.util.Optional; import javax.annotation.Nullable; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; -import org.sonar.api.platform.Server; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.System2; import org.sonar.api.web.UserRole; +import org.sonar.core.documentation.DocumentationLinkGenerator; import org.sonar.core.platform.EditionProvider; import org.sonar.core.platform.PlatformEditionProvider; import org.sonar.core.util.UuidFactoryFast; @@ -53,6 +54,7 @@ import org.sonar.server.ws.WsActionTester; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.entry; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.sonar.db.component.BranchDto.DEFAULT_MAIN_BRANCH_NAME; @@ -70,10 +72,14 @@ public class SetActionIT { private ComponentFinder componentFinder = TestComponentFinder.from(db); private PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class); private NewCodePeriodDao dao = new NewCodePeriodDao(System2.INSTANCE, UuidFactoryFast.getInstance()); - private Server server = new NewCodeTestServer("9.9.0.65466"); - private SetAction underTest = new SetAction(dbClient, userSession, componentFinder, editionProvider, dao, server); - private WsActionTester ws = new WsActionTester(underTest); + private DocumentationLinkGenerator documentationLinkGenerator = mock(DocumentationLinkGenerator.class); + private WsActionTester ws; + @Before + public void setup(){ + when(documentationLinkGenerator.getDocumentationLink(any())).thenReturn("https://docs.sonarqube.org/9.9/project-administration/defining-new-code/"); + ws = new WsActionTester(new SetAction(dbClient, userSession, componentFinder, editionProvider, dao, documentationLinkGenerator)); + } @Test public void test_definition() { diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/ShowActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/ShowActionIT.java index e5608c63afb..930e9fde621 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/ShowActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/ShowActionIT.java @@ -19,12 +19,13 @@ */ package org.sonar.server.newcodeperiod.ws; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.sonar.api.platform.Server; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.System2; import org.sonar.api.web.UserRole; +import org.sonar.core.documentation.DocumentationLinkGenerator; import org.sonar.core.util.UuidFactoryFast; import org.sonar.db.DbClient; import org.sonar.db.DbTester; @@ -44,6 +45,9 @@ import org.sonarqube.ws.NewCodePeriods.ShowWSResponse; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class ShowActionIT { @Rule @@ -56,9 +60,14 @@ public class ShowActionIT { private ComponentFinder componentFinder = TestComponentFinder.from(db); private NewCodePeriodDao dao = new NewCodePeriodDao(System2.INSTANCE, UuidFactoryFast.getInstance()); private NewCodePeriodDbTester tester = new NewCodePeriodDbTester(db); - private Server server = new NewCodeTestServer("10.1-SNAPSHOT"); - private ShowAction underTest = new ShowAction(dbClient, userSession, componentFinder, dao, server); - private WsActionTester ws = new WsActionTester(underTest); + private DocumentationLinkGenerator documentationLinkGenerator = mock(DocumentationLinkGenerator.class); + private WsActionTester ws; + + @Before + public void setup(){ + when(documentationLinkGenerator.getDocumentationLink(any())).thenReturn("https://docs.sonarqube.org/latest/project-administration/defining-new-code/"); + ws = new WsActionTester(new ShowAction(dbClient, userSession, componentFinder, dao, documentationLinkGenerator)); + } @Test public void test_definition() { diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/UnsetActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/UnsetActionIT.java index 6dee555fbff..c6e73d9de37 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/UnsetActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/ws/UnsetActionIT.java @@ -21,12 +21,13 @@ package org.sonar.server.newcodeperiod.ws; import java.util.Optional; import javax.annotation.Nullable; +import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.sonar.api.platform.Server; import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.System2; import org.sonar.api.web.UserRole; +import org.sonar.core.documentation.DocumentationLinkGenerator; import org.sonar.core.platform.EditionProvider; import org.sonar.core.platform.PlatformEditionProvider; import org.sonar.core.util.UuidFactoryFast; @@ -47,6 +48,7 @@ import org.sonar.server.ws.WsActionTester; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.Assertions.entry; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -62,9 +64,14 @@ public class UnsetActionIT { private ComponentFinder componentFinder = TestComponentFinder.from(db); private NewCodePeriodDao dao = new NewCodePeriodDao(System2.INSTANCE, UuidFactoryFast.getInstance()); private PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class); - private Server server = new NewCodeTestServer("9.9.0.65466"); - private UnsetAction underTest = new UnsetAction(dbClient, userSession, componentFinder, editionProvider, dao, server); - private WsActionTester ws = new WsActionTester(underTest); + private DocumentationLinkGenerator documentationLinkGenerator = mock(DocumentationLinkGenerator.class); + private WsActionTester ws; + + @Before + public void setup(){ + when(documentationLinkGenerator.getDocumentationLink(any())).thenReturn("https://docs.sonarqube.org/9.9/project-administration/defining-new-code/"); + ws = new WsActionTester(new UnsetAction(dbClient, userSession, componentFinder, editionProvider, dao, documentationLinkGenerator)); + } @Test public void test_definition() { diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ListAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ListAction.java index 001dace3c75..293cf23c422 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ListAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ListAction.java @@ -26,12 +26,12 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; import javax.annotation.Nullable; -import org.sonar.api.platform.Server; 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.DateUtils; import org.sonar.api.web.UserRole; +import org.sonar.core.documentation.DocumentationLinkGenerator; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.BranchDto; @@ -47,7 +47,7 @@ import org.sonarqube.ws.NewCodePeriods; import org.sonarqube.ws.NewCodePeriods.ListWSResponse; import static org.sonar.core.util.stream.MoreCollectors.toList; -import static org.sonar.server.newcodeperiod.ws.NewCodeActionSupport.getDocumentationUrl; +import static org.sonar.server.ws.WsUtils.createHtmlExternalLink; import static org.sonar.server.ws.WsUtils.writeProtobuf; import static org.sonarqube.ws.NewCodePeriods.ShowWSResponse.newBuilder; @@ -58,21 +58,22 @@ public class ListAction implements NewCodePeriodsWsAction { private final UserSession userSession; private final ComponentFinder componentFinder; private final NewCodePeriodDao newCodePeriodDao; - private final Server server; + private final String newCodeDefinitionDocumentationUrl; - public ListAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder, NewCodePeriodDao newCodePeriodDao, Server server) { + public ListAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder, NewCodePeriodDao newCodePeriodDao, + DocumentationLinkGenerator documentationLinkGenerator) { this.dbClient = dbClient; this.userSession = userSession; this.componentFinder = componentFinder; this.newCodePeriodDao = newCodePeriodDao; - this.server = server; + this.newCodeDefinitionDocumentationUrl = documentationLinkGenerator.getDocumentationLink("/project-administration/defining-new-code/"); } @Override public void define(WebService.NewController context) { WebService.NewAction action = context.createAction("list") - .setDescription("Lists the <a href=\"" + getDocumentationUrl(server.getVersion(), "project-administration/defining-new-code/") + - "\">new code definition</a> for all branches in a project.<br>" + + .setDescription("Lists the " + createHtmlExternalLink(newCodeDefinitionDocumentationUrl, "new code definition") + + " for all branches in a project.<br>" + "Requires the permission to browse the project") .setSince("8.0") .setResponseExample(getClass().getResource("list-example.json")) diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/NewCodeActionSupport.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/NewCodeActionSupport.java deleted file mode 100644 index ee4592d1a9a..00000000000 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/NewCodeActionSupport.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 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.newcodeperiod.ws; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import javax.annotation.Nonnull; - -class NewCodeActionSupport { - - private static final Pattern pattern = Pattern.compile("^(\\d{1,3}\\.\\d{1,3}).*$"); - - private NewCodeActionSupport() { - throw new UnsupportedOperationException("This class cannot be instantiated."); - } - - protected static String getDocumentationUrl(@Nonnull String version, @Nonnull String docPath) { - Matcher matcher = pattern.matcher(version); - boolean isSnapshot = version.contains("-SNAPSHOT"); - boolean isPreviousVersion = matcher.matches() && matcher.groupCount() == 1 && !isSnapshot; - String docVersion = isPreviousVersion ? matcher.group(1) : "latest"; - return String.format("https://docs.sonarqube.org/%s/%s", docVersion, docPath); - } - -} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/NewCodePeriodsWs.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/NewCodePeriodsWs.java index 5a90630b4a8..d4ef9bcb279 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/NewCodePeriodsWs.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/NewCodePeriodsWs.java @@ -20,19 +20,24 @@ package org.sonar.server.newcodeperiod.ws; import org.sonar.api.server.ws.WebService; +import org.sonar.core.documentation.DocumentationLinkGenerator; + +import static org.sonar.server.ws.WsUtils.createHtmlExternalLink; public class NewCodePeriodsWs implements WebService { private final NewCodePeriodsWsAction[] actions; + private final DocumentationLinkGenerator documentationLinkGenerator; - public NewCodePeriodsWs(NewCodePeriodsWsAction... actions) { + public NewCodePeriodsWs(DocumentationLinkGenerator documentationLinkGenerator, NewCodePeriodsWsAction... actions) { this.actions = actions; + this.documentationLinkGenerator = documentationLinkGenerator; } @Override public void define(Context context) { NewController controller = context.createController("api/new_code_periods") - .setDescription("Manage new code definitions.") + .setDescription("Manage "+ createHtmlExternalLink(documentationLinkGenerator.getDocumentationLink("/project-administration/defining-new-code/"), "new code definition") +".") .setSince("8.0"); for (NewCodePeriodsWsAction action : actions) { action.define(controller); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/SetAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/SetAction.java index 79683db81cf..317b09c3ffc 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/SetAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/SetAction.java @@ -24,11 +24,11 @@ import java.util.EnumSet; import java.util.Locale; import java.util.Set; import javax.annotation.Nullable; -import org.sonar.api.platform.Server; 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.core.documentation.DocumentationLinkGenerator; import org.sonar.core.platform.EditionProvider; import org.sonar.core.platform.PlatformEditionProvider; import org.sonar.db.DbClient; @@ -50,7 +50,7 @@ import static org.sonar.db.newcodeperiod.NewCodePeriodType.NUMBER_OF_DAYS; import static org.sonar.db.newcodeperiod.NewCodePeriodType.PREVIOUS_VERSION; import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH; import static org.sonar.db.newcodeperiod.NewCodePeriodType.SPECIFIC_ANALYSIS; -import static org.sonar.server.newcodeperiod.ws.NewCodeActionSupport.getDocumentationUrl; +import static org.sonar.server.ws.WsUtils.createHtmlExternalLink; public class SetAction implements NewCodePeriodsWsAction { private static final String PARAM_BRANCH = "branch"; @@ -71,24 +71,24 @@ public class SetAction implements NewCodePeriodsWsAction { private final ComponentFinder componentFinder; private final PlatformEditionProvider editionProvider; private final NewCodePeriodDao newCodePeriodDao; - private final Server server; + private final String newCodeDefinitionDocumentationUrl; public SetAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder, PlatformEditionProvider editionProvider, - NewCodePeriodDao newCodePeriodDao, Server server) { + NewCodePeriodDao newCodePeriodDao, DocumentationLinkGenerator documentationLinkGenerator) { this.dbClient = dbClient; this.userSession = userSession; this.componentFinder = componentFinder; this.editionProvider = editionProvider; this.newCodePeriodDao = newCodePeriodDao; - this.server = server; + this.newCodeDefinitionDocumentationUrl = documentationLinkGenerator.getDocumentationLink("/project-administration/defining-new-code/"); } @Override public void define(WebService.NewController context) { WebService.NewAction action = context.createAction("set") .setPost(true) - .setDescription("Updates the <a href=\"" + getDocumentationUrl(server.getVersion(), "project-administration/defining-new-code/") + - "\">new code definition</a> on different levels:<br>" + + .setDescription("Updates the " + createHtmlExternalLink(newCodeDefinitionDocumentationUrl, "new code definition") + + " on different levels:<br>" + BEGIN_LIST + BEGIN_ITEM_LIST + "Not providing a project key and a branch key will update the default value at global level. " + "Existing projects or branches having a specific new code definition will not be impacted" + END_ITEM_LIST + diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ShowAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ShowAction.java index dd5111679ae..b47d88c9528 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ShowAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/ShowAction.java @@ -21,11 +21,11 @@ package org.sonar.server.newcodeperiod.ws; import java.util.Optional; import javax.annotation.Nullable; -import org.sonar.api.platform.Server; 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.core.documentation.DocumentationLinkGenerator; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.BranchDto; @@ -40,8 +40,8 @@ import org.sonarqube.ws.NewCodePeriods; import static java.lang.String.format; import static org.sonar.db.permission.GlobalPermission.SCAN; -import static org.sonar.server.newcodeperiod.ws.NewCodeActionSupport.getDocumentationUrl; import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException; +import static org.sonar.server.ws.WsUtils.createHtmlExternalLink; import static org.sonar.server.ws.WsUtils.writeProtobuf; import static org.sonarqube.ws.NewCodePeriods.ShowWSResponse; @@ -53,22 +53,22 @@ public class ShowAction implements NewCodePeriodsWsAction { private final UserSession userSession; private final ComponentFinder componentFinder; private final NewCodePeriodDao newCodePeriodDao; - private final Server server; + private final String newCodeDefinitionDocumentationUrl; - public ShowAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder, NewCodePeriodDao newCodePeriodDao, Server server) { + public ShowAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder, NewCodePeriodDao newCodePeriodDao, + DocumentationLinkGenerator documentationLinkGenerator) { this.dbClient = dbClient; this.userSession = userSession; this.componentFinder = componentFinder; this.newCodePeriodDao = newCodePeriodDao; - this.server = server; + this.newCodeDefinitionDocumentationUrl = documentationLinkGenerator.getDocumentationLink("/project-administration/defining-new-code/"); } @Override public void define(WebService.NewController context) { WebService.NewAction action = context.createAction("show") - .setDescription("Shows the <a href=\"" + getDocumentationUrl(server.getVersion(), "project-administration/defining-new-code/") + - "\">new code definition</a>.<br> " - + "If the component requested doesn't exist or if no new code definition is set for it, a value is inherited from the project or from the global setting." + + .setDescription("Shows the " + createHtmlExternalLink(newCodeDefinitionDocumentationUrl, "new code definition") + ".<br> " + + "If the component requested doesn't exist or if no new code definition is set for it, a value is inherited from the project or from the global setting." + "Requires one of the following permissions if a component is specified: " + "<ul>" + "<li>'Administer' rights on the specified component</li>" + diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/UnsetAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/UnsetAction.java index dd290bb50f8..0b313178a37 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/UnsetAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/ws/UnsetAction.java @@ -19,11 +19,11 @@ */ package org.sonar.server.newcodeperiod.ws; -import org.sonar.api.platform.Server; 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.core.documentation.DocumentationLinkGenerator; import org.sonar.core.platform.EditionProvider; import org.sonar.core.platform.PlatformEditionProvider; import org.sonar.db.DbClient; @@ -36,7 +36,7 @@ import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.user.UserSession; import static java.lang.String.format; -import static org.sonar.server.newcodeperiod.ws.NewCodeActionSupport.getDocumentationUrl; +import static org.sonar.server.ws.WsUtils.createHtmlExternalLink; public class UnsetAction implements NewCodePeriodsWsAction { private static final String PARAM_BRANCH = "branch"; @@ -47,24 +47,24 @@ public class UnsetAction implements NewCodePeriodsWsAction { private final ComponentFinder componentFinder; private final PlatformEditionProvider editionProvider; private final NewCodePeriodDao newCodePeriodDao; - private final Server server; + private final String newCodeDefinitionDocumentationUrl; public UnsetAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder, - PlatformEditionProvider editionProvider, NewCodePeriodDao newCodePeriodDao, Server server) { + PlatformEditionProvider editionProvider, NewCodePeriodDao newCodePeriodDao, DocumentationLinkGenerator documentationLinkGenerator) { this.dbClient = dbClient; this.userSession = userSession; this.componentFinder = componentFinder; this.editionProvider = editionProvider; this.newCodePeriodDao = newCodePeriodDao; - this.server = server; + this.newCodeDefinitionDocumentationUrl = documentationLinkGenerator.getDocumentationLink("/project-administration/defining-new-code/"); } @Override public void define(WebService.NewController context) { WebService.NewAction action = context.createAction("unset") .setPost(true) - .setDescription("Unsets the <a href=\"" + getDocumentationUrl(server.getVersion(), "project-administration/defining-new-code/") + - "\">new code definition</a> for a branch, project or global.<br>" + + .setDescription("Unsets the " + createHtmlExternalLink(newCodeDefinitionDocumentationUrl, "new code definition") + + " for a branch, project or global.<br>" + "Requires one of the following permissions: " + "<ul>" + "<li>'Administer System' to change the global setting</li>" + diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/webhook/ws/DeleteAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/webhook/ws/DeleteAction.java index 212d19cb7dc..187ac7408e9 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/webhook/ws/DeleteAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/webhook/ws/DeleteAction.java @@ -29,12 +29,12 @@ import org.sonar.db.project.ProjectDto; import org.sonar.db.webhook.WebhookDto; import org.sonar.server.user.UserSession; +import static java.lang.String.format; import static org.sonar.server.exceptions.NotFoundException.checkFoundWithOptional; import static org.sonar.server.webhook.ws.WebhooksWsParameters.DELETE_ACTION; import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM; import static org.sonar.server.webhook.ws.WebhooksWsParameters.KEY_PARAM_MAXIMUM_LENGTH; import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001; -import static org.sonar.server.ws.WsUtils.checkStateWithOptional; public class DeleteAction implements WebhooksWsAction { @@ -78,7 +78,7 @@ public class DeleteAction implements WebhooksWsAction { String projectUuid = webhookDto.getProjectUuid(); if (projectUuid != null) { Optional<ProjectDto> optionalDto = dbClient.projectDao().selectByUuid(dbSession, projectUuid); - ProjectDto projectDto = checkStateWithOptional(optionalDto, "the requested project '%s' was not found", projectUuid); + ProjectDto projectDto = optionalDto.orElseThrow(() -> new IllegalStateException(format("the requested project '%s' was not found", projectUuid))); webhookSupport.checkPermission(projectDto); deleteWebhook(dbSession, webhookDto); } else { diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/NewCodeActionSupportTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/NewCodeActionSupportTest.java deleted file mode 100644 index 552008e872c..00000000000 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/NewCodeActionSupportTest.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2023 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.newcodeperiod.ws; - -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.sonar.server.newcodeperiod.ws.NewCodeActionSupport.getDocumentationUrl; - -public class NewCodeActionSupportTest { - private static final String LATEST = "https://docs.sonarqube.org/latest/project-administration/defining-new-code/"; - - @Test - public void check_documentation_url_generation() { - assertThat(getDocumentationUrl("10.1-SNAPSHOT", "project-administration/defining-new-code/")).isEqualTo(LATEST); - assertThat(getDocumentationUrl("10.1.0.654666-SNAPSHOT", "project-administration/defining-new-code/")).isEqualTo(LATEST); - assertThat(getDocumentationUrl("X.X.10.WHATEVER", "project-administration/defining-new-code/")).isEqualTo(LATEST); - assertThat(getDocumentationUrl("9.9.0.65466", "another/path/")).isEqualTo("https://docs.sonarqube.org/9.9/another/path/"); - assertThat(getDocumentationUrl("9.8.0", "yet/another/path/")).isEqualTo("https://docs.sonarqube.org/9.8/yet/another/path/"); - assertThat(getDocumentationUrl("100.999.0", "yet/another/path/")).isEqualTo("https://docs.sonarqube.org/100.999/yet/another/path/"); - } - -} diff --git a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/NewCodePeriodsWsTest.java b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/NewCodePeriodsWsTest.java index 7c96703c725..9dffbbf9831 100644 --- a/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/NewCodePeriodsWsTest.java +++ b/server/sonar-webserver-webapi/src/test/java/org/sonar/server/newcodeperiod/ws/NewCodePeriodsWsTest.java @@ -23,14 +23,19 @@ import org.junit.Test; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; +import org.sonar.core.documentation.DocumentationLinkGenerator; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; public class NewCodePeriodsWsTest { private String actionKey = randomAlphanumeric(10); + private DocumentationLinkGenerator documentationLinkGenerator = mock(DocumentationLinkGenerator.class); + + private NewCodePeriodsWs underTest = new NewCodePeriodsWs( - new NewCodePeriodsWsAction() { + documentationLinkGenerator, new NewCodePeriodsWsAction() { @Override public void define(WebService.NewController context) { context.createAction(actionKey).setHandler(this); diff --git a/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/WsUtils.java b/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/WsUtils.java index ed7fbb04004..dcc3f80c6c4 100644 --- a/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/WsUtils.java +++ b/server/sonar-webserver-ws/src/main/java/org/sonar/server/ws/WsUtils.java @@ -33,13 +33,9 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.sonarqube.ws.MediaTypes.JSON; import static org.sonarqube.ws.MediaTypes.PROTOBUF; -public class WsUtils { +public interface WsUtils { - private WsUtils() { - // only statics - } - - public static void writeProtobuf(Message msg, Request request, Response response) { + static void writeProtobuf(Message msg, Request request, Response response) { OutputStream output = response.stream().output(); try { if (request.getMediaType().equals(PROTOBUF)) { @@ -58,11 +54,7 @@ public class WsUtils { } } - public static <T> T checkStateWithOptional(java.util.Optional<T> value, String message, Object... messageArguments) { - if (!value.isPresent()) { - throw new IllegalStateException(format(message, messageArguments)); - } - - return value.get(); + static String createHtmlExternalLink(String url, String text) { + return String.format("<a href=\"%s\" target=\"_blank\" rel=\"noopener noreferrer\">%s</a>", url, text); } } diff --git a/server/sonar-webserver-ws/src/test/java/org/sonar/server/ws/WsUtilsTest.java b/server/sonar-webserver-ws/src/test/java/org/sonar/server/ws/WsUtilsTest.java index ebc320c6259..5981e9cb53f 100644 --- a/server/sonar-webserver-ws/src/test/java/org/sonar/server/ws/WsUtilsTest.java +++ b/server/sonar-webserver-ws/src/test/java/org/sonar/server/ws/WsUtilsTest.java @@ -84,6 +84,12 @@ public class WsUtilsTest { } @Test + public void create_safe_external_link_tag() { + assertThat(WsUtils.createHtmlExternalLink("http://google.com", "Google")) + .isEqualTo("<a href=\"http://google.com\" target=\"_blank\" rel=\"noopener noreferrer\">Google</a>"); + } + + @Test public void checkRequest_ko() { assertThatThrownBy(() -> BadRequestException.checkRequest(false, "Missing param: %s", "foo")) .isInstanceOf(BadRequestException.class) diff --git a/sonar-core/src/main/java/org/sonar/core/documentation/DefaultDocumentationLinkGenerator.java b/sonar-core/src/main/java/org/sonar/core/documentation/DefaultDocumentationLinkGenerator.java index 6b447820b0d..dbfa0740328 100644 --- a/sonar-core/src/main/java/org/sonar/core/documentation/DefaultDocumentationLinkGenerator.java +++ b/sonar-core/src/main/java/org/sonar/core/documentation/DefaultDocumentationLinkGenerator.java @@ -41,7 +41,11 @@ public class DefaultDocumentationLinkGenerator implements DocumentationLinkGener if (!url.endsWith("/")) { url += "/"; } - url += version.major() + "." + version.minor(); + if (version.qualifier().equals("SNAPSHOT")) { + url += "latest"; + } else { + url += version.major() + "." + version.minor(); + } return url; } diff --git a/sonar-core/src/test/java/org/sonar/core/documentation/DefaultDocumentationLinkGeneratorTest.java b/sonar-core/src/test/java/org/sonar/core/documentation/DefaultDocumentationLinkGeneratorTest.java index 5d6cc77e5c2..5c20ea45ab4 100644 --- a/sonar-core/src/test/java/org/sonar/core/documentation/DefaultDocumentationLinkGeneratorTest.java +++ b/sonar-core/src/test/java/org/sonar/core/documentation/DefaultDocumentationLinkGeneratorTest.java @@ -49,6 +49,7 @@ public class DefaultDocumentationLinkGeneratorTest { public void setUp() { when(sonarQubeVersion.get().major()).thenReturn(100); when(sonarQubeVersion.get().minor()).thenReturn(1000); + when(sonarQubeVersion.get().qualifier()).thenReturn(""); when(configuration.get(DOCUMENTATION_BASE_URL)).thenReturn(Optional.empty()); documentationLinkGenerator = new DefaultDocumentationLinkGenerator(sonarQubeVersion, configuration); } @@ -61,6 +62,16 @@ public class DefaultDocumentationLinkGeneratorTest { } @Test + public void getDocumentationLink_whenSnapshot_returnLatest() { + when(sonarQubeVersion.get().qualifier()).thenReturn("SNAPSHOT"); + documentationLinkGenerator = new DefaultDocumentationLinkGenerator(sonarQubeVersion, configuration); + + String generatedLink = documentationLinkGenerator.getDocumentationLink(TEST_SUFFIX); + + assertThat(generatedLink).isEqualTo(DOCUMENTATION_PUBLIC_URL + "latest/documentation/analyzing-source-code/scm-integration/"); + } + + @Test public void getDocumentationLink_whenSuffixNotProvided_returnsBaseUrl() { String generatedLink = documentationLinkGenerator.getDocumentationLink(null); |