* SONAR-12396 List new code periods for all branches with effective current values * SONAR-12347 Migrate old definitions of leak periodtags/8.0
@@ -101,6 +101,7 @@ import org.sonar.ce.task.projectanalysis.measure.MeasureToMeasureDto; | |||
import org.sonar.ce.task.projectanalysis.metric.MetricModule; | |||
import org.sonar.ce.task.projectanalysis.notification.NotificationFactory; | |||
import org.sonar.ce.task.projectanalysis.organization.DefaultOrganizationLoader; | |||
import org.sonar.ce.task.projectanalysis.period.NewCodePeriodResolver; | |||
import org.sonar.ce.task.projectanalysis.period.PeriodHolderImpl; | |||
import org.sonar.ce.task.projectanalysis.qualitygate.EvaluationResultTextConverterImpl; | |||
import org.sonar.ce.task.projectanalysis.qualitygate.QualityGateHolderImpl; | |||
@@ -285,6 +286,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop | |||
BranchPersisterImpl.class, | |||
SiblingsIssuesLoader.class, | |||
SiblingsIssueMerger.class, | |||
NewCodePeriodResolver.class, | |||
// filemove | |||
ScoreMatrixDumperImpl.class, |
@@ -0,0 +1,190 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 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.ce.task.projectanalysis.period; | |||
import java.time.Duration; | |||
import java.time.Instant; | |||
import java.time.temporal.ChronoUnit; | |||
import java.util.List; | |||
import java.util.Optional; | |||
import java.util.function.Supplier; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.utils.DateUtils; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.component.SnapshotDto; | |||
import org.sonar.db.component.SnapshotQuery; | |||
import org.sonar.db.event.EventDto; | |||
import org.sonar.db.newcodeperiod.NewCodePeriodDto; | |||
import org.sonar.db.newcodeperiod.NewCodePeriodParser; | |||
import org.sonar.db.newcodeperiod.NewCodePeriodType; | |||
import static com.google.common.base.Preconditions.checkNotNull; | |||
import static com.google.common.base.Preconditions.checkState; | |||
import static java.lang.String.format; | |||
import static org.sonar.db.component.SnapshotDto.STATUS_PROCESSED; | |||
import static org.sonar.db.component.SnapshotQuery.SORT_FIELD.BY_DATE; | |||
import static org.sonar.db.component.SnapshotQuery.SORT_ORDER.ASC; | |||
public class NewCodePeriodResolver { | |||
private static final Logger LOG = Loggers.get(NewCodePeriodResolver.class); | |||
private final DbClient dbClient; | |||
public NewCodePeriodResolver(DbClient dbClient) { | |||
this.dbClient = dbClient; | |||
} | |||
public Period resolve(DbSession dbSession, String branchUuid, NewCodePeriodDto newCodePeriodDto, long referenceDate, String projectVersion) { | |||
return toPeriod(newCodePeriodDto.getType(), newCodePeriodDto.getValue(), dbSession, projectVersion, branchUuid, referenceDate); | |||
} | |||
private Period toPeriod(NewCodePeriodType type, @Nullable String value, DbSession dbSession, String projectVersion, String rootUuid, long referenceDate) { | |||
switch (type) { | |||
case NUMBER_OF_DAYS: | |||
Integer days = NewCodePeriodParser.parseDays(value); | |||
checkNotNullValue(value, type); | |||
return resolveByDays(dbSession, rootUuid, days, value, referenceDate); | |||
case PREVIOUS_VERSION: | |||
return resolveByPreviousVersion(dbSession, rootUuid, projectVersion); | |||
case SPECIFIC_ANALYSIS: | |||
checkNotNullValue(value, type); | |||
return resolveBySpecificAnalysis(dbSession, rootUuid, value); | |||
default: | |||
throw new IllegalStateException("Unexpected type: " + type); | |||
} | |||
} | |||
private Period resolveBySpecificAnalysis(DbSession dbSession, String rootUuid, String value) { | |||
SnapshotDto baseline = dbClient.snapshotDao().selectByUuid(dbSession, value) | |||
.filter(t -> t.getComponentUuid().equals(rootUuid)) | |||
.orElseThrow(() -> new IllegalStateException("Analysis '" + value + "' of project '" + rootUuid | |||
+ "' defined as the baseline does not exist")); | |||
LOG.debug("Resolving new code period with a specific analysis"); | |||
return newPeriod(NewCodePeriodType.SPECIFIC_ANALYSIS, value, Instant.ofEpochMilli(baseline.getCreatedAt())); | |||
} | |||
private Period resolveByPreviousVersion(DbSession dbSession, String projectUuid, String projectVersion) { | |||
List<EventDto> versions = dbClient.eventDao().selectVersionsByMostRecentFirst(dbSession, projectUuid); | |||
if (versions.isEmpty()) { | |||
return findOldestAnalysis(dbSession, projectUuid); | |||
} | |||
String mostRecentVersion = Optional.ofNullable(versions.iterator().next().getName()) | |||
.orElseThrow(() -> new IllegalStateException("selectVersionsByMostRecentFirst returned a DTO which didn't have a name")); | |||
if (versions.size() == 1) { | |||
return findOldestAnalysis(dbSession, projectUuid); | |||
} | |||
return resolvePreviousVersion(dbSession, projectVersion, versions, mostRecentVersion); | |||
} | |||
private Period resolveByDays(DbSession dbSession, String rootUuid, Integer days, String value, long referenceDate) { | |||
checkPeriodProperty(days > 0, value, "number of days is <= 0"); | |||
List<SnapshotDto> snapshots = dbClient.snapshotDao().selectAnalysesByQuery(dbSession, createCommonQuery(rootUuid) | |||
.setCreatedBefore(referenceDate).setSort(BY_DATE, ASC)); | |||
ensureNotOnFirstAnalysis(!snapshots.isEmpty()); | |||
Instant targetDate = DateUtils.addDays(Instant.ofEpochMilli(referenceDate), -days); | |||
LOG.debug("Resolving new code period by {} days: {}", days, supplierToString(() -> logDate(targetDate))); | |||
SnapshotDto snapshot = findNearestSnapshotToTargetDate(snapshots, targetDate); | |||
return newPeriod(NewCodePeriodType.NUMBER_OF_DAYS, String.valueOf((int) days), Instant.ofEpochMilli(snapshot.getCreatedAt())); | |||
} | |||
private Period resolvePreviousVersion(DbSession dbSession, String currentVersion, List<EventDto> versions, String mostRecentVersion) { | |||
EventDto previousVersion = versions.get(currentVersion.equals(mostRecentVersion) ? 1 : 0); | |||
LOG.debug("Resolving new code period by previous version: {}", previousVersion.getName()); | |||
return newPeriod(dbSession, previousVersion); | |||
} | |||
private Period findOldestAnalysis(DbSession dbSession, String projectUuid) { | |||
LOG.debug("Resolving first analysis as new code period as there is only one existing version"); | |||
Optional<Period> period = dbClient.snapshotDao().selectOldestSnapshot(dbSession, projectUuid) | |||
.map(dto -> newPeriod(NewCodePeriodType.PREVIOUS_VERSION, null, Instant.ofEpochMilli(dto.getCreatedAt()))); | |||
ensureNotOnFirstAnalysis(period.isPresent()); | |||
return period.get(); | |||
} | |||
private Period newPeriod(DbSession dbSession, EventDto previousVersion) { | |||
Optional<Period> period = dbClient.snapshotDao().selectByUuid(dbSession, previousVersion.getAnalysisUuid()) | |||
.map(dto -> newPeriod(NewCodePeriodType.PREVIOUS_VERSION, dto.getProjectVersion(), Instant.ofEpochMilli(dto.getCreatedAt()))); | |||
if (!period.isPresent()) { | |||
throw new IllegalStateException(format("Analysis '%s' for version event '%s' has been deleted", | |||
previousVersion.getAnalysisUuid(), previousVersion.getName())); | |||
} | |||
return period.get(); | |||
} | |||
private static Period newPeriod(NewCodePeriodType type, @Nullable String value, Instant instant) { | |||
return new Period(type.name(), value, instant.toEpochMilli()); | |||
} | |||
private static Object supplierToString(Supplier<String> s) { | |||
return new Object() { | |||
@Override | |||
public String toString() { | |||
return s.get(); | |||
} | |||
}; | |||
} | |||
private static SnapshotQuery createCommonQuery(String projectUuid) { | |||
return new SnapshotQuery().setComponentUuid(projectUuid).setStatus(STATUS_PROCESSED); | |||
} | |||
private static SnapshotDto findNearestSnapshotToTargetDate(List<SnapshotDto> snapshots, Instant targetDate) { | |||
// FIXME shouldn't this be the first analysis after targetDate? | |||
Duration bestDuration = null; | |||
SnapshotDto nearest = null; | |||
for (SnapshotDto snapshot : snapshots) { | |||
Instant createdAt = Instant.ofEpochMilli(snapshot.getCreatedAt()); | |||
Duration duration = Duration.between(targetDate, createdAt).abs(); | |||
if (bestDuration == null || duration.compareTo(bestDuration) <= 0) { | |||
bestDuration = duration; | |||
nearest = snapshot; | |||
} | |||
} | |||
return nearest; | |||
} | |||
private static void checkPeriodProperty(boolean test, String propertyValue, String testDescription, Object... args) { | |||
if (!test) { | |||
LOG.debug("Invalid code period '{}': {}", propertyValue, supplierToString(() -> format(testDescription, args))); | |||
throw MessageException.of(format("Invalid new code period. '%s' is not one of: " + | |||
"integer > 0, date before current analysis j, \"previous_version\", or version string that exists in the project' \n" + | |||
"Please contact a project administrator to correct this setting", propertyValue)); | |||
} | |||
} | |||
private static void ensureNotOnFirstAnalysis(boolean expression) { | |||
checkState(expression, "Attempting to resolve period while no analysis exist for project"); | |||
} | |||
private static void checkNotNullValue(@Nullable String value, NewCodePeriodType type) { | |||
checkNotNull(value, "Value can't be null with type %s", type); | |||
} | |||
private static String logDate(Instant instant) { | |||
return DateUtils.formatDate(instant.truncatedTo(ChronoUnit.SECONDS)); | |||
} | |||
} |
@@ -19,39 +19,19 @@ | |||
*/ | |||
package org.sonar.ce.task.projectanalysis.step; | |||
import java.time.Duration; | |||
import java.time.Instant; | |||
import java.time.temporal.ChronoUnit; | |||
import java.util.List; | |||
import java.util.Optional; | |||
import java.util.function.Supplier; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.utils.DateUtils; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; | |||
import org.sonar.ce.task.projectanalysis.component.TreeRootHolder; | |||
import org.sonar.ce.task.projectanalysis.period.NewCodePeriodResolver; | |||
import org.sonar.ce.task.projectanalysis.period.Period; | |||
import org.sonar.ce.task.projectanalysis.period.PeriodHolder; | |||
import org.sonar.ce.task.projectanalysis.period.PeriodHolderImpl; | |||
import org.sonar.ce.task.step.ComputationStep; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.component.SnapshotDto; | |||
import org.sonar.db.component.SnapshotQuery; | |||
import org.sonar.db.event.EventDto; | |||
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 static com.google.common.base.Preconditions.checkNotNull; | |||
import static com.google.common.base.Preconditions.checkState; | |||
import static java.lang.String.format; | |||
import static org.sonar.db.component.SnapshotDto.STATUS_PROCESSED; | |||
import static org.sonar.db.component.SnapshotQuery.SORT_FIELD.BY_DATE; | |||
import static org.sonar.db.component.SnapshotQuery.SORT_ORDER.ASC; | |||
/** | |||
* Populates the {@link PeriodHolder} | |||
@@ -62,21 +42,22 @@ import static org.sonar.db.component.SnapshotQuery.SORT_ORDER.ASC; | |||
* - If a snapshot is found, a period is set to the repository, otherwise fail with MessageException | |||
*/ | |||
public class LoadPeriodsStep implements ComputationStep { | |||
private static final Logger LOG = Loggers.get(LoadPeriodsStep.class); | |||
private final AnalysisMetadataHolder analysisMetadataHolder; | |||
private final NewCodePeriodDao newCodePeriodDao; | |||
private final TreeRootHolder treeRootHolder; | |||
private final PeriodHolderImpl periodsHolder; | |||
private final DbClient dbClient; | |||
private final NewCodePeriodResolver resolver; | |||
public LoadPeriodsStep(AnalysisMetadataHolder analysisMetadataHolder, NewCodePeriodDao newCodePeriodDao, TreeRootHolder treeRootHolder, | |||
PeriodHolderImpl periodsHolder, DbClient dbClient) { | |||
PeriodHolderImpl periodsHolder, DbClient dbClient, NewCodePeriodResolver resolver) { | |||
this.analysisMetadataHolder = analysisMetadataHolder; | |||
this.newCodePeriodDao = newCodePeriodDao; | |||
this.treeRootHolder = treeRootHolder; | |||
this.periodsHolder = periodsHolder; | |||
this.dbClient = dbClient; | |||
this.resolver = resolver; | |||
} | |||
@Override | |||
@@ -101,8 +82,9 @@ public class LoadPeriodsStep implements ComputationStep { | |||
() -> getProjectSetting(dbSession, projectUuid), | |||
() -> getGlobalSetting(dbSession)); | |||
Period period = dto.map(d -> toPeriod(d.getType(), d.getValue(), dbSession, projectVersion, branchUuid)) | |||
.orElseGet(() -> toPeriod(NewCodePeriodType.PREVIOUS_VERSION, null, dbSession, projectVersion, branchUuid)); | |||
long analysisDate = analysisMetadataHolder.getAnalysisDate(); | |||
Period period = dto.map(d -> resolver.resolve(dbSession, branchUuid, d, analysisDate, projectVersion)) | |||
.orElseGet(() -> resolver.resolve(dbSession, branchUuid, NewCodePeriodDto.defaultInstance(), analysisDate, projectVersion)); | |||
periodsHolder.setPeriod(period); | |||
} | |||
} | |||
@@ -129,137 +111,7 @@ public class LoadPeriodsStep implements ComputationStep { | |||
return newCodePeriodDao.selectGlobal(dbSession); | |||
} | |||
private Period toPeriod(NewCodePeriodType type, @Nullable String value, DbSession dbSession, String analysisProjectVersion, String rootUuid) { | |||
switch (type) { | |||
case NUMBER_OF_DAYS: | |||
Integer days = NewCodePeriodParser.parseDays(value); | |||
checkNotNullValue(value, type); | |||
return resolveByDays(dbSession, rootUuid, days, value); | |||
case PREVIOUS_VERSION: | |||
return resolveByPreviousVersion(dbSession, rootUuid, analysisProjectVersion); | |||
case SPECIFIC_ANALYSIS: | |||
checkNotNullValue(value, type); | |||
return resolveBySpecificAnalysis(dbSession, rootUuid, value); | |||
default: | |||
throw new IllegalStateException("Unexpected type: " + type); | |||
} | |||
} | |||
private String getProjectBranchUuid() { | |||
return analysisMetadataHolder.getProject().getUuid(); | |||
} | |||
private Period resolveBySpecificAnalysis(DbSession dbSession, String rootUuid, String value) { | |||
SnapshotDto baseline = dbClient.snapshotDao().selectByUuid(dbSession, value) | |||
.filter(t -> t.getComponentUuid().equals(rootUuid)) | |||
.orElseThrow(() -> new IllegalStateException("Analysis '" + value + "' of project '" + rootUuid | |||
+ "' defined as the baseline does not exist")); | |||
LOG.debug("Resolving new code period with a specific analysis"); | |||
return newPeriod(NewCodePeriodType.SPECIFIC_ANALYSIS, value, Instant.ofEpochMilli(baseline.getCreatedAt())); | |||
} | |||
private Period resolveByPreviousVersion(DbSession dbSession, String projectUuid, String analysisProjectVersion) { | |||
List<EventDto> versions = dbClient.eventDao().selectVersionsByMostRecentFirst(dbSession, projectUuid); | |||
if (versions.isEmpty()) { | |||
return findOldestAnalysis(dbSession, projectUuid); | |||
} | |||
String mostRecentVersion = Optional.ofNullable(versions.iterator().next().getName()) | |||
.orElseThrow(() -> new IllegalStateException("selectVersionsByMostRecentFirst returned a DTO which didn't have a name")); | |||
if (versions.size() == 1) { | |||
return findOldestAnalysis(dbSession, projectUuid); | |||
} | |||
return resolvePreviousVersion(dbSession, analysisProjectVersion, versions, mostRecentVersion); | |||
} | |||
private Period resolveByDays(DbSession dbSession, String rootUuid, Integer days, String propertyValue) { | |||
checkPeriodProperty(days > 0, propertyValue, "number of days is <= 0"); | |||
long analysisDate = analysisMetadataHolder.getAnalysisDate(); | |||
List<SnapshotDto> snapshots = dbClient.snapshotDao().selectAnalysesByQuery(dbSession, createCommonQuery(rootUuid) | |||
.setCreatedBefore(analysisDate).setSort(BY_DATE, ASC)); | |||
ensureNotOnFirstAnalysis(!snapshots.isEmpty()); | |||
Instant targetDate = DateUtils.addDays(Instant.ofEpochMilli(analysisDate), -days); | |||
LOG.debug("Resolving new code period by {} days: {}", days, supplierToString(() -> logDate(targetDate))); | |||
SnapshotDto snapshot = findNearestSnapshotToTargetDate(snapshots, targetDate); | |||
return newPeriod(NewCodePeriodType.NUMBER_OF_DAYS, String.valueOf((int) days), Instant.ofEpochMilli(snapshot.getCreatedAt())); | |||
} | |||
private Period resolvePreviousVersion(DbSession dbSession, String currentVersion, List<EventDto> versions, String mostRecentVersion) { | |||
EventDto previousVersion = versions.get(currentVersion.equals(mostRecentVersion) ? 1 : 0); | |||
LOG.debug("Resolving new code period by previous version: {}", previousVersion.getName()); | |||
return newPeriod(dbSession, previousVersion); | |||
} | |||
private Period findOldestAnalysis(DbSession dbSession, String projectUuid) { | |||
LOG.debug("Resolving first analysis as new code period as there is only one existing version"); | |||
Optional<Period> period = dbClient.snapshotDao().selectOldestSnapshot(dbSession, projectUuid) | |||
.map(dto -> newPeriod(NewCodePeriodType.PREVIOUS_VERSION, null, Instant.ofEpochMilli(dto.getCreatedAt()))); | |||
ensureNotOnFirstAnalysis(period.isPresent()); | |||
return period.get(); | |||
} | |||
private Period newPeriod(DbSession dbSession, EventDto previousVersion) { | |||
Optional<Period> period = dbClient.snapshotDao().selectByUuid(dbSession, previousVersion.getAnalysisUuid()) | |||
.map(dto -> newPeriod(NewCodePeriodType.PREVIOUS_VERSION, dto.getProjectVersion(), Instant.ofEpochMilli(dto.getCreatedAt()))); | |||
if (!period.isPresent()) { | |||
throw new IllegalStateException(format("Analysis '%s' for version event '%s' has been deleted", | |||
previousVersion.getAnalysisUuid(), previousVersion.getName())); | |||
} | |||
return period.get(); | |||
} | |||
private static Period newPeriod(NewCodePeriodType type, @Nullable String value, Instant instant) { | |||
return new Period(type.name(), value, instant.toEpochMilli()); | |||
} | |||
private static Object supplierToString(Supplier<String> s) { | |||
return new Object() { | |||
@Override | |||
public String toString() { | |||
return s.get(); | |||
} | |||
}; | |||
} | |||
private static SnapshotQuery createCommonQuery(String projectUuid) { | |||
return new SnapshotQuery().setComponentUuid(projectUuid).setStatus(STATUS_PROCESSED); | |||
} | |||
private static SnapshotDto findNearestSnapshotToTargetDate(List<SnapshotDto> snapshots, Instant targetDate) { | |||
// FIXME shouldn't this be the first analysis after targetDate? | |||
Duration bestDuration = null; | |||
SnapshotDto nearest = null; | |||
for (SnapshotDto snapshot : snapshots) { | |||
Instant createdAt = Instant.ofEpochMilli(snapshot.getCreatedAt()); | |||
Duration duration = Duration.between(targetDate, createdAt).abs(); | |||
if (bestDuration == null || duration.compareTo(bestDuration) <= 0) { | |||
bestDuration = duration; | |||
nearest = snapshot; | |||
} | |||
} | |||
return nearest; | |||
} | |||
private static void checkPeriodProperty(boolean test, String propertyValue, String testDescription, Object... args) { | |||
if (!test) { | |||
LOG.debug("Invalid code period '{}': {}", propertyValue, supplierToString(() -> format(testDescription, args))); | |||
throw MessageException.of(format("Invalid new code period. '%s' is not one of: " + | |||
"integer > 0, date before current analysis j, \"previous_version\", or version string that exists in the project' \n" + | |||
"Please contact a project administrator to correct this setting", propertyValue)); | |||
} | |||
} | |||
private static void ensureNotOnFirstAnalysis(boolean expression) { | |||
checkState(expression, "Attempting to resolve period while no analysis exist for project"); | |||
} | |||
private static void checkNotNullValue(@Nullable String value, NewCodePeriodType type) { | |||
checkNotNull(value, "Value can't be null with type %s", type); | |||
} | |||
private static String logDate(Instant instant) { | |||
return DateUtils.formatDate(instant.truncatedTo(ChronoUnit.SECONDS)); | |||
} | |||
} |
@@ -44,6 +44,7 @@ import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder; | |||
import org.sonar.ce.task.projectanalysis.component.Component; | |||
import org.sonar.ce.task.projectanalysis.component.ReportComponent; | |||
import org.sonar.ce.task.projectanalysis.component.TreeRootHolderRule; | |||
import org.sonar.ce.task.projectanalysis.period.NewCodePeriodResolver; | |||
import org.sonar.ce.task.projectanalysis.period.Period; | |||
import org.sonar.ce.task.projectanalysis.period.PeriodHolderImpl; | |||
import org.sonar.ce.task.step.ComputationStep; | |||
@@ -86,9 +87,10 @@ public class LoadPeriodsStepTest extends BaseStepTest { | |||
private PeriodHolderImpl periodsHolder = new PeriodHolderImpl(); | |||
private System2 system2Mock = mock(System2.class); | |||
private NewCodePeriodDao dao = new NewCodePeriodDao(system2Mock, new SequenceUuidFactory()); | |||
private NewCodePeriodResolver newCodePeriodResolver = new NewCodePeriodResolver(dbTester.getDbClient()); | |||
private ZonedDateTime analysisDate = ZonedDateTime.of(2019, 3, 20, 5, 30, 40, 0, ZoneId.systemDefault()); | |||
private LoadPeriodsStep underTest = new LoadPeriodsStep(analysisMetadataHolder, dao, treeRootHolder, periodsHolder, dbTester.getDbClient()); | |||
private LoadPeriodsStep underTest = new LoadPeriodsStep(analysisMetadataHolder, dao, treeRootHolder, periodsHolder, dbTester.getDbClient(), newCodePeriodResolver); | |||
private OrganizationDto organization; | |||
private ComponentDto project; | |||
@@ -373,7 +375,7 @@ public class LoadPeriodsStepTest extends BaseStepTest { | |||
@DataProvider | |||
public static Object[][] zeroOrLess() { | |||
return new Object[][] { | |||
return new Object[][]{ | |||
{0}, | |||
{-1 - new Random().nextInt(30)} | |||
}; | |||
@@ -381,7 +383,7 @@ public class LoadPeriodsStepTest extends BaseStepTest { | |||
@DataProvider | |||
public static Object[][] stringConsideredAsVersions() { | |||
return new Object[][] { | |||
return new Object[][]{ | |||
{randomAlphabetic(5)}, | |||
{"1,3"}, | |||
{"1.3"}, | |||
@@ -393,7 +395,7 @@ public class LoadPeriodsStepTest extends BaseStepTest { | |||
@DataProvider | |||
public static Object[][] projectVersionNullOrNot() { | |||
return new Object[][] { | |||
return new Object[][]{ | |||
{null}, | |||
{randomAlphabetic(15)}, | |||
}; | |||
@@ -401,7 +403,7 @@ public class LoadPeriodsStepTest extends BaseStepTest { | |||
@DataProvider | |||
public static Object[][] anyValidLeakPeriodSettingValue() { | |||
return new Object[][] { | |||
return new Object[][]{ | |||
// days | |||
{"100"}, | |||
// previous_version keyword |
@@ -19,6 +19,7 @@ | |||
*/ | |||
package org.sonar.db.newcodeperiod; | |||
import java.util.List; | |||
import java.util.Optional; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.utils.System2; | |||
@@ -76,6 +77,11 @@ public class NewCodePeriodDao implements Dao { | |||
return Optional.ofNullable(mapper(dbSession).selectByProject(projectUuid)); | |||
} | |||
public List<NewCodePeriodDto> selectAllByProject(DbSession dbSession, String projectUuid) { | |||
requireNonNull(projectUuid, "Project uuid must be specified."); | |||
return mapper(dbSession).selectAllByProject(projectUuid); | |||
} | |||
public Optional<NewCodePeriodDto> selectByBranch(DbSession dbSession, String projectUuid, String branchUuid) { | |||
requireNonNull(projectUuid, "Project uuid must be specified."); | |||
requireNonNull(branchUuid, "Branch uuid must be specified."); |
@@ -30,6 +30,10 @@ public class NewCodePeriodDto { | |||
private long updatedAt; | |||
private long createdAt; | |||
public static NewCodePeriodDto defaultInstance() { | |||
return new NewCodePeriodDto().setType(NewCodePeriodType.PREVIOUS_VERSION); | |||
} | |||
public long getCreatedAt() { | |||
return createdAt; | |||
} |
@@ -19,6 +19,7 @@ | |||
*/ | |||
package org.sonar.db.newcodeperiod; | |||
import java.util.List; | |||
import java.util.Optional; | |||
import org.apache.ibatis.annotations.Param; | |||
@@ -39,4 +40,6 @@ public interface NewCodePeriodMapper { | |||
NewCodePeriodDto selectByBranch(@Param("projectUuid") String projectUuid, @Param("branchUuid") String branchUuid); | |||
long countByProjectAnalysis(String projectAnalysisUuid); | |||
List<NewCodePeriodDto> selectAllByProject(String projectUuid); | |||
} |
@@ -22,6 +22,5 @@ package org.sonar.db.newcodeperiod; | |||
public enum NewCodePeriodType { | |||
PREVIOUS_VERSION, | |||
NUMBER_OF_DAYS, | |||
DATE, | |||
SPECIFIC_ANALYSIS | |||
} |
@@ -79,6 +79,14 @@ | |||
AND ncp.branch_uuid is null | |||
</select> | |||
<select id="selectAllByProject" parameterType="map" resultType="org.sonar.db.newcodeperiod.NewCodePeriodDto"> | |||
SELECT | |||
<include refid="newCodePeriodMapperColumns"/> | |||
FROM new_code_periods ncp | |||
WHERE | |||
ncp.project_uuid=#{projectUuid, jdbcType=VARCHAR} | |||
</select> | |||
<select id="selectByBranch" parameterType="map" resultType="org.sonar.db.newcodeperiod.NewCodePeriodDto"> | |||
SELECT | |||
<include refid="newCodePeriodMapperColumns"/> |
@@ -39,15 +39,15 @@ public class NewCodePeriodDtoTest { | |||
.setBranchUuid("branchUuid") | |||
.setCreatedAt(currentTime) | |||
.setUpdatedAt(currentTime) | |||
.setType(NewCodePeriodType.DATE) | |||
.setValue("2018-01-02"); | |||
.setType(NewCodePeriodType.NUMBER_OF_DAYS) | |||
.setValue("1"); | |||
assertThat(newCodePeriodDto.getUuid()).isEqualTo("uuid"); | |||
assertThat(newCodePeriodDto.getProjectUuid()).isEqualTo("projectUuid"); | |||
assertThat(newCodePeriodDto.getBranchUuid()).isEqualTo("branchUuid"); | |||
assertThat(newCodePeriodDto.getCreatedAt()).isEqualTo(currentTime); | |||
assertThat(newCodePeriodDto.getUpdatedAt()).isEqualTo(currentTime); | |||
assertThat(newCodePeriodDto.getType()).isEqualTo(NewCodePeriodType.DATE); | |||
assertThat(newCodePeriodDto.getValue()).isEqualTo("2018-01-02"); | |||
assertThat(newCodePeriodDto.getType()).isEqualTo(NewCodePeriodType.NUMBER_OF_DAYS); | |||
assertThat(newCodePeriodDto.getValue()).isEqualTo("1"); | |||
} | |||
} |
@@ -563,7 +563,7 @@ public class PurgeCommandsTest { | |||
dbTester.newCodePeriods().insert(NewCodePeriodType.PREVIOUS_VERSION, null); | |||
//project settings | |||
dbTester.newCodePeriods().insert(project.uuid(), NewCodePeriodType.DATE, "2019-01-01"); | |||
dbTester.newCodePeriods().insert(project.uuid(), NewCodePeriodType.NUMBER_OF_DAYS, "20"); | |||
//branch settings | |||
dbTester.newCodePeriods().insert(project.uuid(), branch.getUuid(), NewCodePeriodType.NUMBER_OF_DAYS, "1"); | |||
@@ -586,7 +586,7 @@ public class PurgeCommandsTest { | |||
dbTester.newCodePeriods().insert(NewCodePeriodType.PREVIOUS_VERSION, null); | |||
//project settings | |||
dbTester.newCodePeriods().insert(project.uuid(), NewCodePeriodType.DATE, "2019-01-01"); | |||
dbTester.newCodePeriods().insert(project.uuid(), NewCodePeriodType.NUMBER_OF_DAYS, "20"); | |||
//branch settings | |||
dbTester.newCodePeriods().insert(project.uuid(), branch.getUuid(), NewCodePeriodType.NUMBER_OF_DAYS, "1"); | |||
@@ -609,7 +609,7 @@ public class PurgeCommandsTest { | |||
dbTester.newCodePeriods().insert(NewCodePeriodType.PREVIOUS_VERSION, null); | |||
//project settings | |||
dbTester.newCodePeriods().insert(project.uuid(), NewCodePeriodType.DATE, "2019-01-01"); | |||
dbTester.newCodePeriods().insert(project.uuid(), NewCodePeriodType.NUMBER_OF_DAYS, "20"); | |||
//branch settings | |||
dbTester.newCodePeriods().insert(project.uuid(), branch.getUuid(), NewCodePeriodType.NUMBER_OF_DAYS, "1"); |
@@ -20,14 +20,30 @@ | |||
package org.sonar.server.platform.db.migration.version.v80; | |||
import java.sql.SQLException; | |||
import java.time.LocalDate; | |||
import java.time.ZoneId; | |||
import java.time.format.DateTimeParseException; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import java.util.Optional; | |||
import java.util.stream.Collectors; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
import org.sonar.core.util.UuidFactory; | |||
import org.sonar.db.Database; | |||
import org.sonar.server.platform.db.migration.step.DataChange; | |||
import org.sonar.server.platform.db.migration.step.Upsert; | |||
public class PopulateNewCodePeriodTable extends DataChange { | |||
private static final Logger LOG = Loggers.get(PopulateNewCodePeriodTable.class); | |||
private static final String TYPE_PREVIOUS_VERSION = "PREVIOUS_VERSION"; | |||
private static final String TYPE_NUMBER_OF_DAYS = "NUMBER_OF_DAYS"; | |||
private static final String TYPE_SPECIFIC_ANALYSIS = "SPECIFIC_ANALYSIS"; | |||
private static final String LEAK_PERIOD_PROP_KEY = "sonar.leak.period"; | |||
private final UuidFactory uuidFactory; | |||
private final System2 system2; | |||
@@ -40,41 +56,132 @@ public class PopulateNewCodePeriodTable extends DataChange { | |||
@Override | |||
protected void execute(Context context) throws SQLException { | |||
List<ProjectBranchCodePeriod> projectBranchCodePeriods = context.prepareSelect( | |||
"select pb.uuid, pb.project_uuid, pb.manual_baseline_analysis_uuid from project_branches pb where pb.manual_baseline_analysis_uuid is not null") | |||
List<String> branchUuidsAlreadyMigrated = populateFromManualBaselines(context); | |||
populateFromSettings(context, branchUuidsAlreadyMigrated); | |||
} | |||
private List<String> populateFromManualBaselines(Context context) throws SQLException { | |||
List<ProjectBranchManualBaselineDto> projectBranchManualBaselines = context.prepareSelect( | |||
"SELECT pb.uuid, pb.project_uuid, pb.manual_baseline_analysis_uuid FROM project_branches pb WHERE pb.manual_baseline_analysis_uuid IS NOT NULL") | |||
.list(row -> { | |||
String branchUuid = row.getString(1); | |||
String projectUuid = row.getString(2); | |||
String manualBaselineAnalysisUuid = row.getString(3); | |||
return new ProjectBranchCodePeriod(branchUuid, projectUuid, manualBaselineAnalysisUuid); | |||
return new ProjectBranchManualBaselineDto(branchUuid, projectUuid, manualBaselineAnalysisUuid); | |||
}); | |||
if (!projectBranchManualBaselines.isEmpty()) { | |||
populateWithManualBaselines(context, projectBranchManualBaselines); | |||
} | |||
return projectBranchManualBaselines.stream() | |||
.map(pb -> pb.branchUuid) | |||
.collect(Collectors.toList()); | |||
} | |||
private void populateFromSettings(Context context, List<String> branchUuidsAlreadyMigrated) throws SQLException { | |||
// migrate global setting | |||
String globalSetting = context | |||
.prepareSelect("SELECT props.text_value FROM properties props WHERE props.prop_key = '" + LEAK_PERIOD_PROP_KEY + "' AND props.resource_id IS NULL") | |||
.get(r -> r.getString(1)); | |||
if (globalSetting != null) { | |||
populateGlobalSetting(context, globalSetting); | |||
} | |||
List<BranchLeakPeriodPropertyDto> projectLeakPeriodProperties = new ArrayList<>(); | |||
context.prepareSelect( | |||
"SELECT projs.uuid, projs.main_branch_project_uuid, props.text_value " | |||
+ "FROM properties props INNER JOIN projects projs ON props.resource_id = projs.id " | |||
+ "WHERE props.prop_key = '" + LEAK_PERIOD_PROP_KEY + "' AND props.resource_id IS NOT NULL ") | |||
.scroll(row -> { | |||
String branchUuid = row.getString(1); | |||
if (branchUuidsAlreadyMigrated.contains(branchUuid)) { | |||
return; | |||
} | |||
String mainBranchUuid = row.getString(2); | |||
String value = row.getString(3); | |||
if (mainBranchUuid == null) { | |||
mainBranchUuid = branchUuid; | |||
} | |||
projectLeakPeriodProperties.add(new BranchLeakPeriodPropertyDto(branchUuid, mainBranchUuid, value)); | |||
}); | |||
if (!projectBranchCodePeriods.isEmpty()) { | |||
populateProjectBranchCodePeriods(context, projectBranchCodePeriods); | |||
populateWithSettings(context, projectLeakPeriodProperties); | |||
} | |||
private void populateGlobalSetting(Context context, String value) { | |||
Optional<TypeValuePair> typeValue = tryParse(context, null, value); | |||
typeValue.ifPresent(tp -> { | |||
try (Upsert upsertQuery = prepareUpsertNewCodePeriodQuery(context)) { | |||
long currentTime = system2.now(); | |||
insert(upsertQuery, null, null, tp.type, tp.value, currentTime); | |||
upsertQuery | |||
.execute() | |||
.commit(); | |||
} catch (SQLException e) { | |||
LOG.warn("Failed to migrate the global property for the new code period", e); | |||
} | |||
}); | |||
} | |||
private void populateWithSettings(Context context, List<BranchLeakPeriodPropertyDto> projectLeakPeriodProperties) throws SQLException { | |||
try (Upsert upsertQuery = prepareUpsertNewCodePeriodQuery(context)) { | |||
long currentTime = system2.now(); | |||
boolean commit = false; | |||
for (BranchLeakPeriodPropertyDto branchLeakPeriodProperty : projectLeakPeriodProperties) { | |||
Optional<TypeValuePair> typeValueOpt = tryParse(context, branchLeakPeriodProperty.branchUuid, branchLeakPeriodProperty.value); | |||
if (!typeValueOpt.isPresent()) { | |||
continue; | |||
} | |||
TypeValuePair typeValue = typeValueOpt.get(); | |||
String branchUuid = null; | |||
if (!branchLeakPeriodProperty.isMainBranch() || TYPE_SPECIFIC_ANALYSIS.equals(typeValue.type)) { | |||
branchUuid = branchLeakPeriodProperty.branchUuid; | |||
} | |||
insert(upsertQuery, branchLeakPeriodProperty.mainBranchUuid, branchUuid, typeValue.type, typeValue.value, currentTime); | |||
commit = true; | |||
} | |||
if (commit) { | |||
upsertQuery | |||
.execute() | |||
.commit(); | |||
} | |||
} | |||
} | |||
private void populateProjectBranchCodePeriods(Context context, List<ProjectBranchCodePeriod> projectBranchCodePeriods) throws SQLException { | |||
Upsert insertQuery = prepareInsertProjectQualityGateQuery(context); | |||
for (ProjectBranchCodePeriod projectBranchCodePeriod : projectBranchCodePeriods) { | |||
long currenTime = system2.now(); | |||
insertQuery | |||
.setString(1, uuidFactory.create()) | |||
.setString(2, projectBranchCodePeriod.projectUuid) | |||
.setString(3, projectBranchCodePeriod.branchUuid) | |||
.setString(4, "SPECIFIC_ANALYSIS") | |||
.setString(5, projectBranchCodePeriod.manualBaselineAnalysisUuid) | |||
.setLong(6, currenTime) | |||
.setLong(7, currenTime) | |||
.addBatch(); | |||
private void populateWithManualBaselines(Context context, List<ProjectBranchManualBaselineDto> projectBranchManualBaselines) throws SQLException { | |||
try (Upsert upsertQuery = prepareUpsertNewCodePeriodQuery(context)) { | |||
long currentTime = system2.now(); | |||
for (ProjectBranchManualBaselineDto projectBranchManualBaseline : projectBranchManualBaselines) { | |||
insert(upsertQuery, projectBranchManualBaseline.projectUuid, projectBranchManualBaseline.branchUuid, TYPE_SPECIFIC_ANALYSIS, | |||
projectBranchManualBaseline.manualBaselineAnalysisUuid, currentTime); | |||
} | |||
upsertQuery | |||
.execute() | |||
.commit(); | |||
} | |||
insertQuery | |||
.execute() | |||
.commit(); | |||
} | |||
private static Upsert prepareInsertProjectQualityGateQuery(Context context) throws SQLException { | |||
return context.prepareUpsert("insert into new_code_periods(" + | |||
private void insert(Upsert upsert, @Nullable String projectUuid, @Nullable String branchUuid, String type, @Nullable String value, long currentTime) throws SQLException { | |||
upsert | |||
.setString(1, uuidFactory.create()) | |||
.setString(2, projectUuid) | |||
.setString(3, branchUuid) | |||
.setString(4, type) | |||
.setString(5, value) | |||
.setLong(6, currentTime) | |||
.setLong(7, currentTime) | |||
.addBatch(); | |||
} | |||
private static Upsert prepareUpsertNewCodePeriodQuery(Context context) throws SQLException { | |||
return context.prepareUpsert("INSERT INTO new_code_periods(" + | |||
"uuid, " + | |||
"project_uuid," + | |||
"branch_uuid," + | |||
@@ -85,16 +192,102 @@ public class PopulateNewCodePeriodTable extends DataChange { | |||
") VALUES (?, ?, ?, ?, ?, ?, ?)"); | |||
} | |||
private static class ProjectBranchCodePeriod { | |||
private static Optional<TypeValuePair> tryParse(Context context, @Nullable String branchUuid, String value) { | |||
try { | |||
if (value.equals("previous_version")) { | |||
return Optional.of(new TypeValuePair(TYPE_PREVIOUS_VERSION, null)); | |||
} | |||
try { | |||
Integer.parseInt(value); | |||
return Optional.of(new TypeValuePair(TYPE_NUMBER_OF_DAYS, value)); | |||
} catch (NumberFormatException e) { | |||
// ignore | |||
} | |||
if (branchUuid == null) { | |||
return Optional.empty(); | |||
} | |||
try { | |||
LocalDate localDate = LocalDate.parse(value); | |||
Optional<String> snapshot = findFirstSnapshot(context, branchUuid, localDate.atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli()); | |||
return snapshot.map(uuid -> new TypeValuePair(TYPE_SPECIFIC_ANALYSIS, uuid)); | |||
} catch (DateTimeParseException e) { | |||
// ignore | |||
} | |||
List<EventDto> versions = getVersionsByMostRecentFirst(context, branchUuid); | |||
Optional<EventDto> versionMatch = versions.stream().filter(v -> v.version.equals(value)).findFirst(); | |||
return versionMatch.map(e -> new TypeValuePair(TYPE_SPECIFIC_ANALYSIS, e.analysisUuid)); | |||
} catch (SQLException e) { | |||
LOG.warn("Failed to migrate a property for the new code period", e); | |||
return Optional.empty(); | |||
} | |||
} | |||
private static List<EventDto> getVersionsByMostRecentFirst(Context context, String branchUuid) throws SQLException { | |||
// TODO in LoadPeriodsStep we do a join with snapshots and see if the analysis is still unprocessed. Do we need to do it here? | |||
return context.prepareSelect("SELECT name, analysis_uuid FROM events " | |||
+ "WHERE component_uuid = '" + branchUuid + "' AND category = 'Version' " | |||
+ "ORDER BY created_at DESC") | |||
.list(r -> new EventDto(r.getString(1), r.getString(2))); | |||
} | |||
private static Optional<String> findFirstSnapshot(Context context, String branchUuid, long date) throws SQLException { | |||
String analysisUuid = context.prepareSelect("SELECT uuid FROM snapshots " | |||
+ "WHERE component_uuid = '" + branchUuid + "' AND created_at > " + date + " AND status = 'P' " | |||
+ "ORDER BY created_at ASC") | |||
.get(r -> r.getString(1)); | |||
return Optional.ofNullable(analysisUuid); | |||
} | |||
private static class TypeValuePair { | |||
private final String type; | |||
@Nullable | |||
private final String value; | |||
private TypeValuePair(String type, @Nullable String value) { | |||
this.type = type; | |||
this.value = value; | |||
} | |||
} | |||
private static class EventDto { | |||
private final String version; | |||
private final String analysisUuid; | |||
private EventDto(String version, String analysisUuid) { | |||
this.version = version; | |||
this.analysisUuid = analysisUuid; | |||
} | |||
} | |||
private static class ProjectBranchManualBaselineDto { | |||
private final String branchUuid; | |||
private final String projectUuid; | |||
private final String manualBaselineAnalysisUuid; | |||
ProjectBranchCodePeriod(String branchUuid, String projectUuid, String manualBaselineAnalysisUuid) { | |||
ProjectBranchManualBaselineDto(String branchUuid, String projectUuid, String manualBaselineAnalysisUuid) { | |||
this.branchUuid = branchUuid; | |||
this.projectUuid = projectUuid; | |||
this.manualBaselineAnalysisUuid = manualBaselineAnalysisUuid; | |||
} | |||
} | |||
private static class BranchLeakPeriodPropertyDto { | |||
private final String branchUuid; | |||
private final String mainBranchUuid; | |||
private final String value; | |||
BranchLeakPeriodPropertyDto(String branchUuid, String mainBranchUuid, String value) { | |||
this.branchUuid = branchUuid; | |||
this.mainBranchUuid = mainBranchUuid; | |||
this.value = value; | |||
} | |||
private boolean isMainBranch() { | |||
return mainBranchUuid.equals(branchUuid); | |||
} | |||
} | |||
} |
@@ -20,14 +20,23 @@ | |||
package org.sonar.server.platform.db.migration.version.v80; | |||
import java.sql.SQLException; | |||
import org.junit.Assert; | |||
import java.time.Instant; | |||
import java.time.LocalDate; | |||
import java.time.ZoneId; | |||
import java.util.Map; | |||
import java.util.Optional; | |||
import javax.annotation.Nullable; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.resources.Scopes; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.core.util.UuidFactoryImpl; | |||
import org.sonar.db.CoreDbTester; | |||
import static java.lang.String.valueOf; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class PopulateNewCodePeriodTableTest { | |||
private static final String NEW_CODE_PERIODS_TABLE_NAME = "new_code_periods"; | |||
private static final String PROJECT_BRANCHES_TABLE_NAME = "project_branches"; | |||
@@ -35,7 +44,6 @@ public class PopulateNewCodePeriodTableTest { | |||
@Rule | |||
public CoreDbTester dbTester = CoreDbTester.createForSchema(PopulateNewCodePeriodTableTest.class, "schema.sql"); | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
@@ -43,31 +51,211 @@ public class PopulateNewCodePeriodTableTest { | |||
@Test | |||
public void copy_manual_baseline_analysis_to_new_code_period_table() throws SQLException { | |||
for (long i = 1; i <= NUMBER_OF_PROJECT_BRANCHES_TO_INSERT; i++) { | |||
insertProjectBranch(i); | |||
for (long i = 0; i < NUMBER_OF_PROJECT_BRANCHES_TO_INSERT; i++) { | |||
insertMainBranch(i, true); | |||
} | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable(NEW_CODE_PERIODS_TABLE_NAME)).isEqualTo(10); | |||
int propertiesCount = dbTester.countRowsOfTable(NEW_CODE_PERIODS_TABLE_NAME); | |||
Assert.assertEquals(10, propertiesCount); | |||
for (int i = 0; i < NUMBER_OF_PROJECT_BRANCHES_TO_INSERT; i++) { | |||
assertNewCodePeriod(i, "pb-uuid-" + i, "pb-uuid-" + i, "SPECIFIC_ANALYSIS", "mba-uuid" + i); | |||
} | |||
//should not fail if executed twice | |||
underTest.execute(); | |||
} | |||
private void insertProjectBranch(long counter) { | |||
@Test | |||
public void do_nothing_if_cant_find_matching_analysis() throws SQLException { | |||
insertMainBranch(0, false); | |||
insertMainBranch(1, false); | |||
insertProperty(0, "2.0"); | |||
insertProperty(0, "2019-04-05"); | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable(NEW_CODE_PERIODS_TABLE_NAME)).isEqualTo(0); | |||
} | |||
@Test | |||
public void do_nothing_if_cant_migrate_global_property() throws SQLException { | |||
insertProperty(null, "2.0"); | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable(NEW_CODE_PERIODS_TABLE_NAME)).isEqualTo(0); | |||
} | |||
@Test | |||
public void migrate_project_property_set_to_previous_version() throws SQLException { | |||
insertMainBranch(0, true); | |||
insertMainBranch(1, false); | |||
// no property defined for it | |||
insertMainBranch(2, false); | |||
// doesn't get copied since there is a manual baseline taking precedence | |||
insertProperty(0, "20"); | |||
insertProperty(1, "previous_version"); | |||
// doesn't exist | |||
insertProperty(3, "previous_version"); | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable(NEW_CODE_PERIODS_TABLE_NAME)).isEqualTo(2); | |||
assertNewCodePeriod(0, "pb-uuid-" + 0, "pb-uuid-" + 0, "SPECIFIC_ANALYSIS", "mba-uuid" + 0); | |||
assertNewCodePeriod(1, "pb-uuid-" + 1, null, "PREVIOUS_VERSION", null); | |||
} | |||
@Test | |||
public void migrate_project_property_set_to_number_of_days() throws SQLException { | |||
insertMainBranch(0, false); | |||
insertBranch(0, 1, false); | |||
insertProperty(1, "20"); | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable(NEW_CODE_PERIODS_TABLE_NAME)).isEqualTo(1); | |||
assertNewCodePeriod(0, "pb-uuid-" + 0, "pb-uuid-" + 1, "NUMBER_OF_DAYS", "20"); | |||
} | |||
@Test | |||
public void migrate_branch_property_set_to_number_of_days() throws SQLException { | |||
insertMainBranch(0, false); | |||
insertProperty(0, "20"); | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable(NEW_CODE_PERIODS_TABLE_NAME)).isEqualTo(1); | |||
assertNewCodePeriod(0, "pb-uuid-" + 0, null, "NUMBER_OF_DAYS", "20"); | |||
} | |||
@Test | |||
public void migrate_global_property_set_to_number_of_days() throws SQLException { | |||
insertProperty(null, "20"); | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable(NEW_CODE_PERIODS_TABLE_NAME)).isEqualTo(1); | |||
assertNewCodePeriod(0, null, null, "NUMBER_OF_DAYS", "20"); | |||
} | |||
@Test | |||
public void migrate_project_property_set_to_version() throws SQLException { | |||
insertMainBranch(0, false); | |||
insertProperty(0, "2.0"); | |||
insertVersionEvent(0, 10L, "1.0"); | |||
insertVersionEvent(0, 20L, "2.0"); | |||
insertVersionEvent(0, 30L, "3.0"); | |||
insertVersionEvent(0, 40L, "2.0"); | |||
insertVersionEvent(0, 50L, "3.0"); | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable(NEW_CODE_PERIODS_TABLE_NAME)).isEqualTo(1); | |||
// we don't support specific analysis for projects, so we set it for the branch | |||
assertNewCodePeriod(0, "pb-uuid-" + 0, "pb-uuid-" + 0, "SPECIFIC_ANALYSIS", "analysis-40"); | |||
} | |||
@Test | |||
public void migrate_project_property_set_to_date() throws SQLException { | |||
insertMainBranch(0, false); | |||
insertProperty(0, "2019-04-05"); | |||
long reference = LocalDate.parse("2019-04-05").atStartOfDay(ZoneId.systemDefault()).toInstant().toEpochMilli(); | |||
insertSnapshot(100, 0, reference - 100); | |||
insertSnapshot(200, 0, reference + 100); | |||
insertSnapshot(300, 0, reference + 200); | |||
underTest.execute(); | |||
assertThat(dbTester.countRowsOfTable(NEW_CODE_PERIODS_TABLE_NAME)).isEqualTo(1); | |||
// we don't support specific analysis for projects, so we set it for the branch | |||
assertNewCodePeriod(0, "pb-uuid-" + 0, "pb-uuid-" + 0, "SPECIFIC_ANALYSIS", "snapshot-200"); | |||
} | |||
private void assertNewCodePeriod(int row, @Nullable String projectUuid, @Nullable String branchUuid, String type, @Nullable String value) { | |||
Optional<Map<String, Object>> r = dbTester.select("SELECT PROJECT_UUID, BRANCH_UUID, TYPE, VALUE FROM " + NEW_CODE_PERIODS_TABLE_NAME) | |||
.stream() | |||
.skip(row) | |||
.findFirst(); | |||
assertThat(r).isPresent(); | |||
assertThat(r.get().get("PROJECT_UUID")).isEqualTo(projectUuid); | |||
assertThat(r.get().get("BRANCH_UUID")).isEqualTo(branchUuid); | |||
assertThat(r.get().get("TYPE")).isEqualTo(type); | |||
assertThat(r.get().get("VALUE")).isEqualTo(value); | |||
} | |||
private void insertBranch(long mainBranchUid, long uid, boolean withBaseLine) { | |||
String manualBaseline = withBaseLine ? "mba-uuid" + uid : null; | |||
String mainBranchProjectUuid = mainBranchUid == uid ? null : "pb-uuid-" + mainBranchUid; | |||
insertComponent(uid, "pb-uuid-" + uid, mainBranchProjectUuid); | |||
dbTester.executeInsert( | |||
PROJECT_BRANCHES_TABLE_NAME, | |||
"UUID", "pb-uuid-" + counter, | |||
"PROJECT_UUID", "pb-uuid-" + counter, | |||
"KEE", "pb-key-" + counter, | |||
"UUID", "pb-uuid-" + uid, | |||
"PROJECT_UUID", "pb-uuid-" + mainBranchUid, | |||
"KEE", "pb-key-" + uid, | |||
"KEY_TYPE", "TSR", | |||
"BRANCH_TYPE", "LONG", | |||
"MERGE_BRANCH_UUID", "mb-uuid-" + counter, | |||
"MANUAL_BASELINE_ANALYSIS_UUID", "mba-uuid" + counter, | |||
"MERGE_BRANCH_UUID", "mb-uuid-" + mainBranchUid, | |||
"MANUAL_BASELINE_ANALYSIS_UUID", manualBaseline, | |||
"CREATED_AT", System2.INSTANCE.now(), | |||
"UPDATED_AT", System2.INSTANCE.now() | |||
); | |||
} | |||
private void insertSnapshot(int uid, int branchUid, long creationDate) { | |||
dbTester.executeInsert( | |||
"SNAPSHOTS", | |||
"UUID", "snapshot-" + uid, | |||
"COMPONENT_UUID", "pb-uuid-" + branchUid, | |||
"STATUS", "P", | |||
"CREATED_AT", creationDate | |||
); | |||
} | |||
private void insertProperty(@Nullable Integer uid, String value) { | |||
dbTester.executeInsert( | |||
"PROPERTIES", | |||
"PROP_KEY", "sonar.leak.period", | |||
"RESOURCE_ID", uid, | |||
"USER_ID", null, | |||
"IS_EMPTY", false, | |||
"TEXT_VALUE", value, | |||
"CLOB_VALUE", null, | |||
"CREATED_AT", System2.INSTANCE.now() | |||
); | |||
} | |||
private void insertComponent(long id, String uuid, @Nullable String mainBranchProjectUuid) { | |||
dbTester.executeInsert( | |||
"projects", | |||
"ID", valueOf(id), | |||
"UUID", uuid, | |||
"ROOT_UUID", uuid, | |||
"PROJECT_UUID", uuid, | |||
"MAIN_BRANCH_PROJECT_UUID", mainBranchProjectUuid, | |||
"SCOPE", Scopes.PROJECT, | |||
"QUALIFIER", "TRK", | |||
"NAME", "name-" + id); | |||
} | |||
private void insertVersionEvent(long id, long createdAt, String version) { | |||
dbTester.executeInsert( | |||
"events", | |||
"ANALYSIS_UUID", "analysis-" + createdAt, | |||
"NAME", version, | |||
"COMPONENT_UUID", "pb-uuid-" + id, | |||
"CATEGORY", "Version", | |||
"CREATED_AT", createdAt); | |||
} | |||
private void insertMainBranch(long uid, boolean withBaseLine) { | |||
insertBranch(uid, uid, withBaseLine); | |||
} | |||
} |
@@ -19,9 +19,68 @@ CREATE TABLE "NEW_CODE_PERIODS" ( | |||
"PROJECT_UUID" VARCHAR(40), | |||
"BRANCH_UUID" VARCHAR(40), | |||
"TYPE" VARCHAR(30) NOT NULL, | |||
"VALUE" VARCHAR(40) NOT NULL, | |||
"VALUE" VARCHAR(40), | |||
"UPDATED_AT" BIGINT NOT NULL, | |||
"CREATED_AT" BIGINT NOT NULL, | |||
CONSTRAINT "PK_NEW_CODE_PERIOD" PRIMARY KEY ("UUID") | |||
); | |||
CREATE TABLE "PROPERTIES" ( | |||
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), | |||
"PROP_KEY" VARCHAR(512) NOT NULL, | |||
"RESOURCE_ID" INTEGER, | |||
"USER_ID" INTEGER, | |||
"IS_EMPTY" BOOLEAN NOT NULL, | |||
"TEXT_VALUE" VARCHAR(4000), | |||
"CLOB_VALUE" CLOB, | |||
"CREATED_AT" BIGINT | |||
); | |||
CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES" ("PROP_KEY"); | |||
CREATE TABLE "PROJECTS" ( | |||
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), | |||
"KEE" VARCHAR(400), | |||
"UUID" VARCHAR(50) NOT NULL, | |||
"ROOT_UUID" VARCHAR(50), | |||
"PROJECT_UUID" VARCHAR(50) NOT NULL, | |||
"MODULE_UUID" VARCHAR(50), | |||
"MODULE_UUID_PATH" VARCHAR(1500), | |||
"MAIN_BRANCH_PROJECT_UUID" VARCHAR(50), | |||
"NAME" VARCHAR(2000), | |||
"TAGS" VARCHAR(500), | |||
"ENABLED" BOOLEAN NOT NULL DEFAULT TRUE, | |||
"SCOPE" VARCHAR(3), | |||
"QUALIFIER" VARCHAR(10) | |||
); | |||
CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE"); | |||
CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID"); | |||
CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID"); | |||
CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID"); | |||
CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID"); | |||
CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER"); | |||
CREATE TABLE "EVENTS" ( | |||
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), | |||
"NAME" VARCHAR(400), | |||
"ANALYSIS_UUID" VARCHAR(50) NOT NULL, | |||
"COMPONENT_UUID" VARCHAR(50) NOT NULL, | |||
"CATEGORY" VARCHAR(50), | |||
"CREATED_AT" BIGINT NOT NULL, | |||
"DESCRIPTION" VARCHAR(4000), | |||
"EVENT_DATA" VARCHAR(4000) | |||
); | |||
CREATE INDEX "EVENTS_ANALYSIS" ON "EVENTS" ("ANALYSIS_UUID"); | |||
CREATE INDEX "EVENTS_COMPONENT_UUID" ON "EVENTS" ("COMPONENT_UUID"); | |||
CREATE TABLE "SNAPSHOTS" ( | |||
"ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1), | |||
"UUID" VARCHAR(50) NOT NULL, | |||
"CREATED_AT" BIGINT, | |||
"BUILD_DATE" BIGINT, | |||
"COMPONENT_UUID" VARCHAR(50) NOT NULL, | |||
"STATUS" VARCHAR(4) NOT NULL DEFAULT 'U', | |||
"ISLAST" BOOLEAN NOT NULL DEFAULT FALSE | |||
); | |||
CREATE INDEX "SNAPSHOT_COMPONENT" ON "SNAPSHOTS" ("COMPONENT_UUID"); | |||
CREATE UNIQUE INDEX "ANALYSES_UUID" ON "SNAPSHOTS" ("UUID"); |
@@ -0,0 +1,152 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 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.Collection; | |||
import java.util.Map; | |||
import java.util.stream.Collectors; | |||
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; | |||
import org.sonar.api.web.UserRole; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbSession; | |||
import org.sonar.db.component.BranchDto; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.newcodeperiod.NewCodePeriodDao; | |||
import org.sonar.db.newcodeperiod.NewCodePeriodDto; | |||
import org.sonar.db.newcodeperiod.NewCodePeriodType; | |||
import org.sonar.server.component.ComponentFinder; | |||
import org.sonar.server.user.UserSession; | |||
import org.sonarqube.ws.NewCodePeriods; | |||
import org.sonarqube.ws.NewCodePeriods.ListWSResponse; | |||
import static org.sonar.core.util.stream.MoreCollectors.toList; | |||
import static org.sonar.db.component.BranchType.LONG; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
import static org.sonarqube.ws.NewCodePeriods.ShowWSResponse.newBuilder; | |||
public class ListAction implements NewCodePeriodsWsAction { | |||
private static final String PARAM_PROJECT = "project"; | |||
private final DbClient dbClient; | |||
private final UserSession userSession; | |||
private final ComponentFinder componentFinder; | |||
private final NewCodePeriodDao newCodePeriodDao; | |||
public ListAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder, NewCodePeriodDao newCodePeriodDao) { | |||
this.dbClient = dbClient; | |||
this.userSession = userSession; | |||
this.componentFinder = componentFinder; | |||
this.newCodePeriodDao = newCodePeriodDao; | |||
} | |||
@Override | |||
public void define(WebService.NewController context) { | |||
WebService.NewAction action = context.createAction("list") | |||
.setDescription("List the New Code Periods for all long lived branches in a project.<br>" + | |||
"Requires the permission to browse the project") | |||
.setSince("8.0") | |||
.setResponseExample(getClass().getResource("list-example.json")) | |||
.setHandler(this); | |||
action.createParam(PARAM_PROJECT) | |||
.setRequired(true) | |||
.setDescription("Project key"); | |||
} | |||
@Override | |||
public void handle(Request request, Response response) throws Exception { | |||
String projectKey = request.mandatoryParam(PARAM_PROJECT); | |||
try (DbSession dbSession = dbClient.openSession(false)) { | |||
ComponentDto project = componentFinder.getByKey(dbSession, projectKey); | |||
userSession.checkComponentPermission(UserRole.ADMIN, project); | |||
Collection<BranchDto> branches = dbClient.branchDao().selectByComponent(dbSession, project).stream() | |||
.filter(b -> b.getBranchType() == LONG) | |||
.collect(toList()); | |||
Map<String, InheritedNewCodePeriod> newCodePeriodByBranchUuid = newCodePeriodDao | |||
.selectAllByProject(dbSession, project.uuid()) | |||
.stream() | |||
.collect(Collectors.toMap(NewCodePeriodDto::getBranchUuid, dto -> new InheritedNewCodePeriod(dto, dto.getBranchUuid() == null))); | |||
InheritedNewCodePeriod projectDefault = newCodePeriodByBranchUuid.getOrDefault(null, | |||
newCodePeriodDao.selectGlobal(dbSession) | |||
.map(dto -> new InheritedNewCodePeriod(dto, true)) | |||
.orElse(new InheritedNewCodePeriod(NewCodePeriodDto.defaultInstance(), true)) | |||
); | |||
ListWSResponse.Builder builder = ListWSResponse.newBuilder(); | |||
for (BranchDto branch : branches) { | |||
InheritedNewCodePeriod inherited = newCodePeriodByBranchUuid.getOrDefault(branch.getUuid(), projectDefault); | |||
builder.addNewCodePeriods( | |||
build(projectKey, branch.getKey(), inherited.getType(), inherited.getValue(), inherited.inherited)); | |||
} | |||
writeProtobuf(builder.build(), request, response); | |||
} | |||
} | |||
private NewCodePeriods.ShowWSResponse build(String projectKey, String branchKey, NewCodePeriodType newCodePeriodType, @Nullable String value, boolean inherited) { | |||
NewCodePeriods.ShowWSResponse.Builder builder = newBuilder() | |||
.setType(convertType(newCodePeriodType)) | |||
.setInherited(inherited) | |||
.setBranchKey(branchKey) | |||
.setProjectKey(projectKey); | |||
if (value != null) { | |||
builder.setValue(value); | |||
} | |||
return builder.build(); | |||
} | |||
private static NewCodePeriods.NewCodePeriodType convertType(NewCodePeriodType type) { | |||
switch (type) { | |||
case NUMBER_OF_DAYS: | |||
return NewCodePeriods.NewCodePeriodType.NUMBER_OF_DAYS; | |||
case PREVIOUS_VERSION: | |||
return NewCodePeriods.NewCodePeriodType.PREVIOUS_VERSION; | |||
case SPECIFIC_ANALYSIS: | |||
return NewCodePeriods.NewCodePeriodType.SPECIFIC_ANALYSIS; | |||
default: | |||
throw new IllegalStateException("Unexpected type: " + type); | |||
} | |||
} | |||
private static class InheritedNewCodePeriod { | |||
NewCodePeriodDto newCodePeriod; | |||
boolean inherited; | |||
InheritedNewCodePeriod(NewCodePeriodDto newCodePeriod, boolean inherited) { | |||
this.newCodePeriod = newCodePeriod; | |||
this.inherited = inherited; | |||
} | |||
NewCodePeriodType getType() { | |||
return newCodePeriod.getType(); | |||
} | |||
String getValue() { | |||
return newCodePeriod.getValue(); | |||
} | |||
} | |||
} |
@@ -20,7 +20,7 @@ | |||
package org.sonar.server.newcodeperiod.ws; | |||
import com.google.common.base.Preconditions; | |||
import com.google.common.collect.ImmutableSet; | |||
import java.util.EnumSet; | |||
import java.util.Locale; | |||
import java.util.Set; | |||
import javax.annotation.Nullable; | |||
@@ -44,7 +44,6 @@ 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.DATE; | |||
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.SPECIFIC_ANALYSIS; | |||
@@ -55,9 +54,9 @@ public class SetAction implements NewCodePeriodsWsAction { | |||
private static final String PARAM_PROJECT = "project"; | |||
private static final String PARAM_TYPE = "type"; | |||
private static final String PARAM_VALUE = "value"; | |||
private static final Set<NewCodePeriodType> OVERALL_TYPES = ImmutableSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS); | |||
private static final Set<NewCodePeriodType> PROJECT_TYPES = ImmutableSet.of(DATE, PREVIOUS_VERSION, NUMBER_OF_DAYS); | |||
private static final Set<NewCodePeriodType> BRANCH_TYPES = ImmutableSet.of(DATE, PREVIOUS_VERSION, NUMBER_OF_DAYS, SPECIFIC_ANALYSIS); | |||
private static final Set<NewCodePeriodType> OVERALL_TYPES = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS); | |||
private static final Set<NewCodePeriodType> PROJECT_TYPES = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS); | |||
private static final Set<NewCodePeriodType> BRANCH_TYPES = EnumSet.of(PREVIOUS_VERSION, NUMBER_OF_DAYS, SPECIFIC_ANALYSIS); | |||
private final DbClient dbClient; | |||
private final UserSession userSession; | |||
@@ -146,14 +145,6 @@ public class SetAction implements NewCodePeriodsWsAction { | |||
throw new IllegalArgumentException("Failed to parse number of days: " + value); | |||
} | |||
break; | |||
case DATE: | |||
requireValue(type, value); | |||
try { | |||
dto.setValue(NewCodePeriodParser.parseDate(value).toString()); | |||
} catch (Exception e) { | |||
throw new IllegalArgumentException("Failed to parse date: " + value); | |||
} | |||
break; | |||
case SPECIFIC_ANALYSIS: | |||
requireValue(type, value); | |||
SnapshotDto analysis = getAnalysis(dbSession, value, projectBranch, branch); |
@@ -37,12 +37,12 @@ import org.sonar.server.component.ComponentFinder; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import org.sonar.server.user.UserSession; | |||
import org.sonarqube.ws.NewCodePeriods; | |||
import org.sonarqube.ws.NewCodePeriods.ShowWSResponse; | |||
import static com.google.common.base.Preconditions.checkArgument; | |||
import static java.lang.String.format; | |||
import static org.sonar.server.component.ComponentFinder.ParamNames.PROJECT_ID_AND_KEY; | |||
import static org.sonar.server.ws.WsUtils.writeProtobuf; | |||
import static org.sonarqube.ws.NewCodePeriods.*; | |||
public class ShowAction implements NewCodePeriodsWsAction { | |||
private static final String PARAM_BRANCH = "branch"; | |||
@@ -154,8 +154,6 @@ public class ShowAction implements NewCodePeriodsWsAction { | |||
switch (type) { | |||
case NUMBER_OF_DAYS: | |||
return NewCodePeriods.NewCodePeriodType.NUMBER_OF_DAYS; | |||
case DATE: | |||
return NewCodePeriods.NewCodePeriodType.DATE; | |||
case PREVIOUS_VERSION: | |||
return NewCodePeriods.NewCodePeriodType.PREVIOUS_VERSION; | |||
case SPECIFIC_ANALYSIS: |
@@ -0,0 +1,23 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 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. | |||
*/ | |||
@ParametersAreNonnullByDefault | |||
package org.sonar.server.newcodeperiod.ws; | |||
import javax.annotation.ParametersAreNonnullByDefault; |
@@ -0,0 +1,319 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 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.Optional; | |||
import org.junit.Rule; | |||
import org.junit.Test; | |||
import org.junit.rules.ExpectedException; | |||
import org.sonar.api.server.ws.WebService; | |||
import org.sonar.api.utils.System2; | |||
import org.sonar.api.web.UserRole; | |||
import org.sonar.core.util.UuidFactoryFast; | |||
import org.sonar.db.DbClient; | |||
import org.sonar.db.DbTester; | |||
import org.sonar.db.component.BranchType; | |||
import org.sonar.db.component.ComponentDbTester; | |||
import org.sonar.db.component.ComponentDto; | |||
import org.sonar.db.newcodeperiod.NewCodePeriodDao; | |||
import org.sonar.db.newcodeperiod.NewCodePeriodDbTester; | |||
import org.sonar.db.newcodeperiod.NewCodePeriodDto; | |||
import org.sonar.db.newcodeperiod.NewCodePeriodType; | |||
import org.sonar.server.component.ComponentFinder; | |||
import org.sonar.server.component.TestComponentFinder; | |||
import org.sonar.server.exceptions.ForbiddenException; | |||
import org.sonar.server.exceptions.NotFoundException; | |||
import org.sonar.server.tester.UserSessionRule; | |||
import org.sonar.server.ws.WsActionTester; | |||
import org.sonarqube.ws.NewCodePeriods; | |||
import org.sonarqube.ws.NewCodePeriods.ListWSResponse; | |||
import org.sonarqube.ws.NewCodePeriods.ShowWSResponse; | |||
import static org.assertj.core.api.Assertions.assertThat; | |||
public class ListActionTest { | |||
@Rule | |||
public ExpectedException expectedException = ExpectedException.none(); | |||
@Rule | |||
public UserSessionRule userSession = UserSessionRule.standalone(); | |||
@Rule | |||
public DbTester db = DbTester.create(System2.INSTANCE); | |||
private ComponentDbTester componentDb = new ComponentDbTester(db); | |||
private DbClient dbClient = db.getDbClient(); | |||
private ComponentFinder componentFinder = TestComponentFinder.from(db); | |||
private NewCodePeriodDao dao = new NewCodePeriodDao(System2.INSTANCE, UuidFactoryFast.getInstance()); | |||
private NewCodePeriodDbTester tester = new NewCodePeriodDbTester(db); | |||
private ListAction underTest = new ListAction(dbClient, userSession, componentFinder, dao); | |||
private WsActionTester ws = new WsActionTester(underTest); | |||
@Test | |||
public void test_definition() { | |||
WebService.Action definition = ws.getDef(); | |||
assertThat(definition.key()).isEqualTo("list"); | |||
assertThat(definition.isInternal()).isFalse(); | |||
assertThat(definition.since()).isEqualTo("8.0"); | |||
assertThat(definition.isPost()).isFalse(); | |||
assertThat(definition.params()).extracting(WebService.Param::key).containsOnly("project"); | |||
assertThat(definition.param("project").isRequired()).isTrue(); | |||
} | |||
@Test | |||
public void throw_NFE_if_project_not_found() { | |||
expectedException.expect(NotFoundException.class); | |||
expectedException.expectMessage("Component key 'unknown' not found"); | |||
ws.newRequest() | |||
.setParam("project", "unknown") | |||
.execute(); | |||
} | |||
@Test | |||
public void throw_FE_if_no_project_permission() { | |||
ComponentDto project = componentDb.insertMainBranch(); | |||
expectedException.expect(ForbiddenException.class); | |||
expectedException.expectMessage("Insufficient privileges"); | |||
ws.newRequest() | |||
.setParam("project", project.getKey()) | |||
.execute(); | |||
} | |||
@Test | |||
public void list_only_LLB() { | |||
ComponentDto project = componentDb.insertMainBranch(); | |||
createBranches(project, 5, BranchType.LONG); | |||
createBranches(project, 3, BranchType.SHORT); | |||
logInAsProjectAdministrator(project); | |||
ListWSResponse response = ws.newRequest() | |||
.setParam("project", project.getKey()) | |||
.executeProtobuf(ListWSResponse.class); | |||
assertThat(response).isNotNull(); | |||
assertThat(response.getNewCodePeriodsCount()).isEqualTo(6); | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getBranchKey) | |||
.contains("master", "LONG_0", "LONG_1", "LONG_2", "LONG_3", "LONG_4"); | |||
//check if global default is set | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getType) | |||
.contains(NewCodePeriods.NewCodePeriodType.PREVIOUS_VERSION); | |||
} | |||
@Test | |||
public void list_inherited_global_settings() { | |||
ComponentDto project = componentDb.insertMainBranch(); | |||
tester.insert(new NewCodePeriodDto().setType(NewCodePeriodType.SPECIFIC_ANALYSIS).setValue("uuid")); | |||
createBranches(project, 5, BranchType.LONG); | |||
logInAsProjectAdministrator(project); | |||
ListWSResponse response = ws.newRequest() | |||
.setParam("project", project.getKey()) | |||
.executeProtobuf(ListWSResponse.class); | |||
assertThat(response).isNotNull(); | |||
assertThat(response.getNewCodePeriodsCount()).isEqualTo(6); | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getBranchKey) | |||
.contains("master", "LONG_0", "LONG_1", "LONG_2", "LONG_3", "LONG_4"); | |||
//check if global default is set | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getType) | |||
.contains(NewCodePeriods.NewCodePeriodType.SPECIFIC_ANALYSIS); | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getValue) | |||
.contains("uuid"); | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getInherited) | |||
.contains(true); | |||
} | |||
@Test | |||
public void list_inherited_project_settings() { | |||
ComponentDto projectWithOwnSettings = componentDb.insertMainBranch(); | |||
ComponentDto projectWithGlobalSettings = componentDb.insertMainBranch(); | |||
tester.insert(new NewCodePeriodDto() | |||
.setType(NewCodePeriodType.SPECIFIC_ANALYSIS) | |||
.setValue("global_uuid")); | |||
tester.insert(new NewCodePeriodDto() | |||
.setProjectUuid(projectWithOwnSettings.uuid()) | |||
.setType(NewCodePeriodType.SPECIFIC_ANALYSIS) | |||
.setValue("project_uuid")); | |||
createBranches(projectWithOwnSettings, 5, BranchType.LONG); | |||
logInAsProjectAdministrator(projectWithOwnSettings, projectWithGlobalSettings); | |||
ListWSResponse response = ws.newRequest() | |||
.setParam("project", projectWithOwnSettings.getKey()) | |||
.executeProtobuf(ListWSResponse.class); | |||
//verify project with project level settings | |||
assertThat(response).isNotNull(); | |||
assertThat(response.getNewCodePeriodsCount()).isEqualTo(6); | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getBranchKey) | |||
.contains("master", "LONG_0", "LONG_1", "LONG_2", "LONG_3", "LONG_4"); | |||
//check if project setting is set | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getType) | |||
.contains(NewCodePeriods.NewCodePeriodType.SPECIFIC_ANALYSIS); | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getValue) | |||
.containsOnly("project_uuid"); | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getInherited) | |||
.containsOnly(true); | |||
//verify project with global level settings | |||
response = ws.newRequest() | |||
.setParam("project", projectWithGlobalSettings.getKey()) | |||
.executeProtobuf(ListWSResponse.class); | |||
assertThat(response).isNotNull(); | |||
assertThat(response.getNewCodePeriodsCount()).isEqualTo(1); | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getBranchKey) | |||
.containsOnly("master"); | |||
//check if global setting is set | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getType) | |||
.contains(NewCodePeriods.NewCodePeriodType.SPECIFIC_ANALYSIS); | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getValue) | |||
.contains("global_uuid"); | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getInherited) | |||
.containsOnly(true); | |||
} | |||
@Test | |||
public void list_branch_and_inherited_global_settings() { | |||
ComponentDto project = componentDb.insertMainBranch(); | |||
ComponentDto branchWithOwnSettings = componentDb.insertProjectBranch(project, branchDto -> branchDto.setKey("OWN_SETTINGS")); | |||
componentDb.insertProjectBranch(project, branchDto -> branchDto.setKey("GLOBAL_SETTINGS")); | |||
tester.insert(new NewCodePeriodDto() | |||
.setType(NewCodePeriodType.SPECIFIC_ANALYSIS) | |||
.setValue("global_uuid")); | |||
tester.insert(new NewCodePeriodDto() | |||
.setProjectUuid(project.uuid()) | |||
.setBranchUuid(branchWithOwnSettings.uuid()) | |||
.setType(NewCodePeriodType.SPECIFIC_ANALYSIS) | |||
.setValue("branch_uuid")); | |||
logInAsProjectAdministrator(project); | |||
ListWSResponse response = ws.newRequest() | |||
.setParam("project", project.getKey()) | |||
.executeProtobuf(ListWSResponse.class); | |||
assertThat(response).isNotNull(); | |||
assertThat(response.getNewCodePeriodsCount()).isEqualTo(3); | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getBranchKey) | |||
.contains("master", "OWN_SETTINGS", "GLOBAL_SETTINGS"); | |||
Optional<ShowWSResponse> ownSettings = response.getNewCodePeriodsList().stream() | |||
.filter(s -> !s.getInherited()) | |||
.findFirst(); | |||
assertThat(ownSettings).isNotNull(); | |||
assertThat(ownSettings).isNotEmpty(); | |||
assertThat(ownSettings.get().getProjectKey()).isEqualTo(project.getKey()); | |||
assertThat(ownSettings.get().getBranchKey()).isEqualTo("OWN_SETTINGS"); | |||
assertThat(ownSettings.get().getType()).isEqualTo(NewCodePeriods.NewCodePeriodType.SPECIFIC_ANALYSIS); | |||
assertThat(ownSettings.get().getValue()).isEqualTo("branch_uuid"); | |||
assertThat(ownSettings.get().getInherited()).isFalse(); | |||
//check if global default is set | |||
assertThat(response.getNewCodePeriodsList()) | |||
.filteredOn(ShowWSResponse::getInherited) | |||
.extracting(ShowWSResponse::getType) | |||
.contains(NewCodePeriods.NewCodePeriodType.SPECIFIC_ANALYSIS); | |||
assertThat(response.getNewCodePeriodsList()) | |||
.filteredOn(ShowWSResponse::getInherited) | |||
.extracting(ShowWSResponse::getValue) | |||
.contains("global_uuid"); | |||
} | |||
@Test | |||
public void list_branch_and_inherited_project_settings() { | |||
ComponentDto project = componentDb.insertMainBranch(); | |||
ComponentDto branchWithOwnSettings = componentDb.insertProjectBranch(project, branchDto -> branchDto.setKey("OWN_SETTINGS")); | |||
componentDb.insertProjectBranch(project, branchDto -> branchDto.setKey("PROJECT_SETTINGS")); | |||
tester.insert(new NewCodePeriodDto() | |||
.setType(NewCodePeriodType.SPECIFIC_ANALYSIS) | |||
.setValue("global_uuid")); | |||
tester.insert(new NewCodePeriodDto() | |||
.setProjectUuid(project.uuid()) | |||
.setType(NewCodePeriodType.SPECIFIC_ANALYSIS) | |||
.setValue("project_uuid")); | |||
tester.insert(new NewCodePeriodDto() | |||
.setProjectUuid(project.uuid()) | |||
.setBranchUuid(branchWithOwnSettings.uuid()) | |||
.setType(NewCodePeriodType.SPECIFIC_ANALYSIS) | |||
.setValue("branch_uuid")); | |||
logInAsProjectAdministrator(project); | |||
ListWSResponse response = ws.newRequest() | |||
.setParam("project", project.getKey()) | |||
.executeProtobuf(ListWSResponse.class); | |||
assertThat(response).isNotNull(); | |||
assertThat(response.getNewCodePeriodsCount()).isEqualTo(3); | |||
assertThat(response.getNewCodePeriodsList()).extracting(ShowWSResponse::getBranchKey) | |||
.contains("master", "OWN_SETTINGS", "PROJECT_SETTINGS"); | |||
Optional<ShowWSResponse> ownSettings = response.getNewCodePeriodsList().stream() | |||
.filter(s -> !s.getInherited()) | |||
.findFirst(); | |||
assertThat(ownSettings).isNotNull(); | |||
assertThat(ownSettings).isNotEmpty(); | |||
assertThat(ownSettings.get().getProjectKey()).isEqualTo(project.getKey()); | |||
assertThat(ownSettings.get().getBranchKey()).isEqualTo("OWN_SETTINGS"); | |||
assertThat(ownSettings.get().getType()).isEqualTo(NewCodePeriods.NewCodePeriodType.SPECIFIC_ANALYSIS); | |||
assertThat(ownSettings.get().getValue()).isEqualTo("branch_uuid"); | |||
assertThat(ownSettings.get().getInherited()).isFalse(); | |||
//check if global default is set | |||
assertThat(response.getNewCodePeriodsList()) | |||
.filteredOn(ShowWSResponse::getInherited) | |||
.extracting(ShowWSResponse::getType) | |||
.contains(NewCodePeriods.NewCodePeriodType.SPECIFIC_ANALYSIS); | |||
assertThat(response.getNewCodePeriodsList()) | |||
.filteredOn(ShowWSResponse::getInherited) | |||
.extracting(ShowWSResponse::getValue) | |||
.contains("project_uuid"); | |||
} | |||
private void createBranches(ComponentDto project, int numberOfBranches, BranchType branchType) { | |||
for (int branchCount = 0; branchCount < numberOfBranches; branchCount++) { | |||
String branchKey = String.format("%s_%d", branchType.name(), branchCount); | |||
componentDb.insertProjectBranch(project, branchDto -> branchDto.setKey(branchKey).setBranchType(branchType)); | |||
} | |||
} | |||
private void logInAsProjectAdministrator(ComponentDto... project) { | |||
userSession.logIn().addProjectPermission(UserRole.ADMIN, project); | |||
} | |||
} |
@@ -102,10 +102,10 @@ public class SetActionTest { | |||
@Test | |||
public void throw_IAE_if_type_is_invalid_for_global() { | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("Invalid type 'DATE'. Overall setting can only be set with types: [PREVIOUS_VERSION, NUMBER_OF_DAYS]"); | |||
expectedException.expectMessage("Invalid type 'SPECIFIC_ANALYSIS'. Overall setting can only be set with types: [PREVIOUS_VERSION, NUMBER_OF_DAYS]"); | |||
ws.newRequest() | |||
.setParam("type", "date") | |||
.setParam("type", "specific_analysis") | |||
.execute(); | |||
} | |||
@@ -115,7 +115,7 @@ public class SetActionTest { | |||
logInAsProjectAdministrator(project); | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("Invalid type 'SPECIFIC_ANALYSIS'. Projects can only be set with types: [DATE, PREVIOUS_VERSION, NUMBER_OF_DAYS]"); | |||
expectedException.expectMessage("Invalid type 'SPECIFIC_ANALYSIS'. Projects can only be set with types: [PREVIOUS_VERSION, NUMBER_OF_DAYS]"); | |||
ws.newRequest() | |||
.setParam("project", project.getKey()) | |||
@@ -123,20 +123,6 @@ public class SetActionTest { | |||
.execute(); | |||
} | |||
// validation of value | |||
@Test | |||
public void throw_IAE_if_no_value_for_date() { | |||
ComponentDto project = componentDb.insertPublicProject(); | |||
logInAsProjectAdministrator(project); | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("New Code Period type 'DATE' requires a value"); | |||
ws.newRequest() | |||
.setParam("project", project.getKey()) | |||
.setParam("type", "date") | |||
.execute(); | |||
} | |||
@Test | |||
public void throw_IAE_if_no_value_for_days() { | |||
ComponentDto project = componentDb.insertMainBranch(); | |||
@@ -165,21 +151,6 @@ public class SetActionTest { | |||
.execute(); | |||
} | |||
@Test | |||
public void throw_IAE_if_date_is_invalid() { | |||
ComponentDto project = componentDb.insertMainBranch(); | |||
logInAsProjectAdministrator(project); | |||
expectedException.expect(IllegalArgumentException.class); | |||
expectedException.expectMessage("Failed to parse date: unknown"); | |||
ws.newRequest() | |||
.setParam("project", project.getKey()) | |||
.setParam("type", "date") | |||
.setParam("branch", "master") | |||
.setParam("value", "unknown") | |||
.execute(); | |||
} | |||
@Test | |||
public void throw_IAE_if_days_is_invalid() { | |||
ComponentDto project = componentDb.insertMainBranch(); |
@@ -173,15 +173,15 @@ public class ShowActionTest { | |||
tester.insert(new NewCodePeriodDto() | |||
.setProjectUuid(project.uuid()) | |||
.setBranchUuid(branch.uuid()) | |||
.setType(NewCodePeriodType.DATE) | |||
.setValue("2018-04-05")); | |||
.setType(NewCodePeriodType.NUMBER_OF_DAYS) | |||
.setValue("1")); | |||
ShowWSResponse response = ws.newRequest() | |||
.setParam("project", project.getKey()) | |||
.setParam("branch", "branch") | |||
.executeProtobuf(ShowWSResponse.class); | |||
assertResponse(response, project.getKey(), "branch", NewCodePeriods.NewCodePeriodType.DATE, "2018-04-05", false); | |||
assertResponse(response, project.getKey(), "branch", NewCodePeriods.NewCodePeriodType.NUMBER_OF_DAYS, "1", false); | |||
} | |||
@Test | |||
@@ -206,15 +206,15 @@ public class ShowActionTest { | |||
tester.insert(new NewCodePeriodDto() | |||
.setProjectUuid(project.uuid()) | |||
.setType(NewCodePeriodType.DATE) | |||
.setValue("2018-04-05")); | |||
.setType(NewCodePeriodType.NUMBER_OF_DAYS) | |||
.setValue("1")); | |||
ShowWSResponse response = ws.newRequest() | |||
.setParam("project", project.getKey()) | |||
.setParam("branch", "branch") | |||
.executeProtobuf(ShowWSResponse.class); | |||
assertResponse(response, project.getKey(), "branch", NewCodePeriods.NewCodePeriodType.DATE, "2018-04-05", true); | |||
assertResponse(response, project.getKey(), "branch", NewCodePeriods.NewCodePeriodType.NUMBER_OF_DAYS, "1", true); | |||
} | |||
@Test |
@@ -173,20 +173,22 @@ public class UnsetActionTest { | |||
@Test | |||
public void delete_project_period_twice() { | |||
ComponentDto project = componentDb.insertMainBranch(); | |||
db.newCodePeriods().insert(project.uuid(), null, NewCodePeriodType.SPECIFIC_ANALYSIS, "uuid"); | |||
ComponentDto project1 = componentDb.insertMainBranch(); | |||
ComponentDto project2 = componentDb.insertMainBranch(); | |||
db.newCodePeriods().insert(project1.uuid(), null, NewCodePeriodType.SPECIFIC_ANALYSIS, "uuid1"); | |||
db.newCodePeriods().insert(project2.uuid(), null, NewCodePeriodType.SPECIFIC_ANALYSIS, "uuid2"); | |||
logInAsProjectAdministrator(project); | |||
logInAsProjectAdministrator(project1); | |||
ws.newRequest() | |||
.setParam("project", project.getKey()) | |||
.setParam("project", project1.getKey()) | |||
.execute(); | |||
assertTableEmpty(); | |||
assertTableContainsOnly(project2.uuid(), null, NewCodePeriodType.SPECIFIC_ANALYSIS, "uuid2"); | |||
ws.newRequest() | |||
.setParam("project", project.getKey()) | |||
.setParam("project", project1.getKey()) | |||
.execute(); | |||
assertTableEmpty(); | |||
assertTableContainsOnly(project2.uuid(), null, NewCodePeriodType.SPECIFIC_ANALYSIS, "uuid2"); | |||
} | |||
@Test |
@@ -1,58 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 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.sonarqube.ws.client.settings; | |||
import java.util.List; | |||
import javax.annotation.Generated; | |||
/** | |||
* This is part of the internal API. | |||
* This is a POST request. | |||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/settings/delete_new_code_period">Further information about this action online (including a response example)</a> | |||
* @since 8.0 | |||
*/ | |||
@Generated("sonar-ws-generator") | |||
public class DeleteNewCodePeriodRequest { | |||
private String branch; | |||
private String project; | |||
/** | |||
*/ | |||
public DeleteNewCodePeriodRequest setBranch(String branch) { | |||
this.branch = branch; | |||
return this; | |||
} | |||
public String getBranch() { | |||
return branch; | |||
} | |||
/** | |||
*/ | |||
public DeleteNewCodePeriodRequest setProject(String project) { | |||
this.project = project; | |||
return this; | |||
} | |||
public String getProject() { | |||
return project; | |||
} | |||
} |
@@ -1,58 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 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.sonarqube.ws.client.settings; | |||
import java.util.List; | |||
import javax.annotation.Generated; | |||
/** | |||
* This is part of the internal API. | |||
* This is a POST request. | |||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/settings/show_new_code_period">Further information about this action online (including a response example)</a> | |||
* @since 8.0 | |||
*/ | |||
@Generated("sonar-ws-generator") | |||
public class ShowNewCodePeriodRequest { | |||
private String branch; | |||
private String project; | |||
/** | |||
*/ | |||
public ShowNewCodePeriodRequest setBranch(String branch) { | |||
this.branch = branch; | |||
return this; | |||
} | |||
public String getBranch() { | |||
return branch; | |||
} | |||
/** | |||
*/ | |||
public ShowNewCodePeriodRequest setProject(String project) { | |||
this.project = project; | |||
return this; | |||
} | |||
public String getProject() { | |||
return project; | |||
} | |||
} |
@@ -1,86 +0,0 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 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.sonarqube.ws.client.settings; | |||
import javax.annotation.Generated; | |||
/** | |||
* This is part of the internal API. | |||
* This is a POST request. | |||
* | |||
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/settings/update_new_code_period">Further information about this action online (including a response example)</a> | |||
* @since 8.0 | |||
*/ | |||
@Generated("sonar-ws-generator") | |||
public class UpdateNewCodePeriodRequest { | |||
private String branch; | |||
private String project; | |||
private String type; | |||
private String value; | |||
/** | |||
* | |||
*/ | |||
public UpdateNewCodePeriodRequest setBranch(String branch) { | |||
this.branch = branch; | |||
return this; | |||
} | |||
public String getBranch() { | |||
return branch; | |||
} | |||
/** | |||
* | |||
*/ | |||
public UpdateNewCodePeriodRequest setProject(String project) { | |||
this.project = project; | |||
return this; | |||
} | |||
public String getProject() { | |||
return project; | |||
} | |||
/** | |||
* This is a mandatory parameter. | |||
*/ | |||
public UpdateNewCodePeriodRequest setType(String type) { | |||
this.type = type; | |||
return this; | |||
} | |||
public String getType() { | |||
return type; | |||
} | |||
/** | |||
* | |||
*/ | |||
public UpdateNewCodePeriodRequest setValue(String value) { | |||
this.value = value; | |||
return this; | |||
} | |||
public String getValue() { | |||
return value; | |||
} | |||
} |
@@ -16,27 +16,30 @@ | |||
// along with this program; if not, write to the Free Software Foundation, | |||
// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
syntax = "proto2"; | |||
syntax = "proto3"; | |||
package sonarqube.ws.batch; | |||
option java_package = "org.sonarqube.ws"; | |||
option java_outer_classname = "NewCodePeriods"; | |||
option optimize_for = SPEED; | |||
// WS api/new_code_periods/show | |||
message ShowWSResponse { | |||
optional string projectKey = 1; | |||
optional string branchKey = 2; | |||
required NewCodePeriodType type = 3; | |||
optional string value = 4; | |||
required bool inherited = 5; | |||
string projectKey = 1; | |||
string branchKey = 2; | |||
NewCodePeriodType type = 3; | |||
string value = 4; | |||
bool inherited = 5; | |||
} | |||
// WS api/new_code_periods/list | |||
message ListWSResponse { | |||
repeated ShowWSResponse newCodePeriods = 1; | |||
} | |||
enum NewCodePeriodType { | |||
PREVIOUS_VERSION = 0; | |||
NUMBER_OF_DAYS = 1; | |||
DATE = 2; | |||
SPECIFIC_ANALYSIS = 3; | |||
SPECIFIC_ANALYSIS = 2; | |||
} |