3 * Copyright (C) 2009-2023 SonarSource SA
4 * mailto:info AT sonarsource DOT com
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 package org.sonar.server.newcodeperiod;
22 import com.google.common.base.Preconditions;
23 import java.util.EnumSet;
24 import java.util.Locale;
25 import java.util.Optional;
26 import javax.annotation.Nullable;
27 import org.sonar.core.platform.EditionProvider;
28 import org.sonar.core.platform.PlatformEditionProvider;
29 import org.sonar.db.DbClient;
30 import org.sonar.db.DbSession;
31 import org.sonar.db.newcodeperiod.NewCodePeriodDto;
32 import org.sonar.db.newcodeperiod.NewCodePeriodParser;
33 import org.sonar.db.newcodeperiod.NewCodePeriodType;
35 import static org.sonar.db.newcodeperiod.NewCodePeriodType.NUMBER_OF_DAYS;
36 import static org.sonar.db.newcodeperiod.NewCodePeriodType.PREVIOUS_VERSION;
37 import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH;
39 public class NewCodeDefinitionResolver {
40 private static final String BEGIN_LIST = "<ul>";
42 private static final String END_LIST = "</ul>";
43 private static final String BEGIN_ITEM_LIST = "<li>";
44 private static final String END_ITEM_LIST = "</li>";
46 public static final String NEW_CODE_PERIOD_TYPE_DESCRIPTION_PROJECT_CREATION = "Project New Code Definition Type<br/>" +
47 "New code definitions of the following types are allowed:" +
49 BEGIN_ITEM_LIST + PREVIOUS_VERSION.name() + END_ITEM_LIST +
50 BEGIN_ITEM_LIST + NUMBER_OF_DAYS.name() + END_ITEM_LIST +
51 BEGIN_ITEM_LIST + REFERENCE_BRANCH.name() + " - will default to the main branch." + END_ITEM_LIST +
54 public static final String NEW_CODE_PERIOD_VALUE_DESCRIPTION_PROJECT_CREATION = "Project New Code Definition Value<br/>" +
55 "For each new code definition type, a different value is expected:" +
57 BEGIN_ITEM_LIST + "no value, when the new code definition type is " + PREVIOUS_VERSION.name() + " and " + REFERENCE_BRANCH.name() + END_ITEM_LIST +
58 BEGIN_ITEM_LIST + "a number between 1 and 90, when the new code definition type is " + NUMBER_OF_DAYS.name() + END_ITEM_LIST +
61 private static final String UNEXPECTED_VALUE_ERROR_MESSAGE = "Unexpected value for newCodeDefinitionType '%s'";
63 private static final EnumSet<NewCodePeriodType> projectCreationNCDTypes = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS, REFERENCE_BRANCH);
65 private final DbClient dbClient;
66 private final PlatformEditionProvider editionProvider;
68 public NewCodeDefinitionResolver(DbClient dbClient, PlatformEditionProvider editionProvider) {
69 this.dbClient = dbClient;
70 this.editionProvider = editionProvider;
73 public void createNewCodeDefinition(DbSession dbSession, String projectUuid, String defaultBranchName, String newCodeDefinitionType, String newCodeDefinitionValue) {
75 boolean isCommunityEdition = editionProvider.get().filter(EditionProvider.Edition.COMMUNITY::equals).isPresent();
76 NewCodePeriodType newCodePeriodType = parseNewCodeDefinitionType(newCodeDefinitionType);
78 NewCodePeriodDto dto = new NewCodePeriodDto();
79 dto.setType(newCodePeriodType);
80 dto.setProjectUuid(projectUuid);
82 if (isCommunityEdition) {
83 dto.setBranchUuid(projectUuid);
86 getNewCodeDefinitionValueProjectCreation(newCodePeriodType, newCodeDefinitionValue, defaultBranchName).ifPresent(dto::setValue);
88 if (!CaycUtils.isNewCodePeriodCompliant(dto.getType(), dto.getValue())) {
89 throw new IllegalArgumentException("Failed to set the New Code Definition. The given value is not compatible with the Clean as You Code methodology. "
90 + "Please refer to the documentation for compliant options.");
93 dbClient.newCodePeriodDao().insert(dbSession, dto);
96 public static void checkNewCodeDefinitionParam(@Nullable String newCodeDefinitionType, @Nullable String newCodeDefinitionValue) {
97 if (newCodeDefinitionType == null && newCodeDefinitionValue != null) {
98 throw new IllegalArgumentException("New code definition type is required when new code definition value is provided");
102 private static Optional<String> getNewCodeDefinitionValueProjectCreation(NewCodePeriodType type, @Nullable String value, String defaultBranchName) {
103 return switch (type) {
104 case PREVIOUS_VERSION -> {
105 Preconditions.checkArgument(value == null, UNEXPECTED_VALUE_ERROR_MESSAGE, type);
106 yield Optional.empty();
108 case NUMBER_OF_DAYS -> {
109 requireValue(type, value);
110 yield Optional.of(parseDays(value));
112 case REFERENCE_BRANCH -> {
113 Preconditions.checkArgument(value == null, UNEXPECTED_VALUE_ERROR_MESSAGE, type);
114 yield Optional.of(defaultBranchName);
116 default -> throw new IllegalStateException("Unexpected type: " + type);
120 private static String parseDays(String value) {
122 return Integer.toString(NewCodePeriodParser.parseDays(value));
123 } catch (Exception e) {
124 throw new IllegalArgumentException("Failed to parse number of days: " + value);
128 private static void requireValue(NewCodePeriodType type, @Nullable String value) {
129 Preconditions.checkArgument(value != null, "New code definition type '%s' requires a newCodeDefinitionValue", type);
132 private static NewCodePeriodType parseNewCodeDefinitionType(String typeStr) {
133 NewCodePeriodType type;
135 type = NewCodePeriodType.valueOf(typeStr.toUpperCase(Locale.US));
136 } catch (IllegalArgumentException e) {
137 throw new IllegalArgumentException("Invalid type: " + typeStr);
143 private static void validateType(NewCodePeriodType type) {
144 Preconditions.checkArgument(projectCreationNCDTypes.contains(type), "Invalid type '%s'. `newCodeDefinitionType` can only be set with types: %s",
145 type, projectCreationNCDTypes);