From 451a379605676df360745519038b5ae2770a00ea Mon Sep 17 00:00:00 2001 From: Zipeng WU Date: Fri, 9 Jun 2023 15:30:19 +0200 Subject: [PATCH] SONAR-19454 Update the api/alm_integrations/import_gitlab_project API (#8493) Co-authored-by: Nolwenn Cadic --- .../gitlab/ImportGitLabProjectActionIT.java | 92 +++++++- .../NewCodeDefinitionResolverTest.java | 148 ++++++++++++ .../newcodeperiod/NewCodePeriodUtilsTest.java | 217 ------------------ .../server/project/ws/CreateActionIT.java | 9 +- .../ws/gitlab/ImportGitLabProjectAction.java | 51 +++- .../NewCodeDefinitionResolver.java | 148 ++++++++++++ .../newcodeperiod/NewCodePeriodUtils.java | 190 --------------- .../server/newcodeperiod/ws/SetAction.java | 126 +++++++++- .../sonar/server/project/ws/CreateAction.java | 62 ++--- .../platformlevel/PlatformLevel4.java | 2 + 10 files changed, 569 insertions(+), 476 deletions(-) create mode 100644 server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/NewCodeDefinitionResolverTest.java delete mode 100644 server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/NewCodePeriodUtilsTest.java create mode 100644 server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/NewCodeDefinitionResolver.java delete mode 100644 server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/NewCodePeriodUtils.java diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectActionIT.java index 17c8b80921f..a2748b8af42 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectActionIT.java @@ -29,10 +29,13 @@ import org.sonar.alm.client.gitlab.GitlabHttpClient; import org.sonar.alm.client.gitlab.Project; import org.sonar.api.utils.System2; import org.sonar.core.i18n.I18n; +import org.sonar.core.platform.EditionProvider; +import org.sonar.core.platform.PlatformEditionProvider; import org.sonar.core.util.SequenceUuidFactory; import org.sonar.db.DbTester; import org.sonar.db.alm.setting.AlmSettingDto; import org.sonar.db.component.BranchDto; +import org.sonar.db.newcodeperiod.NewCodePeriodDto; import org.sonar.db.project.ProjectDto; import org.sonar.db.user.UserDto; import org.sonar.server.almintegration.ws.ImportHelper; @@ -40,6 +43,7 @@ import org.sonar.server.almintegration.ws.ProjectKeyGenerator; import org.sonar.server.component.ComponentUpdater; import org.sonar.server.es.TestProjectIndexers; import org.sonar.server.favorite.FavoriteUpdater; +import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver; import org.sonar.server.permission.PermissionTemplateService; import org.sonar.server.project.DefaultBranchNameResolver; import org.sonar.server.project.ProjectDefaultVisibility; @@ -58,8 +62,11 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.sonar.db.component.BranchDto.DEFAULT_MAIN_BRANCH_NAME; +import static org.sonar.db.newcodeperiod.NewCodePeriodType.NUMBER_OF_DAYS; import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS; import static org.sonar.server.tester.UserSessionRule.standalone; +import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_TYPE; +import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_VALUE; public class ImportGitLabProjectActionIT { @@ -83,8 +90,11 @@ public class ImportGitLabProjectActionIT { private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession); private final ProjectDefaultVisibility projectDefaultVisibility = mock(ProjectDefaultVisibility.class); private final ProjectKeyGenerator projectKeyGenerator = mock(ProjectKeyGenerator.class); + private PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class); + private NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider); private final ImportGitLabProjectAction importGitLabProjectAction = new ImportGitLabProjectAction( - db.getDbClient(), userSession, projectDefaultVisibility, gitlabHttpClient, componentUpdater, importHelper, projectKeyGenerator); + db.getDbClient(), userSession, projectDefaultVisibility, gitlabHttpClient, componentUpdater, importHelper, projectKeyGenerator, newCodeDefinitionResolver, + defaultBranchNameResolver); private final WsActionTester ws = new WsActionTester(importGitLabProjectAction); @Before @@ -94,7 +104,9 @@ public class ImportGitLabProjectActionIT { } @Test - public void import_project() { + public void import_project_developer_edition() { + when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.DEVELOPER)); + UserDto user = db.users().insertUser(); userSession.logIn(user).addPermission(PROVISION_PROJECTS); AlmSettingDto almSetting = db.almSettings().insertGitlabAlmSetting(); @@ -111,6 +123,8 @@ public class ImportGitLabProjectActionIT { Projects.CreateWsResponse response = ws.newRequest() .setParam("almSetting", almSetting.getKey()) .setParam("gitlabProjectId", "12345") + .setParam(PARAM_NEW_CODE_DEFINITION_TYPE, "NUMBER_OF_DAYS") + .setParam(PARAM_NEW_CODE_DEFINITION_VALUE, "30") .executeProtobuf(Projects.CreateWsResponse.class); verify(gitlabHttpClient).getProject(almSetting.getUrl(), "PAT", 12345L); @@ -122,6 +136,48 @@ public class ImportGitLabProjectActionIT { Optional projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey()); assertThat(projectDto).isPresent(); assertThat(db.getDbClient().projectAlmSettingDao().selectByProject(db.getSession(), projectDto.get())).isPresent(); + + assertThat(db.getDbClient().newCodePeriodDao().selectByProject(db.getSession(), projectDto.get().getUuid())) + .isPresent() + .get() + .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue, NewCodePeriodDto::getBranchUuid) + .containsExactly(NUMBER_OF_DAYS, "30", null); + } + + @Test + public void import_project_community_edition() { + when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.COMMUNITY)); + + UserDto user = db.users().insertUser(); + userSession.logIn(user).addPermission(PROVISION_PROJECTS); + AlmSettingDto almSetting = db.almSettings().insertGitlabAlmSetting(); + db.almPats().insert(dto -> { + dto.setAlmSettingUuid(almSetting.getUuid()); + dto.setUserUuid(user.getUuid()); + dto.setPersonalAccessToken("PAT"); + }); + Project project = getGitlabProject(); + when(gitlabHttpClient.getProject(any(), any(), any())).thenReturn(project); + when(gitlabHttpClient.getBranches(any(), any(), any())).thenReturn(singletonList(new GitLabBranch("master", true))); + when(projectKeyGenerator.generateUniqueProjectKey(project.getPathWithNamespace())).thenReturn(PROJECT_KEY_NAME); + + Projects.CreateWsResponse response = ws.newRequest() + .setParam("almSetting", almSetting.getKey()) + .setParam("gitlabProjectId", "12345") + .setParam(PARAM_NEW_CODE_DEFINITION_TYPE, "NUMBER_OF_DAYS") + .setParam(PARAM_NEW_CODE_DEFINITION_VALUE, "30") + .executeProtobuf(Projects.CreateWsResponse.class); + + Projects.CreateWsResponse.Project result = response.getProject(); + + Optional projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey()); + + String projectUuid = projectDto.get().getUuid(); + assertThat(db.getDbClient().newCodePeriodDao().selectByBranch(db.getSession(), projectUuid, projectUuid)) + .isPresent() + .get() + .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue, NewCodePeriodDto::getBranchUuid) + .containsExactly(NUMBER_OF_DAYS, "30", projectUuid); } @Test @@ -197,6 +253,38 @@ public class ImportGitLabProjectActionIT { } + @Test + public void import_project_without_NCD() { + UserDto user = db.users().insertUser(); + userSession.logIn(user).addPermission(PROVISION_PROJECTS); + AlmSettingDto almSetting = db.almSettings().insertGitlabAlmSetting(); + db.almPats().insert(dto -> { + dto.setAlmSettingUuid(almSetting.getUuid()); + dto.setUserUuid(user.getUuid()); + dto.setPersonalAccessToken("PAT"); + }); + Project project = getGitlabProject(); + when(gitlabHttpClient.getProject(any(), any(), any())).thenReturn(project); + when(gitlabHttpClient.getBranches(any(), any(), any())).thenReturn(singletonList(new GitLabBranch("master", true))); + when(projectKeyGenerator.generateUniqueProjectKey(project.getPathWithNamespace())).thenReturn(PROJECT_KEY_NAME); + + Projects.CreateWsResponse response = ws.newRequest() + .setParam("almSetting", almSetting.getKey()) + .setParam("gitlabProjectId", "12345") + .executeProtobuf(Projects.CreateWsResponse.class); + + verify(gitlabHttpClient).getProject(almSetting.getUrl(), "PAT", 12345L); + + Projects.CreateWsResponse.Project result = response.getProject(); + assertThat(result.getKey()).isEqualTo(PROJECT_KEY_NAME); + assertThat(result.getName()).isEqualTo(project.getName()); + + Optional projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey()); + assertThat(projectDto).isPresent(); + assertThat(db.getDbClient().projectAlmSettingDao().selectByProject(db.getSession(), projectDto.get())).isPresent(); + } + + private Project getGitlabProject() { return new Project(randomAlphanumeric(5), randomAlphanumeric(5)); } diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/NewCodeDefinitionResolverTest.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/NewCodeDefinitionResolverTest.java new file mode 100644 index 00000000000..8aa503e2eaa --- /dev/null +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/NewCodeDefinitionResolverTest.java @@ -0,0 +1,148 @@ +/* + * 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; + +import java.util.Optional; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.core.platform.PlatformEditionProvider; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDbTester; +import org.sonar.db.newcodeperiod.NewCodePeriodDto; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNoException; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +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; + +public class NewCodeDefinitionResolverTest { + + @Rule + public DbTester db = DbTester.create(System2.INSTANCE, true); + + private static final String DEFAULT_PROJECT_ID = "12345"; + + private static final String MAIN_BRANCH = "main"; + private ComponentDbTester componentDb = new ComponentDbTester(db); + + private DbSession dbSession = db.getSession(); + private DbClient dbClient = db.getDbClient(); + private PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class); + private NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider); + + @Test + public void createNewCodeDefinition_throw_IAE_if_no_valid_type() { + assertThatThrownBy(() -> newCodeDefinitionResolver.createNewCodeDefinition(dbSession, DEFAULT_PROJECT_ID, MAIN_BRANCH, "nonValid", null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid type: nonValid"); + } + + @Test + public void createNewCodeDefinition_throw_IAE_if_type_is_not_allowed() { + assertThatThrownBy(() -> newCodeDefinitionResolver.createNewCodeDefinition(dbSession, DEFAULT_PROJECT_ID, MAIN_BRANCH, SPECIFIC_ANALYSIS.name(), null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Invalid type 'SPECIFIC_ANALYSIS'. `newCodeDefinitionType` can only be set with types: [PREVIOUS_VERSION, NUMBER_OF_DAYS, REFERENCE_BRANCH]"); + } + + @Test + public void createNewCodeDefinition_throw_IAE_if_no_value_for_days() { + assertThatThrownBy(() -> newCodeDefinitionResolver.createNewCodeDefinition(dbSession, DEFAULT_PROJECT_ID, MAIN_BRANCH, NUMBER_OF_DAYS.name(), null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("New code definition type 'NUMBER_OF_DAYS' requires a newCodeDefinitionValue"); + } + + @Test + public void createNewCodeDefinition_throw_IAE_if_days_is_invalid() { + assertThatThrownBy(() -> newCodeDefinitionResolver.createNewCodeDefinition(dbSession, DEFAULT_PROJECT_ID, MAIN_BRANCH, NUMBER_OF_DAYS.name(), "unknown")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Failed to parse number of days: unknown"); + } + + @Test + public void createNewCodeDefinition_throw_IAE_if_value_is_set_for_reference_branch() { + assertThatThrownBy(() -> newCodeDefinitionResolver.createNewCodeDefinition(dbSession, DEFAULT_PROJECT_ID, MAIN_BRANCH, REFERENCE_BRANCH.name(), "feature/zw")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Unexpected value for newCodeDefinitionType 'REFERENCE_BRANCH'"); + } + + @Test + public void createNewCodeDefinition_throw_IAE_if_previous_version_type_and_value_provided() { + assertThatThrownBy(() -> newCodeDefinitionResolver.createNewCodeDefinition(dbSession, DEFAULT_PROJECT_ID, MAIN_BRANCH, PREVIOUS_VERSION.name(), "10.2.3")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("Unexpected value for newCodeDefinitionType 'PREVIOUS_VERSION'"); + } + + @Test + public void createNewCodeDefinition_persist_previous_version_type() { + newCodeDefinitionResolver.createNewCodeDefinition(dbSession, DEFAULT_PROJECT_ID, MAIN_BRANCH, PREVIOUS_VERSION.name(), null); + + Optional newCodePeriodDto = dbClient.newCodePeriodDao().selectByProject(dbSession, DEFAULT_PROJECT_ID); + assertThat(newCodePeriodDto).map(NewCodePeriodDto::getType).hasValue(PREVIOUS_VERSION); + } + + @Test + public void createNewCodeDefinition_return_days_value_for_number_of_days_type() { + String numberOfDays = "30"; + + newCodeDefinitionResolver.createNewCodeDefinition(dbSession, DEFAULT_PROJECT_ID, MAIN_BRANCH, NUMBER_OF_DAYS.name(), numberOfDays); + + Optional newCodePeriodDto = dbClient.newCodePeriodDao().selectByProject(dbSession, DEFAULT_PROJECT_ID); + + assertThat(newCodePeriodDto) + .isPresent() + .get() + .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue) + .containsExactly(NUMBER_OF_DAYS, numberOfDays); + } + + @Test + public void createNewCodeDefinition_return_branch_value_for_reference_branch_type() { + newCodeDefinitionResolver.createNewCodeDefinition(dbSession, DEFAULT_PROJECT_ID, MAIN_BRANCH, REFERENCE_BRANCH.name(), null); + + Optional newCodePeriodDto = dbClient.newCodePeriodDao().selectByProject(dbSession, DEFAULT_PROJECT_ID); + + assertThat(newCodePeriodDto) + .isPresent() + .get() + .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue) + .containsExactly(REFERENCE_BRANCH, MAIN_BRANCH); + } + + @Test + public void checkNewCodeDefinitionParam_throw_IAE_if_newCodeDefinitionValue_is_provided_without_newCodeDefinitionType() { + assertThatThrownBy(() -> newCodeDefinitionResolver.checkNewCodeDefinitionParam(null, "anyvalue")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("New code definition type is required when new code definition value is provided"); + } + + @Test + public void checkNewCodeDefinitionParam_do_not_throw_when_both_value_and_type_are_provided() { + assertThatNoException() + .isThrownBy(() -> newCodeDefinitionResolver.checkNewCodeDefinitionParam("PREVIOUS_VERSION", "anyvalue")); + } + +} diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/NewCodePeriodUtilsTest.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/NewCodePeriodUtilsTest.java deleted file mode 100644 index 088b98769b8..00000000000 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/newcodeperiod/NewCodePeriodUtilsTest.java +++ /dev/null @@ -1,217 +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; - -import org.junit.Rule; -import org.junit.Test; -import org.sonar.api.utils.System2; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.DbTester; -import org.sonar.db.component.BranchDto; -import org.sonar.db.component.ComponentDbTester; -import org.sonar.db.component.ComponentDto; -import org.sonar.db.component.SnapshotDto; -import org.sonar.db.project.ProjectDto; - -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; -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.NewCodePeriodUtils.getNewCodeDefinitionValue; -import static org.sonar.server.newcodeperiod.NewCodePeriodUtils.getNewCodeDefinitionValueProjectCreation; -import static org.sonar.server.newcodeperiod.NewCodePeriodUtils.validateType; - -public class NewCodePeriodUtilsTest { - - @Rule - public DbTester db = DbTester.create(System2.INSTANCE, true); - - private static final String MAIN_BRANCH = "main"; - private ComponentDbTester componentDb = new ComponentDbTester(db); - - private DbSession dbSession = db.getSession(); - private DbClient dbClient = db.getDbClient(); - @Test - public void validateType_throw_IAE_if_no_valid_type() { - assertThatThrownBy(() -> validateType("nonValid", false, false)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Invalid type: nonValid"); - } - - @Test - public void validateType_throw_IAE_if_type_is_invalid_for_global() { - assertThatThrownBy(() -> validateType("SPECIFIC_ANALYSIS", true, false)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Invalid type 'SPECIFIC_ANALYSIS'. Overall setting can only be set with types: [PREVIOUS_VERSION, NUMBER_OF_DAYS]"); - } - - @Test - public void validateType_throw_IAE_if_type_is_invalid_for_project() { - assertThatThrownBy(() -> validateType("SPECIFIC_ANALYSIS", false, false)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Invalid type 'SPECIFIC_ANALYSIS'. Projects can only be set with types: [PREVIOUS_VERSION, NUMBER_OF_DAYS, REFERENCE_BRANCH]"); - } - - @Test - public void validateType_return_type_for_branch() { - assertThat(validateType("REFERENCE_BRANCH", false, true)).isEqualTo(REFERENCE_BRANCH); - } - - @Test - public void validateType_return_type_for_project() { - assertThat(validateType("REFERENCE_BRANCH", false, false)).isEqualTo(REFERENCE_BRANCH); - } - - @Test - public void validateType_return_type_for_overall() { - assertThat(validateType("PREVIOUS_VERSION", true, false)).isEqualTo(PREVIOUS_VERSION); - } - - @Test - public void getNCDValue_throw_IAE_if_no_value_for_days() { - assertThatThrownBy(() -> getNewCodeDefinitionValue(dbSession, dbClient, NUMBER_OF_DAYS, null, null, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("New code definition type 'NUMBER_OF_DAYS' requires a value"); - } - - @Test - public void getNCDValue_throw_IAE_if_no_value_for_reference_branch() { - assertThatThrownBy(() -> getNewCodeDefinitionValue(dbSession, dbClient, REFERENCE_BRANCH, null, null, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("New code definition type 'REFERENCE_BRANCH' requires a value"); - } - - @Test - public void getNCDValue_throw_IAE_if_no_value_for_analysis() { - assertThatThrownBy(() -> getNewCodeDefinitionValue(dbSession, dbClient, SPECIFIC_ANALYSIS, null, null, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("New code definition type 'SPECIFIC_ANALYSIS' requires a value"); - } - - @Test - public void getNCDValue_throw_IAE_if_days_is_invalid() { - assertThatThrownBy(() -> getNewCodeDefinitionValue(dbSession, dbClient, NUMBER_OF_DAYS, null, null, "unknown")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Failed to parse number of days: unknown"); - } - - @Test - public void getNCDValue_throw_IAE_if_previous_version_type_and_value_provided() { - assertThatThrownBy(() -> getNewCodeDefinitionValue(dbSession, dbClient, PREVIOUS_VERSION, null, null, "someValue")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Unexpected value for type 'PREVIOUS_VERSION'"); - } - - @Test - public void getNCDValue_return_empty_for_previous_version_type() { - assertThat(getNewCodeDefinitionValue(dbSession, dbClient, PREVIOUS_VERSION, null, null, null)).isEmpty(); - } - - @Test - public void getNCDValue_return_days_value_for_number_of_days_type() { - String numberOfDays = "30"; - - assertThat(getNewCodeDefinitionValue(dbSession, dbClient, NUMBER_OF_DAYS, null, null, numberOfDays)) - .isPresent() - .get() - .isEqualTo(numberOfDays); - } - - @Test - public void getNCDValue_return_specific_analysis_uuid_for_specific_analysis_type() { - ComponentDto project = componentDb.insertPublicProject().getMainBranchComponent(); - SnapshotDto analysisMaster = db.components().insertSnapshot(project); - ProjectDto projectDto = new ProjectDto().setUuid(project.uuid()); - BranchDto branchDto = new BranchDto().setUuid(project.uuid()); - String numberOfDays = "30"; - - assertThat(getNewCodeDefinitionValue(dbSession, dbClient, SPECIFIC_ANALYSIS, projectDto, branchDto, analysisMaster.getUuid())) - .isPresent() - .get() - .isEqualTo(analysisMaster.getUuid()); - } - - @Test - public void getNCDValue_return_branch_value_for_reference_branch_type() { - String branchKey = "main"; - - assertThat(getNewCodeDefinitionValue(dbSession, dbClient, REFERENCE_BRANCH, null, null, branchKey)) - .isPresent() - .get() - .isEqualTo(branchKey); - } - - @Test - public void getNCDValueProjectCreation_throw_IAE_if_no_value_for_days() { - assertThatThrownBy(() -> getNewCodeDefinitionValueProjectCreation(NUMBER_OF_DAYS, null, MAIN_BRANCH)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("New code definition type 'NUMBER_OF_DAYS' requires a value"); - } - - @Test - public void getNCDValueProjectCreation_throw_IAE_if_days_is_invalid() { - assertThatThrownBy(() -> getNewCodeDefinitionValueProjectCreation(NUMBER_OF_DAYS, "unknown", MAIN_BRANCH)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Failed to parse number of days: unknown"); - } - - @Test - public void getNCDValueProjectCreation_throw_IAE_if_previous_version_type_and_value_provided() { - assertThatThrownBy(() -> getNewCodeDefinitionValueProjectCreation(PREVIOUS_VERSION, "someValue", MAIN_BRANCH)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Unexpected value for type 'PREVIOUS_VERSION'"); - } - - @Test - public void getNCDValueProjectCreation_return_empty_for_previous_version_type() { - assertThat(getNewCodeDefinitionValueProjectCreation(PREVIOUS_VERSION, null, MAIN_BRANCH)).isEmpty(); - } - - @Test - public void getNCDValueProjectCreation_return_days_value_for_number_of_days_type() { - String numberOfDays = "30"; - - assertThat(getNewCodeDefinitionValueProjectCreation(NUMBER_OF_DAYS, numberOfDays, MAIN_BRANCH)) - .isPresent() - .get() - .isEqualTo(numberOfDays); - } - - @Test - public void getNCDValueProjectCreation_return_branch_value_for_reference_branch_type() { - String branchKey = "main"; - - assertThat(getNewCodeDefinitionValueProjectCreation(REFERENCE_BRANCH, null, branchKey)) - .isPresent() - .get() - .isEqualTo(branchKey); - } - - @Test - public void getNCDValueProjectCreation_throw_IAE_fiif_reference_branch_type_and_value_provided() { - assertThatThrownBy(() -> getNewCodeDefinitionValueProjectCreation(REFERENCE_BRANCH, "someValue", MAIN_BRANCH)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessageContaining("Unexpected value for type 'REFERENCE_BRANCH'"); - } - -} diff --git a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/CreateActionIT.java b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/CreateActionIT.java index dcfa88bdd86..9b4128ae968 100644 --- a/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/CreateActionIT.java +++ b/server/sonar-webserver-webapi/src/it/java/org/sonar/server/project/ws/CreateActionIT.java @@ -43,6 +43,7 @@ import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.favorite.FavoriteUpdater; import org.sonar.server.l18n.I18nRule; +import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver; import org.sonar.server.permission.PermissionTemplateService; import org.sonar.server.project.DefaultBranchNameResolver; import org.sonar.server.project.ProjectDefaultVisibility; @@ -66,8 +67,8 @@ import static org.sonar.db.component.BranchDto.DEFAULT_MAIN_BRANCH_NAME; import static org.sonar.db.newcodeperiod.NewCodePeriodType.NUMBER_OF_DAYS; import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH; import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS; -import static org.sonar.server.newcodeperiod.NewCodePeriodUtils.NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION; -import static org.sonar.server.newcodeperiod.NewCodePeriodUtils.NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION; +import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION; +import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION; import static org.sonar.server.project.Visibility.PRIVATE; import static org.sonar.test.JsonAssert.assertJson; import static org.sonarqube.ws.client.WsRequest.Method.POST; @@ -99,12 +100,14 @@ public class CreateActionIT { private final PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class); private PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class); + + private NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider); private final WsActionTester ws = new WsActionTester( new CreateAction( db.getDbClient(), userSession, new ComponentUpdater(db.getDbClient(), i18n, system2, permissionTemplateService, new FavoriteUpdater(db.getDbClient()), projectIndexers, new SequenceUuidFactory(), defaultBranchNameResolver, true), - projectDefaultVisibility, editionProvider, defaultBranchNameResolver)); + projectDefaultVisibility, defaultBranchNameResolver, newCodeDefinitionResolver)); @Before public void before() { diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectAction.java index d6032ea42b5..e895173cf7d 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/almintegration/ws/gitlab/ImportGitLabProjectAction.java @@ -21,6 +21,7 @@ package org.sonar.server.almintegration.ws.gitlab; import java.util.Optional; import javax.annotation.Nullable; +import javax.inject.Inject; import org.sonar.alm.client.gitlab.GitLabBranch; import org.sonar.alm.client.gitlab.GitlabHttpClient; import org.sonar.alm.client.gitlab.Project; @@ -38,6 +39,8 @@ import org.sonar.server.almintegration.ws.ImportHelper; import org.sonar.server.almintegration.ws.ProjectKeyGenerator; import org.sonar.server.component.ComponentCreationData; import org.sonar.server.component.ComponentUpdater; +import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver; +import org.sonar.server.project.DefaultBranchNameResolver; import org.sonar.server.project.ProjectDefaultVisibility; import org.sonar.server.user.UserSession; import org.sonarqube.ws.Projects.CreateWsResponse; @@ -45,7 +48,12 @@ import org.sonarqube.ws.Projects.CreateWsResponse; import static java.util.Objects.requireNonNull; import static org.sonar.api.resources.Qualifiers.PROJECT; import static org.sonar.server.component.NewComponent.newComponentBuilder; +import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION; +import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION; +import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.checkNewCodeDefinitionParam; import static org.sonar.server.ws.WsUtils.writeProtobuf; +import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_TYPE; +import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_VALUE; public class ImportGitLabProjectAction implements AlmIntegrationsWsAction { @@ -58,10 +66,14 @@ public class ImportGitLabProjectAction implements AlmIntegrationsWsAction { private final ComponentUpdater componentUpdater; private final ImportHelper importHelper; private final ProjectKeyGenerator projectKeyGenerator; + private final NewCodeDefinitionResolver newCodeDefinitionResolver; + private final DefaultBranchNameResolver defaultBranchNameResolver; + @Inject public ImportGitLabProjectAction(DbClient dbClient, UserSession userSession, ProjectDefaultVisibility projectDefaultVisibility, GitlabHttpClient gitlabHttpClient, - ComponentUpdater componentUpdater, ImportHelper importHelper, ProjectKeyGenerator projectKeyGenerator) { + ComponentUpdater componentUpdater, ImportHelper importHelper, ProjectKeyGenerator projectKeyGenerator, NewCodeDefinitionResolver newCodeDefinitionResolver, + DefaultBranchNameResolver defaultBranchNameResolver) { this.dbClient = dbClient; this.userSession = userSession; this.projectDefaultVisibility = projectDefaultVisibility; @@ -69,6 +81,8 @@ public class ImportGitLabProjectAction implements AlmIntegrationsWsAction { this.componentUpdater = componentUpdater; this.importHelper = importHelper; this.projectKeyGenerator = projectKeyGenerator; + this.newCodeDefinitionResolver = newCodeDefinitionResolver; + this.defaultBranchNameResolver = defaultBranchNameResolver; } @Override @@ -86,6 +100,14 @@ public class ImportGitLabProjectAction implements AlmIntegrationsWsAction { action.createParam(PARAM_GITLAB_PROJECT_ID) .setRequired(true) .setDescription("GitLab project ID"); + + action.createParam(PARAM_NEW_CODE_DEFINITION_TYPE) + .setDescription(NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION) + .setSince("10.1"); + + action.createParam(PARAM_NEW_CODE_DEFINITION_VALUE) + .setDescription(NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION) + .setSince("10.1"); } @Override @@ -96,12 +118,13 @@ public class ImportGitLabProjectAction implements AlmIntegrationsWsAction { private CreateWsResponse doHandle(Request request) { importHelper.checkProvisionProjectPermission(); - AlmSettingDto almSettingDto = importHelper.getAlmSetting(request); - String userUuid = importHelper.getUserUuid(); + + String newCodeDefinitionType = request.param(PARAM_NEW_CODE_DEFINITION_TYPE); + String newCodeDefinitionValue = request.param(PARAM_NEW_CODE_DEFINITION_VALUE); + try (DbSession dbSession = dbClient.openSession(false)) { - Optional almPatDto = dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto); - String pat = almPatDto.map(AlmPatDto::getPersonalAccessToken) - .orElseThrow(() -> new IllegalArgumentException(String.format("personal access token for '%s' is missing", almSettingDto.getKey()))); + AlmSettingDto almSettingDto = importHelper.getAlmSetting(request); + String pat = getPat(dbSession, almSettingDto); long gitlabProjectId = request.mandatoryParamAsLong(PARAM_GITLAB_PROJECT_ID); @@ -109,15 +132,31 @@ public class ImportGitLabProjectAction implements AlmIntegrationsWsAction { Project gitlabProject = gitlabHttpClient.getProject(gitlabUrl, pat, gitlabProjectId); Optional almMainBranchName = getAlmDefaultBranch(pat, gitlabProjectId, gitlabUrl); + ComponentCreationData componentCreationData = createProject(dbSession, gitlabProject, almMainBranchName.orElse(null)); ProjectDto projectDto = Optional.ofNullable(componentCreationData.projectDto()).orElseThrow(); populateMRSetting(dbSession, gitlabProjectId, projectDto, almSettingDto); + + checkNewCodeDefinitionParam(newCodeDefinitionType, newCodeDefinitionValue); + + if (newCodeDefinitionType != null) { + newCodeDefinitionResolver.createNewCodeDefinition(dbSession, projectDto.getUuid(), almMainBranchName.orElse(defaultBranchNameResolver.getEffectiveMainBranchName()), + newCodeDefinitionType, newCodeDefinitionValue); + } + componentUpdater.commitAndIndex(dbSession, componentCreationData.mainBranchComponent()); return ImportHelper.toCreateResponse(projectDto); } } + private String getPat(DbSession dbSession, AlmSettingDto almSettingDto) { + String userUuid = importHelper.getUserUuid(); + Optional almPatDto = dbClient.almPatDao().selectByUserAndAlmSetting(dbSession, userUuid, almSettingDto); + return almPatDto.map(AlmPatDto::getPersonalAccessToken) + .orElseThrow(() -> new IllegalArgumentException(String.format("personal access token for '%s' is missing", almSettingDto.getKey()))); + } + private Optional getAlmDefaultBranch(String pat, long gitlabProjectId, String gitlabUrl) { Optional almMainBranch = gitlabHttpClient.getBranches(gitlabUrl, pat, gitlabProjectId).stream().filter(GitLabBranch::isDefault).findFirst(); return almMainBranch.map(GitLabBranch::getName); diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/NewCodeDefinitionResolver.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/NewCodeDefinitionResolver.java new file mode 100644 index 00000000000..968ffc732c1 --- /dev/null +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/NewCodeDefinitionResolver.java @@ -0,0 +1,148 @@ +/* + * 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; + +import com.google.common.base.Preconditions; +import java.util.EnumSet; +import java.util.Locale; +import java.util.Optional; +import javax.annotation.Nullable; +import org.sonar.core.platform.EditionProvider; +import org.sonar.core.platform.PlatformEditionProvider; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.newcodeperiod.NewCodePeriodDto; +import org.sonar.db.newcodeperiod.NewCodePeriodParser; +import org.sonar.db.newcodeperiod.NewCodePeriodType; + +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; + +public class NewCodeDefinitionResolver { + private static final String BEGIN_LIST = "
    "; + + private static final String END_LIST = "
"; + private static final String BEGIN_ITEM_LIST = "
  • "; + private static final String END_ITEM_LIST = "
  • "; + + public static final String NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION = "Type
    " + + "New code definitions of the following types are allowed:" + + BEGIN_LIST + + BEGIN_ITEM_LIST + PREVIOUS_VERSION.name() + END_ITEM_LIST + + BEGIN_ITEM_LIST + NUMBER_OF_DAYS.name() + END_ITEM_LIST + + BEGIN_ITEM_LIST + REFERENCE_BRANCH.name() + " - will default to the main branch." + END_ITEM_LIST + + END_LIST; + + public static final String NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION = "Value
    " + + "For each new code definition type, a different value is expected:" + + BEGIN_LIST + + BEGIN_ITEM_LIST + "no value, when the new code definition type is " + PREVIOUS_VERSION.name() + " and " + REFERENCE_BRANCH.name() + END_ITEM_LIST + + BEGIN_ITEM_LIST + "a number between 1 and 90, when the new code definition type is " + NUMBER_OF_DAYS.name() + END_ITEM_LIST + + END_LIST; + + private static final String UNEXPECTED_VALUE_ERROR_MESSAGE = "Unexpected value for newCodeDefinitionType '%s'"; + + private static final EnumSet projectCreationNCDTypes = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS, REFERENCE_BRANCH); + + private final DbClient dbClient; + private final PlatformEditionProvider editionProvider; + + public NewCodeDefinitionResolver(DbClient dbClient, PlatformEditionProvider editionProvider) { + this.dbClient = dbClient; + this.editionProvider = editionProvider; + } + + public void createNewCodeDefinition(DbSession dbSession, String projectUuid, String defaultBranchName, String newCodeDefinitionType, String newCodeDefinitionValue) { + + boolean isCommunityEdition = editionProvider.get().filter(EditionProvider.Edition.COMMUNITY::equals).isPresent(); + NewCodePeriodType newCodePeriodType = parseNewCodeDefinitionType(newCodeDefinitionType); + + NewCodePeriodDto dto = new NewCodePeriodDto(); + dto.setType(newCodePeriodType); + dto.setProjectUuid(projectUuid); + + if (isCommunityEdition) { + dto.setBranchUuid(projectUuid); + } + + getNewCodeDefinitionValueProjectCreation(newCodePeriodType, newCodeDefinitionValue, defaultBranchName).ifPresent(dto::setValue); + + if (!CaycUtils.isNewCodePeriodCompliant(dto.getType(), dto.getValue())) { + throw new IllegalArgumentException("Failed to set the New Code Definition. The given value is not compatible with the Clean as You Code methodology. " + + "Please refer to the documentation for compliant options."); + } + + dbClient.newCodePeriodDao().insert(dbSession, dto); + } + + public static void checkNewCodeDefinitionParam(String newCodeDefinitionType, String newCodeDefinitionValue) { + if (newCodeDefinitionType == null && newCodeDefinitionValue != null) { + throw new IllegalArgumentException("New code definition type is required when new code definition value is provided"); + } + } + + private static Optional getNewCodeDefinitionValueProjectCreation(NewCodePeriodType type, @Nullable String value, String defaultBranchName) { + return switch (type) { + case PREVIOUS_VERSION -> { + Preconditions.checkArgument(value == null, UNEXPECTED_VALUE_ERROR_MESSAGE, type); + yield Optional.empty(); + } + case NUMBER_OF_DAYS -> { + requireValue(type, value); + yield Optional.of(parseDays(value)); + } + case REFERENCE_BRANCH -> { + Preconditions.checkArgument(value == null, UNEXPECTED_VALUE_ERROR_MESSAGE, type); + yield Optional.of(defaultBranchName); + } + default -> throw new IllegalStateException("Unexpected type: " + type); + }; + } + + private static String parseDays(String value) { + try { + return Integer.toString(NewCodePeriodParser.parseDays(value)); + } catch (Exception e) { + throw new IllegalArgumentException("Failed to parse number of days: " + value); + } + } + + private static void requireValue(NewCodePeriodType type, @Nullable String value) { + Preconditions.checkArgument(value != null, "New code definition type '%s' requires a newCodeDefinitionValue", type); + } + + private static NewCodePeriodType parseNewCodeDefinitionType(String typeStr) { + NewCodePeriodType type; + try { + type = NewCodePeriodType.valueOf(typeStr.toUpperCase(Locale.US)); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid type: " + typeStr); + } + validateType(type); + return type; + } + + private static void validateType(NewCodePeriodType type) { + Preconditions.checkArgument(projectCreationNCDTypes.contains(type), "Invalid type '%s'. `newCodeDefinitionType` can only be set with types: %s", + type, projectCreationNCDTypes); + } + +} diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/NewCodePeriodUtils.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/NewCodePeriodUtils.java deleted file mode 100644 index 0b5703841cd..00000000000 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/newcodeperiod/NewCodePeriodUtils.java +++ /dev/null @@ -1,190 +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; - -import com.google.common.base.Preconditions; -import java.util.EnumSet; -import java.util.Locale; -import java.util.Optional; -import java.util.Set; -import javax.annotation.Nullable; -import org.sonar.db.DbClient; -import org.sonar.db.DbSession; -import org.sonar.db.component.BranchDto; -import org.sonar.db.component.SnapshotDto; -import org.sonar.db.newcodeperiod.NewCodePeriodParser; -import org.sonar.db.newcodeperiod.NewCodePeriodType; -import org.sonar.db.project.ProjectDto; -import org.sonar.server.exceptions.NotFoundException; - -import static com.google.common.base.Preconditions.checkArgument; -import static java.lang.String.format; -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; - -public interface NewCodePeriodUtils { - String BEGIN_LIST = "
      "; - - String END_LIST = "
    "; - String BEGIN_ITEM_LIST = "
  • "; - String END_ITEM_LIST = "
  • "; - String NEW_CODE_PERIOD_TYPE_DESCRIPTION = "Type
    " + - "New code definitions of the following types are allowed:" + - BEGIN_LIST + - BEGIN_ITEM_LIST + SPECIFIC_ANALYSIS.name() + " - can be set at branch level only" + END_ITEM_LIST + - BEGIN_ITEM_LIST + PREVIOUS_VERSION.name() + " - can be set at any level (global, project, branch)" + END_ITEM_LIST + - BEGIN_ITEM_LIST + NUMBER_OF_DAYS.name() + " - can be set at any level (global, project, branch)" + END_ITEM_LIST + - BEGIN_ITEM_LIST + REFERENCE_BRANCH.name() + " - can only be set for projects and branches" + END_ITEM_LIST + - END_LIST; - - String NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION = "Type
    " + - "New code definitions of the following types are allowed:" + - BEGIN_LIST + - BEGIN_ITEM_LIST + PREVIOUS_VERSION.name() + END_ITEM_LIST + - BEGIN_ITEM_LIST + NUMBER_OF_DAYS.name() + END_ITEM_LIST + - BEGIN_ITEM_LIST + REFERENCE_BRANCH.name() + " - will default to the main branch. A new code definition should be set for the branch itself. " + - "Until you do so, this branch will use itself as a reference branch and no code will be considered new for this branch" + END_ITEM_LIST + - END_LIST; - - String NEW_CODE_PERIOD_VALUE_DESCRIPTION = "Value
    " + - "For each type, a different value is expected:" + - BEGIN_LIST + - BEGIN_ITEM_LIST + "the uuid of an analysis, when type is " + SPECIFIC_ANALYSIS.name() + END_ITEM_LIST + - BEGIN_ITEM_LIST + "no value, when type is " + PREVIOUS_VERSION.name() + END_ITEM_LIST + - BEGIN_ITEM_LIST + "a number between 1 and 90, when type is " + NUMBER_OF_DAYS.name() + END_ITEM_LIST + - BEGIN_ITEM_LIST + "a string, when type is " + REFERENCE_BRANCH.name() + END_ITEM_LIST + - END_LIST; - String NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION = "Value
    " + - "For each type, a different value is expected:" + - BEGIN_LIST + - BEGIN_ITEM_LIST + "no value, when type is " + PREVIOUS_VERSION.name() + " and " + REFERENCE_BRANCH.name() + END_ITEM_LIST + - BEGIN_ITEM_LIST + "a number between 1 and 90, when type is " + NUMBER_OF_DAYS.name() + END_ITEM_LIST + - END_LIST; - - String UNEXPECTED_VALUE_ERROR_MESSAGE = "Unexpected value for type '%s'"; - - static Optional getNewCodeDefinitionValue(DbSession dbSession, DbClient dbClient, NewCodePeriodType type, @Nullable ProjectDto project, - @Nullable BranchDto branch, @Nullable String value) { - switch (type) { - case PREVIOUS_VERSION: - Preconditions.checkArgument(value == null, UNEXPECTED_VALUE_ERROR_MESSAGE, type); - return Optional.empty(); - case NUMBER_OF_DAYS: - requireValue(type, value); - return Optional.of(parseDays(value)); - case SPECIFIC_ANALYSIS: - requireValue(type, value); - requireBranch(type, branch); - SnapshotDto analysis = getAnalysis(dbSession, value, project, branch, dbClient); - return Optional.of(analysis.getUuid()); - case REFERENCE_BRANCH: - requireValue(type, value); - return Optional.of(value); - default: - throw new IllegalStateException("Unexpected type: " + type); - } - } - - static Optional getNewCodeDefinitionValueProjectCreation(NewCodePeriodType type, @Nullable String value, String defaultBranchName) { - switch (type) { - case PREVIOUS_VERSION: - Preconditions.checkArgument(value == null, UNEXPECTED_VALUE_ERROR_MESSAGE, type); - return Optional.empty(); - case NUMBER_OF_DAYS: - requireValue(type, value); - return Optional.of(parseDays(value)); - case REFERENCE_BRANCH: - Preconditions.checkArgument(value == null, UNEXPECTED_VALUE_ERROR_MESSAGE, type); - return Optional.of(defaultBranchName); - default: - throw new IllegalStateException("Unexpected type: " + type); - } - } - - private static String parseDays(String value) { - try { - return Integer.toString(NewCodePeriodParser.parseDays(value)); - } catch (Exception e) { - throw new IllegalArgumentException("Failed to parse number of days: " + value); - } - } - - private static void requireValue(NewCodePeriodType type, @Nullable String value) { - Preconditions.checkArgument(value != null, "New code definition type '%s' requires a value", type); - } - - private static void requireBranch(NewCodePeriodType type, @Nullable BranchDto branch) { - Preconditions.checkArgument(branch != null, "New code definition type '%s' requires a branch", type); - } - private static Set getInstanceTypes() { - return EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS); - } - - private static Set getProjectTypes() { - return EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS, REFERENCE_BRANCH); - } - - private static Set getBranchTypes() { - return EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS, SPECIFIC_ANALYSIS, REFERENCE_BRANCH); - } - - static NewCodePeriodType validateType(String typeStr, boolean isOverall, boolean isBranch) { - NewCodePeriodType type; - try { - type = NewCodePeriodType.valueOf(typeStr.toUpperCase(Locale.US)); - } catch (IllegalArgumentException e) { - throw new IllegalArgumentException("Invalid type: " + typeStr); - } - - if (isOverall) { - checkType("Overall setting", getInstanceTypes(), type); - } else if (isBranch) { - checkType("Branches", getBranchTypes(), type); - } else { - checkType("Projects", getProjectTypes(), type); - } - return type; - } - - private static SnapshotDto getAnalysis(DbSession dbSession, String analysisUuid, ProjectDto project, BranchDto branch, DbClient dbClient) { - SnapshotDto snapshotDto = dbClient.snapshotDao().selectByUuid(dbSession, analysisUuid) - .orElseThrow(() -> new NotFoundException(format("Analysis '%s' is not found", analysisUuid))); - checkAnalysis(dbSession, project, branch, snapshotDto, dbClient); - return snapshotDto; - } - - private static void checkAnalysis(DbSession dbSession, ProjectDto project, BranchDto branch, SnapshotDto analysis, DbClient dbClient) { - BranchDto analysisBranch = dbClient.branchDao().selectByUuid(dbSession, analysis.getComponentUuid()).orElse(null); - boolean analysisMatchesProjectBranch = analysisBranch != null && analysisBranch.getUuid().equals(branch.getUuid()); - - checkArgument(analysisMatchesProjectBranch, - "Analysis '%s' does not belong to branch '%s' of project '%s'", - analysis.getUuid(), branch.getKey(), project.getKey()); - } - - private static void checkType(String name, Set validTypes, NewCodePeriodType type) { - if (!validTypes.contains(type)) { - throw new IllegalArgumentException(String.format("Invalid type '%s'. %s can only be set with types: %s", type, name, validTypes)); - } - } - -} 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 899398d7651..3c1b6962f6c 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 @@ -19,8 +19,11 @@ */ package org.sonar.server.newcodeperiod.ws; +import com.google.common.base.Preconditions; import java.util.EnumSet; +import java.util.Locale; import java.util.Set; +import javax.annotation.Nullable; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; @@ -31,8 +34,10 @@ import org.sonar.core.platform.PlatformEditionProvider; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.BranchDto; +import org.sonar.db.component.SnapshotDto; import org.sonar.db.newcodeperiod.NewCodePeriodDao; import org.sonar.db.newcodeperiod.NewCodePeriodDto; +import org.sonar.db.newcodeperiod.NewCodePeriodParser; import org.sonar.db.newcodeperiod.NewCodePeriodType; import org.sonar.db.project.ProjectDto; import org.sonar.server.component.ComponentFinder; @@ -40,15 +45,12 @@ import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.newcodeperiod.CaycUtils; import org.sonar.server.user.UserSession; +import static com.google.common.base.Preconditions.checkArgument; import static java.lang.String.format; 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.NewCodePeriodUtils.NEW_CODE_PERIOD_TYPE_DESCRIPTION; -import static org.sonar.server.newcodeperiod.NewCodePeriodUtils.NEW_CODE_PERIOD_VALUE_DESCRIPTION; -import static org.sonar.server.newcodeperiod.NewCodePeriodUtils.getNewCodeDefinitionValue; -import static org.sonar.server.newcodeperiod.NewCodePeriodUtils.validateType; import static org.sonar.server.ws.WsUtils.createHtmlExternalLink; public class SetAction implements NewCodePeriodsWsAction { @@ -63,7 +65,8 @@ public class SetAction implements NewCodePeriodsWsAction { private static final Set OVERALL_TYPES = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS); private static final Set PROJECT_TYPES = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS, REFERENCE_BRANCH); - private static final Set BRANCH_TYPES = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS, SPECIFIC_ANALYSIS, REFERENCE_BRANCH); + private static final Set BRANCH_TYPES = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS, SPECIFIC_ANALYSIS, + REFERENCE_BRANCH); private final DbClient dbClient; private final UserSession userSession; @@ -93,7 +96,6 @@ public class SetAction implements NewCodePeriodsWsAction { "Existing projects or branches having a specific new code definition will not be impacted" + END_ITEM_LIST + BEGIN_ITEM_LIST + "Project key must be provided to update the value for a project" + END_ITEM_LIST + BEGIN_ITEM_LIST + "Both project and branch keys must be provided to update the value for a branch" + END_ITEM_LIST + - BEGIN_ITEM_LIST + "New setting must be compliant with the Clean as You Code methodology" + END_ITEM_LIST + END_LIST + "Requires one of the following permissions: " + BEGIN_LIST + @@ -109,9 +111,25 @@ public class SetAction implements NewCodePeriodsWsAction { .setDescription("Branch key"); action.createParam(PARAM_TYPE) .setRequired(true) - .setDescription(NEW_CODE_PERIOD_VALUE_DESCRIPTION); + .setDescription("Type
    " + + "New code definitions of the following types are allowed:" + + BEGIN_LIST + + BEGIN_ITEM_LIST + SPECIFIC_ANALYSIS.name() + " - can be set at branch level only" + END_ITEM_LIST + + BEGIN_ITEM_LIST + PREVIOUS_VERSION.name() + " - can be set at any level (global, project, branch)" + END_ITEM_LIST + + BEGIN_ITEM_LIST + NUMBER_OF_DAYS.name() + " - can be set at any level (global, project, branch)" + END_ITEM_LIST + + BEGIN_ITEM_LIST + REFERENCE_BRANCH.name() + " - can only be set for projects and branches" + END_ITEM_LIST + + END_LIST + ); action.createParam(PARAM_VALUE) - .setDescription(NEW_CODE_PERIOD_TYPE_DESCRIPTION); + .setDescription("Value
    " + + "For each type, a different value is expected:" + + BEGIN_LIST + + BEGIN_ITEM_LIST + "the uuid of an analysis, when type is " + SPECIFIC_ANALYSIS.name() + END_ITEM_LIST + + BEGIN_ITEM_LIST + "no value, when type is " + PREVIOUS_VERSION.name() + END_ITEM_LIST + + BEGIN_ITEM_LIST + "a number between 1 and 90, when type is " + NUMBER_OF_DAYS.name() + END_ITEM_LIST + + BEGIN_ITEM_LIST + "a string, when type is " + REFERENCE_BRANCH.name() + END_ITEM_LIST + + END_LIST + ); } @Override @@ -154,11 +172,11 @@ public class SetAction implements NewCodePeriodsWsAction { userSession.checkIsSystemAdministrator(); } - getNewCodeDefinitionValue(dbSession, dbClient, type, project, branch, valueStr).ifPresent(dto::setValue); + setValue(dbSession, dto, type, project, branch, valueStr); if (!CaycUtils.isNewCodePeriodCompliant(dto.getType(), dto.getValue())) { - throw new IllegalArgumentException("Failed to set the New Code Definition. The given value is not compatible with the Clean as You Code methodology. " - + "Please refer to the documentation for compliant options."); + throw new IllegalArgumentException("Failed to set the New Code Definition. The given value is not compatible with the Clean as " + + "You Code methodology. Please refer to the documentation for compliant options."); } newCodePeriodDao.upsert(dbSession, dto); @@ -166,6 +184,52 @@ public class SetAction implements NewCodePeriodsWsAction { } } + private void setValue(DbSession dbSession, NewCodePeriodDto dto, NewCodePeriodType type, @Nullable ProjectDto project, + @Nullable BranchDto branch, @Nullable String value) { + switch (type) { + case PREVIOUS_VERSION -> Preconditions.checkArgument(value == null, "Unexpected value for type '%s'", type); + case NUMBER_OF_DAYS -> { + requireValue(type, value); + dto.setValue(parseDays(value)); + } + case SPECIFIC_ANALYSIS -> { + checkValuesForSpecificBranch(type, value, project, branch); + dto.setValue(getAnalysis(dbSession, value, project, branch).getUuid()); + } + case REFERENCE_BRANCH -> { + requireValue(type, value); + dto.setValue(value); + } + default -> throw new IllegalStateException("Unexpected type: " + type); + } + } + + private static void checkValuesForSpecificBranch(NewCodePeriodType type, @Nullable String value, @Nullable ProjectDto project, @Nullable BranchDto branch) { + requireValue(type, value); + requireProject(type, project); + requireBranch(type, branch); + } + + private static String parseDays(String value) { + try { + return Integer.toString(NewCodePeriodParser.parseDays(value)); + } catch (Exception e) { + throw new IllegalArgumentException("Failed to parse number of days: " + value); + } + } + + private static void requireValue(NewCodePeriodType type, @Nullable String value) { + Preconditions.checkArgument(value != null, "New code definition type '%s' requires a value", type); + } + + private static void requireBranch(NewCodePeriodType type, @Nullable BranchDto branch) { + Preconditions.checkArgument(branch != null, "New code definition type '%s' requires a branch", type); + } + + private static void requireProject(NewCodePeriodType type, @Nullable ProjectDto project) { + Preconditions.checkArgument(project != null, "New code definition type '%s' requires a project", type); + } + private BranchDto getBranch(DbSession dbSession, ProjectDto project, String branchKey) { return dbClient.branchDao().selectByBranchKey(dbSession, project.getUuid(), branchKey) .orElseThrow(() -> new NotFoundException(format("Branch '%s' in project '%s' not found", branchKey, project.getKey()))); @@ -181,4 +245,44 @@ public class SetAction implements NewCodePeriodsWsAction { .findFirst() .orElseThrow(() -> new NotFoundException(format("Main branch in project '%s' is not found", project.getKey()))); } + + private static NewCodePeriodType validateType(String typeStr, boolean isOverall, boolean isBranch) { + NewCodePeriodType type; + try { + type = NewCodePeriodType.valueOf(typeStr.toUpperCase(Locale.US)); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid type: " + typeStr); + } + + if (isOverall) { + checkType("Overall setting", OVERALL_TYPES, type); + } else if (isBranch) { + checkType("Branches", BRANCH_TYPES, type); + } else { + checkType("Projects", PROJECT_TYPES, type); + } + return type; + } + + private SnapshotDto getAnalysis(DbSession dbSession, String analysisUuid, ProjectDto project, BranchDto branch) { + SnapshotDto snapshotDto = dbClient.snapshotDao().selectByUuid(dbSession, analysisUuid) + .orElseThrow(() -> new NotFoundException(format("Analysis '%s' is not found", analysisUuid))); + checkAnalysis(dbSession, project, branch, snapshotDto); + return snapshotDto; + } + + private void checkAnalysis(DbSession dbSession, ProjectDto project, BranchDto branch, SnapshotDto analysis) { + BranchDto analysisBranch = dbClient.branchDao().selectByUuid(dbSession, analysis.getComponentUuid()).orElse(null); + boolean analysisMatchesProjectBranch = analysisBranch != null && analysisBranch.getUuid().equals(branch.getUuid()); + + checkArgument(analysisMatchesProjectBranch, + "Analysis '%s' does not belong to branch '%s' of project '%s'", + analysis.getUuid(), branch.getKey(), project.getKey()); + } + + private static void checkType(String name, Set validTypes, NewCodePeriodType type) { + if (!validTypes.contains(type)) { + throw new IllegalArgumentException(String.format("Invalid type '%s'. %s can only be set with types: %s", type, name, validTypes)); + } + } } diff --git a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/CreateAction.java b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/CreateAction.java index 06278fd680d..49a10dc1336 100644 --- a/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/CreateAction.java +++ b/server/sonar-webserver-webapi/src/main/java/org/sonar/server/project/ws/CreateAction.java @@ -26,15 +26,11 @@ import org.sonar.api.server.ws.Change; 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.platform.EditionProvider; -import org.sonar.core.platform.PlatformEditionProvider; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; -import org.sonar.db.newcodeperiod.NewCodePeriodDto; -import org.sonar.db.newcodeperiod.NewCodePeriodType; import org.sonar.server.component.ComponentUpdater; -import org.sonar.server.newcodeperiod.CaycUtils; +import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver; import org.sonar.server.project.DefaultBranchNameResolver; import org.sonar.server.project.ProjectDefaultVisibility; import org.sonar.server.project.Visibility; @@ -48,10 +44,9 @@ import static org.sonar.core.component.ComponentKeys.MAX_COMPONENT_KEY_LENGTH; import static org.sonar.db.component.ComponentValidator.MAX_COMPONENT_NAME_LENGTH; import static org.sonar.db.permission.GlobalPermission.PROVISION_PROJECTS; import static org.sonar.server.component.NewComponent.newComponentBuilder; -import static org.sonar.server.newcodeperiod.NewCodePeriodUtils.NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION; -import static org.sonar.server.newcodeperiod.NewCodePeriodUtils.NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION; -import static org.sonar.server.newcodeperiod.NewCodePeriodUtils.getNewCodeDefinitionValueProjectCreation; -import static org.sonar.server.newcodeperiod.NewCodePeriodUtils.validateType; +import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION; +import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION; +import static org.sonar.server.newcodeperiod.NewCodeDefinitionResolver.checkNewCodeDefinitionParam; 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.project.ProjectsWsParameters.ACTION_CREATE; @@ -68,18 +63,18 @@ public class CreateAction implements ProjectsWsAction { private final UserSession userSession; private final ComponentUpdater componentUpdater; private final ProjectDefaultVisibility projectDefaultVisibility; - private final PlatformEditionProvider editionProvider; private final DefaultBranchNameResolver defaultBranchNameResolver; + private final NewCodeDefinitionResolver newCodeDefinitionResolver; + public CreateAction(DbClient dbClient, UserSession userSession, ComponentUpdater componentUpdater, - ProjectDefaultVisibility projectDefaultVisibility, PlatformEditionProvider editionProvider, - DefaultBranchNameResolver defaultBranchNameResolver) { + ProjectDefaultVisibility projectDefaultVisibility, DefaultBranchNameResolver defaultBranchNameResolver, NewCodeDefinitionResolver newCodeDefinitionResolver) { this.dbClient = dbClient; this.userSession = userSession; this.componentUpdater = componentUpdater; this.projectDefaultVisibility = projectDefaultVisibility; - this.editionProvider = editionProvider; this.defaultBranchNameResolver = defaultBranchNameResolver; + this.newCodeDefinitionResolver = newCodeDefinitionResolver; } @Override @@ -125,7 +120,6 @@ public class CreateAction implements ProjectsWsAction { action.createParam(PARAM_NEW_CODE_DEFINITION_VALUE) .setDescription(NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION) .setSince("10.1"); - } @Override @@ -137,21 +131,18 @@ public class CreateAction implements ProjectsWsAction { private CreateWsResponse doHandle(CreateRequest request) { try (DbSession dbSession = dbClient.openSession(false)) { userSession.checkPermission(PROVISION_PROJECTS); - checkNewCodeDefinitionParam(request); + checkNewCodeDefinitionParam(request.getNewCodeDefinitionType(), request.getNewCodeDefinitionValue()); ComponentDto componentDto = createProject(request, dbSession); - if(request.getNewCodeDefinitionType() != null) { - createNewCodeDefinition(dbSession, request, componentDto.uuid()); + if (request.getNewCodeDefinitionType() != null) { + String defaultBranchName = Optional.ofNullable(request.getMainBranchKey()).orElse(defaultBranchNameResolver.getEffectiveMainBranchName()); + newCodeDefinitionResolver.createNewCodeDefinition(dbSession, componentDto.uuid(), defaultBranchName, request.getNewCodeDefinitionType(), + request.getNewCodeDefinitionValue()); } componentUpdater.commitAndIndex(dbSession, componentDto); return toCreateResponse(componentDto); } } - private static void checkNewCodeDefinitionParam(CreateRequest request) { - if (request.getNewCodeDefinitionType() == null && request.getNewCodeDefinitionValue() != null) { - throw new IllegalArgumentException("New code definition type is required when new code definition value is provided"); - } - } private ComponentDto createProject(CreateRequest request, DbSession dbSession) { String visibility = request.getVisibility(); boolean changeToPrivate = visibility == null ? projectDefaultVisibility.get(dbSession).isPrivate() : "private".equals(visibility); @@ -164,32 +155,9 @@ public class CreateAction implements ProjectsWsAction { .build(), userSession.isLoggedIn() ? userSession.getUuid() : null, userSession.isLoggedIn() ? userSession.getLogin() : null, - request.getMainBranchKey(), s -> {}).mainBranchComponent(); - - } - private void createNewCodeDefinition(DbSession dbSession, CreateRequest request, String projectUuid) { - - boolean isCommunityEdition = editionProvider.get().filter(EditionProvider.Edition.COMMUNITY::equals).isPresent(); - NewCodePeriodType newCodePeriodType = validateType(request.getNewCodeDefinitionType(), false, isCommunityEdition); - String newCodePeriodValue = request.getNewCodeDefinitionValue(); - String defaultBranchName = Optional.ofNullable(request.getMainBranchKey()).orElse(defaultBranchNameResolver.getEffectiveMainBranchName()); - - NewCodePeriodDto dto = new NewCodePeriodDto(); - dto.setType(newCodePeriodType); - dto.setProjectUuid(projectUuid); - - if (isCommunityEdition) { - dto.setBranchUuid(projectUuid); - } - - getNewCodeDefinitionValueProjectCreation(newCodePeriodType, newCodePeriodValue, defaultBranchName).ifPresent(dto::setValue); - - if (!CaycUtils.isNewCodePeriodCompliant(dto.getType(), dto.getValue())) { - throw new IllegalArgumentException("Failed to set the New Code Definition. The given value is not compatible with the Clean as You Code methodology. " - + "Please refer to the documentation for compliant options."); - } + request.getMainBranchKey(), s -> { + }).mainBranchComponent(); - dbClient.newCodePeriodDao().insert(dbSession, dto); } private static CreateRequest toCreateRequest(Request request) { diff --git a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 719ef992d5f..e46e8aa5909 100644 --- a/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-webserver/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -146,6 +146,7 @@ import org.sonar.server.monitoring.devops.AzureMetricsTask; import org.sonar.server.monitoring.devops.BitbucketMetricsTask; import org.sonar.server.monitoring.devops.GithubMetricsTask; import org.sonar.server.monitoring.devops.GitlabMetricsTask; +import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver; import org.sonar.server.newcodeperiod.ws.NewCodePeriodsWsModule; import org.sonar.server.notification.NotificationModule; import org.sonar.server.notification.ws.NotificationWsModule; @@ -508,6 +509,7 @@ public class PlatformLevel4 extends PlatformLevel { // New Code Periods new NewCodePeriodsWsModule(), + NewCodeDefinitionResolver.class, // Project Links new ProjectLinksModule(), -- 2.39.5