aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java2
-rw-r--r--server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl6
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskCharacteristicDto.java1
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java22
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDto.java58
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchType.java7
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java13
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java40
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentKeyUpdaterDao.java10
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java2
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/component/KeyType.java26
-rw-r--r--server/sonar-db-dao/src/main/protobuf/db-project-branches.proto38
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml12
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml4
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java211
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDtoTest.java32
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java11
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentKeyUpdaterDaoTest.java56
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java14
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/AddKeyTypeInProjectBranches.java46
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/AddPullRequestBinaryInProjectBranches.java46
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71.java6
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/IncreaseBranchTypeSizeForPullRequest.java45
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MakeKeyTypeNotNullableInProjectBranches.java46
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/ReplaceIndexInProjectBranches.java74
-rw-r--r--server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/SetKeyTypeToBranchInProjectBranches.java53
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/AddKeyTypeInProjectBranchesTest.java56
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/AddPullRequestBinaryInProjectBranchesTest.java57
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71Test.java2
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/IncreaseBranchTypeSizeForPullRequestTest.java67
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/MakeKeyTypeNotNullableInProjectBranchesTest.java61
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/ReplaceIndexInProjectBranchesTest.java87
-rw-r--r--server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/SetKeyTypeToBranchInProjectBranchesTest.java108
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/AddKeyTypeInProjectBranchesTest/project_branches.sql11
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/AddPullRequestBinaryInProjectBranchesTest/project_branches.sql12
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/IncreaseBranchTypeSizeForPullRequestTest/project_branches.sql11
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/MakeKeyTypeNotNullableInProjectBranchesTest/project_branches.sql12
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/ReplaceIndexInProjectBranchesTest/project_branches.sql12
-rw-r--r--server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/SetKeyTypeToBranchInProjectBranchesTest/project_branches.sql12
-rw-r--r--server/sonar-server/src/main/java/org/sonar/ce/settings/ProjectConfigurationFactory.java8
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/badge/ws/MeasureAction.java10
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/badge/ws/QualityGateAction.java9
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/batch/ProjectAction.java11
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/batch/ProjectDataLoader.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/batch/ProjectDataQuery.java11
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/DeleteAction.java93
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/ListAction.java142
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestWsAction.java26
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestWsModule.java32
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestsWs.java61
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestsWsParameters.java31
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/package-info.java23
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/branch/ws/DeleteAction.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/branch/ws/ListAction.java31
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/branch/ws/RenameAction.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java3
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ce/ws/TaskFormatter.java36
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java22
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java24
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java1
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ws/MeasuresWsParameters.java1
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java45
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java60
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolder.java15
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java23
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Branch.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterImpl.java23
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/DefaultBranchImpl.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MergeBranchComponentUuids.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ShortBranchComponentsWithIssues.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegator.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStep.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStep.java4
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStep.java17
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsParser.java17
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java38
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowResponseBuilder.java16
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java41
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/SearchRequest.java11
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/notification/AbstractNewIssuesEmailTemplate.java9
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangeNotification.java18
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangesEmailTemplate.java14
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesEmailTemplate.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java6
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java9
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/measure/live/LiveQualityGateComputerImpl.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java54
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java1
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java50
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java11
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java61
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/ProjectAnalysesWsParameters.java1
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchAction.java19
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchRequest.java15
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGate.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java8
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java36
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/setting/ws/ListDefinitionsAction.java32
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/setting/ws/ResetAction.java33
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/setting/ws/SetAction.java31
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsParameters.java1
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsSupport.java10
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/setting/ws/ValuesAction.java42
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/source/ws/LinesAction.java25
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/source/ws/RawAction.java21
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/test/ws/ListAction.java20
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java11
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/ws/CreateAction.java5
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/user/ws/SetHomepageAction.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/webhook/Branch.java2
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookPayloadFactoryImpl.java8
-rw-r--r--server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java1
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/branch/pr/ws/list-example.json17
-rw-r--r--server/sonar-server/src/main/resources/org/sonar/server/user/ws/create-example.json10
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/badge/ws/MeasureActionTest.java1
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/badge/ws/QualityGateActionTest.java3
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/DeleteActionTest.java147
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/ListActionTest.java372
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/PullRequestWsModuleTest.java35
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/PullRequestsWsParametersTest.java33
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/PullRequestsWsTest.java51
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/branch/ws/ListActionTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java46
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/component/ComponentFinderTest.java26
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/component/ws/AppActionTest.java20
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java38
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java29
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java39
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java20
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java16
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutorTest.java5
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterImplTest.java21
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ShortBranchComponentsWithIssuesTest.java19
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegatorTest.java14
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStepTest.java11
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStepTest.java19
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportPersistComponentsStepTest.java5
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStepTest.java39
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsParserTest.java56
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java55
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowResponseBuilderTest.java59
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangeNotificationTest.java12
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangesEmailTemplateTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java13
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java33
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/measure/live/LiveMeasureComputerImplTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/measure/live/LiveQualityGateComputerImplTest.java12
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java29
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java25
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchHistoryActionTest.java27
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ws/SearchActionTest.java23
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java55
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/setting/ws/ListDefinitionsActionTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/setting/ws/ResetActionTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/setting/ws/SetActionTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/setting/ws/ValuesActionTest.java2
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/source/ws/LinesActionTest.java35
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/source/ws/SourcesWsTest.java4
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/test/ws/ListActionTest.java27
-rw-r--r--server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java21
-rw-r--r--sonar-core/src/main/java/org/sonar/core/config/ScannerProperties.java23
-rw-r--r--sonar-core/src/test/java/org/sonar/core/config/CorePropertyDefinitionsTest.java2
-rw-r--r--sonar-plugin-api/src/main/java/org/sonar/api/ce/posttask/Branch.java2
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java4
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java5
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java56
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java9
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java2
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java46
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java17
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfiguration.java16
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationLoader.java2
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationProvider.java4
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchType.java2
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/DefaultBranchConfiguration.java5
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java23
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequests.java49
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsLoader.java34
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProvider.java51
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/PullRequestInfo.java53
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java3
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFilesProvider.java2
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java2
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java4
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java2
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java13
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java8
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java72
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java39
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisherTest.java16
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java39
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/BranchConfigurationProviderTest.java20
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectBranchesProviderTest.java14
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProviderTest.java63
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesProviderTest.java14
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java9
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java19
-rw-r--r--sonar-scanner-protocol/src/main/protobuf/scanner_report.proto3
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/DefaultWsClient.java10
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java3
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/components/AppRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/components/ComponentsService.java5
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/components/ShowRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/components/TreeRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/duplications/DuplicationsService.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/duplications/ShowRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/issues/IssuesService.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/issues/SearchRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/measures/ComponentRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/measures/ComponentTreeRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/measures/MeasuresService.java5
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/measures/SearchHistoryRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/navigation/ComponentRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/navigation/NavigationService.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/permissions/TemplateGroupsRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalyses/ProjectAnalysesService.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalyses/SearchRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesService.java4
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/DeleteRequest.java62
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/ListRequest.java48
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/ProjectPullRequestsService.java70
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/package-info.java25
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CopyRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CreateConditionRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CreateRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DeleteConditionRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DeselectRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DestroyRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/GetByProjectRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ListRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ProjectStatusRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/QualitygatesService.java11
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/RenameRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SearchRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SelectRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SetAsDefaultRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ShowRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/UpdateConditionRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/rules/SearchRequest.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ListDefinitionsRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ResetRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/settings/SetRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/settings/SettingsService.java8
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ValuesRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/sources/LinesRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/sources/RawRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/sources/SourcesService.java2
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/tests/ListRequest.java14
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/tests/TestsService.java1
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/users/SetHomepageRequest.java67
-rw-r--r--sonar-ws/src/main/java/org/sonarqube/ws/client/users/UsersService.java16
-rw-r--r--sonar-ws/src/main/protobuf/ws-ce.proto2
-rw-r--r--sonar-ws/src/main/protobuf/ws-commons.proto1
-rw-r--r--sonar-ws/src/main/protobuf/ws-components.proto1
-rw-r--r--sonar-ws/src/main/protobuf/ws-duplications.proto1
-rw-r--r--sonar-ws/src/main/protobuf/ws-issues.proto2
-rw-r--r--sonar-ws/src/main/protobuf/ws-measures.proto1
-rw-r--r--sonar-ws/src/main/protobuf/ws-projectbranches.proto17
-rw-r--r--sonar-ws/src/main/protobuf/ws-projectpullrequests.proto49
-rw-r--r--sonar-ws/src/main/protobuf/ws-tests.proto2
-rw-r--r--tests/src/test/java/org/sonarqube/tests/issue/AutoAssignTest.java2
-rw-r--r--tests/src/test/java/org/sonarqube/tests/issue/IssueCreationDatePluginChangedTest.java2
-rw-r--r--tests/src/test/java/org/sonarqube/tests/user/LocalAuthenticationTest.java2
-rwxr-xr-xtravis.sh2
273 files changed, 5341 insertions, 519 deletions
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
index 64ed2a4a89b..f959d2a3fdc 100644
--- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
+++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java
@@ -120,7 +120,7 @@ public class ComputeEngineContainerImplTest {
+ 26 // level 1
+ 53 // content of DaoModule
+ 3 // content of EsModule
- + 57 // content of CorePropertyDefinitions
+ + 59 // content of CorePropertyDefinitions
+ 1 // StopFlagContainer
);
assertThat(
diff --git a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
index e036bd73dda..961ca45a144 100644
--- a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
+++ b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
@@ -733,13 +733,15 @@ CREATE TABLE "PROJECT_BRANCHES" (
"UUID" VARCHAR(50) NOT NULL PRIMARY KEY,
"PROJECT_UUID" VARCHAR(50) NOT NULL,
"KEE" VARCHAR(255) NOT NULL,
- "BRANCH_TYPE" VARCHAR(5),
+ "KEY_TYPE" VARCHAR(12) NOT NULL,
+ "BRANCH_TYPE" VARCHAR(12),
"MERGE_BRANCH_UUID" VARCHAR(50),
+ "PULL_REQUEST_BINARY" BLOB,
"CREATED_AT" BIGINT NOT NULL,
"UPDATED_AT" BIGINT NOT NULL
);
CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID");
-CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE");
+CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE_KEY_TYPE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE", "KEY_TYPE");
CREATE TABLE "ANALYSIS_PROPERTIES" (
"UUID" VARCHAR(40) NOT NULL PRIMARY KEY,
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskCharacteristicDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskCharacteristicDto.java
index 1e342a28a93..1be8ca7416f 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskCharacteristicDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/ce/CeTaskCharacteristicDto.java
@@ -23,6 +23,7 @@ public class CeTaskCharacteristicDto {
public static final String BRANCH_KEY = "branch";
public static final String BRANCH_TYPE_KEY = "branchType";
+ public static final String PULL_REQUEST = "pullRequest";
private String uuid;
private String taskUuid;
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java
index fbbecbc5a7b..834e21bb0cd 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDao.java
@@ -37,24 +37,42 @@ public class BranchDao implements Dao {
}
public void insert(DbSession dbSession, BranchDto dto) {
+ setKeyType(dto);
mapper(dbSession).insert(dto, system2.now());
}
public void upsert(DbSession dbSession, BranchDto dto) {
BranchMapper mapper = mapper(dbSession);
long now = system2.now();
+ setKeyType(dto);
if (mapper.update(dto, now) == 0) {
mapper.insert(dto, now);
}
}
+ private static void setKeyType(BranchDto dto) {
+ if (dto.getBranchType() == BranchType.PULL_REQUEST) {
+ dto.setKeyType(KeyType.PULL_REQUEST);
+ } else {
+ dto.setKeyType(KeyType.BRANCH);
+ }
+ }
+
public int updateMainBranchName(DbSession dbSession, String projectUuid, String newBranchKey) {
long now = system2.now();
return mapper(dbSession).updateMainBranchName(projectUuid, newBranchKey, now);
}
- public Optional<BranchDto> selectByKey(DbSession dbSession, String projectUuid, String key) {
- return Optional.ofNullable(mapper(dbSession).selectByKey(projectUuid, key));
+ public Optional<BranchDto> selectByBranchKey(DbSession dbSession, String projectUuid, String key) {
+ return selectByKey(dbSession, projectUuid, key, KeyType.BRANCH);
+ }
+
+ public Optional<BranchDto> selectByPullRequestKey(DbSession dbSession, String projectUuid, String key) {
+ return selectByKey(dbSession, projectUuid, key, KeyType.PULL_REQUEST);
+ }
+
+ private static Optional<BranchDto> selectByKey(DbSession dbSession, String projectUuid, String key, KeyType keyType) {
+ return Optional.ofNullable(mapper(dbSession).selectByKey(projectUuid, key, keyType));
}
public Collection<BranchDto> selectByComponent(DbSession dbSession, ComponentDto component) {
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDto.java
index 2586d87efc0..a1b11b8d2fb 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchDto.java
@@ -19,8 +19,13 @@
*/
package org.sonar.db.component;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
import java.util.Objects;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
+import org.sonar.db.protobuf.DbProjectBranches;
import static com.google.common.base.Preconditions.checkArgument;
@@ -49,11 +54,19 @@ public class BranchDto {
private String projectUuid;
/**
- * Name of branch, for example "feature/foo".
+ * Key that identifies a branch or a pull request.
+ * For keyType=BRANCH, this is the name of the branch, for example "feature/foo".
+ * For keyType=PULL_REQUEST, this is the ID of the pull request in some external system, for example 123 in GitHub.
*/
private String kee;
/**
+ * Key type, as provided by {@link KeyType}.
+ * Not null.
+ */
+ private KeyType keyType;
+
+ /**
* Branch type, as provided by {@link BranchType}.
* Not null.
*/
@@ -69,6 +82,12 @@ public class BranchDto {
@Nullable
private String mergeBranchUuid;
+ /**
+ * Pull Request data, such as branch name, title, url, and provider specific attributes
+ */
+ @Nullable
+ private byte[] pullRequestBinary;
+
public String getUuid() {
return uuid;
}
@@ -115,6 +134,11 @@ public class BranchDto {
return this;
}
+ BranchDto setKeyType(@Nullable KeyType keyType) {
+ this.keyType = keyType;
+ return this;
+ }
+
public BranchType getBranchType() {
return branchType;
}
@@ -134,6 +158,37 @@ public class BranchDto {
return this;
}
+ public BranchDto setPullRequestData(DbProjectBranches.PullRequestData pullRequestData) {
+ this.pullRequestBinary = encodePullRequestData(pullRequestData);
+ return this;
+ }
+
+ @CheckForNull
+ public DbProjectBranches.PullRequestData getPullRequestData() {
+ if (pullRequestBinary == null) {
+ return null;
+ }
+ return decodePullRequestData(pullRequestBinary);
+ }
+
+ private static byte[] encodePullRequestData(DbProjectBranches.PullRequestData pullRequestData) {
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ try {
+ pullRequestData.writeTo(outputStream);
+ return outputStream.toByteArray();
+ } catch (IOException e) {
+ throw new IllegalStateException("Fail to serialize pull request data", e);
+ }
+ }
+
+ private static DbProjectBranches.PullRequestData decodePullRequestData(byte[] pullRequestBinary) {
+ try (ByteArrayInputStream inputStream = new ByteArrayInputStream(pullRequestBinary)) {
+ return DbProjectBranches.PullRequestData.parseFrom(inputStream);
+ } catch (IOException e) {
+ throw new IllegalStateException("Fail to deserialize pull request data", e);
+ }
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) {
@@ -161,6 +216,7 @@ public class BranchDto {
sb.append("uuid='").append(uuid).append('\'');
sb.append(", projectUuid='").append(projectUuid).append('\'');
sb.append(", kee='").append(kee).append('\'');
+ sb.append(", keyType=").append(keyType);
sb.append(", branchType=").append(branchType);
sb.append(", mergeBranchUuid='").append(mergeBranchUuid).append('\'');
sb.append('}');
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java
index b613ab9f44b..17872a13a0e 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchMapper.java
@@ -31,7 +31,7 @@ public interface BranchMapper {
int updateMainBranchName(@Param("projectUuid") String projectUuid, @Param("newBranchName") String newBranchName, @Param("now") long now);
- BranchDto selectByKey(@Param("projectUuid") String projectUuid, @Param("key") String key);
+ BranchDto selectByKey(@Param("projectUuid") String projectUuid, @Param("key") String key, @Param("keyType") KeyType keyType);
BranchDto selectByUuid(@Param("uuid") String uuid);
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchType.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchType.java
index 3358caf82cf..d5937b7d97d 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchType.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/BranchType.java
@@ -29,5 +29,10 @@ public enum BranchType {
/**
* Short-lived branch
*/
- SHORT
+ SHORT,
+
+ /**
+ * Pull request
+ */
+ PULL_REQUEST
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
index 24eb17266ce..535d585ad73 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
@@ -50,6 +50,7 @@ import static org.sonar.db.DatabaseUtils.executeLargeInputs;
import static org.sonar.db.DatabaseUtils.executeLargeUpdates;
import static org.sonar.db.WildcardPosition.BEFORE_AND_AFTER;
import static org.sonar.db.component.ComponentDto.generateBranchKey;
+import static org.sonar.db.component.ComponentDto.generatePullRequestKey;
public class ComponentDao implements Dao {
@@ -201,6 +202,12 @@ public class ComponentDao implements Dao {
return executeLargeInputs(allKeys, subKeys -> mapper(session).selectByKeysAndBranch(subKeys, branch));
}
+ public List<ComponentDto> selectByKeysAndPullRequest(DbSession session, Collection<String> keys, String pullRequestId) {
+ List<String> dbKeys = keys.stream().map(k -> generatePullRequestKey(k, pullRequestId)).collect(toList());
+ List<String> allKeys = Stream.of(keys, dbKeys).flatMap(Collection::stream).collect(toList());
+ return executeLargeInputs(allKeys, subKeys -> mapper(session).selectByKeysAndBranch(subKeys, pullRequestId));
+ }
+
public List<ComponentDto> selectComponentsHavingSameKeyOrderedById(DbSession session, String key) {
return mapper(session).selectComponentsHavingSameKeyOrderedById(key);
}
@@ -247,7 +254,11 @@ public class ComponentDao implements Dao {
}
public java.util.Optional<ComponentDto> selectByKeyAndBranch(DbSession session, String key, String branch) {
- return java.util.Optional.ofNullable(mapper(session).selectByKeyAndBranch(key, generateBranchKey(key, branch), branch));
+ return java.util.Optional.ofNullable(mapper(session).selectByKeyAndBranchKey(key, generateBranchKey(key, branch), branch));
+ }
+
+ public java.util.Optional<ComponentDto> selectByKeyAndPullRequest(DbSession session, String key, String pullRequestId) {
+ return java.util.Optional.ofNullable(mapper(session).selectByKeyAndBranchKey(key, generatePullRequestKey(key, pullRequestId), pullRequestId));
}
public List<UuidWithProjectUuidDto> selectAllViewsAndSubViews(DbSession session) {
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java
index 75d23fd49e5..ef402d4e48f 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDto.java
@@ -24,15 +24,16 @@ import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import java.util.Date;
import java.util.List;
+import java.util.regex.Pattern;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
-import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.sonar.api.resources.Scopes;
import org.sonar.db.WildcardPosition;
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
+import static org.apache.commons.lang.StringUtils.substringBeforeLast;
import static org.sonar.db.DaoDatabaseUtils.buildLikeValue;
import static org.sonar.db.component.ComponentValidator.checkComponentKey;
import static org.sonar.db.component.ComponentValidator.checkComponentName;
@@ -44,8 +45,11 @@ public class ComponentDto {
* Separator used to generate the key of the branch
*/
public static final String BRANCH_KEY_SEPARATOR = ":BRANCH:";
+ public static final String PULL_REQUEST_SEPARATOR = ":PULL_REQUEST:";
+ private static final Splitter BRANCH_OR_PULL_REQUEST_SPLITTER = Splitter.on(Pattern.compile(BRANCH_KEY_SEPARATOR + "|" + PULL_REQUEST_SEPARATOR));
private static final Splitter BRANCH_KEY_SPLITTER = Splitter.on(BRANCH_KEY_SEPARATOR);
+ private static final Splitter PULL_REQUEST_SPLITTER = Splitter.on(PULL_REQUEST_SEPARATOR);
public static final String UUID_PATH_SEPARATOR = ".";
public static final String UUID_PATH_OF_ROOT = UUID_PATH_SEPARATOR;
@@ -65,7 +69,7 @@ public class ComponentDto {
private String organizationUuid;
/**
- * Non-empty and unique functional key
+ * Non-empty and unique functional key. Do not rename, used by MyBatis.
*/
private String kee;
@@ -206,20 +210,6 @@ public class ComponentDto {
return UUID_PATH_SPLITTER.splitToList(uuidPath);
}
- /**
- * Used my MyBatis mapper
- */
- private String getKee(){
- return kee;
- }
-
- /**
- * Used my MyBatis mapper
- */
- private void setKee(String kee){
- this.kee = kee;
- }
-
public String getDbKey() {
return kee;
}
@@ -233,7 +223,7 @@ public class ComponentDto {
* The key to be displayed to user, doesn't contain information on branches
*/
public String getKey() {
- List<String> split = BRANCH_KEY_SPLITTER.splitToList(kee);
+ List<String> split = BRANCH_OR_PULL_REQUEST_SPLITTER.splitToList(kee);
return split.size() == 2 ? split.get(0) : kee;
}
@@ -246,6 +236,15 @@ public class ComponentDto {
return split.size() == 2 ? split.get(1) : null;
}
+ /**
+ * @return the pull request id. It will be null when the component is not on a pull request
+ */
+ @CheckForNull
+ public String getPullRequest() {
+ List<String> split = PULL_REQUEST_SPLITTER.splitToList(kee);
+ return split.size() == 2 ? split.get(1) : null;
+ }
+
public String scope() {
return scope;
}
@@ -525,8 +524,11 @@ public class ComponentDto {
return format("%s%s%s", componentKey, BRANCH_KEY_SEPARATOR, branch);
}
- public static String removeBranchFromKey(String componentKey) {
- return StringUtils.substringBeforeLast(componentKey, ComponentDto.BRANCH_KEY_SEPARATOR);
+ public static String generatePullRequestKey(String componentKey, String pullRequest) {
+ return format("%s%s%s", componentKey, PULL_REQUEST_SEPARATOR, pullRequest);
}
+ public static String removeBranchAndPullRequestFromKey(String componentKey) {
+ return substringBeforeLast(substringBeforeLast(componentKey, ComponentDto.BRANCH_KEY_SEPARATOR), ComponentDto.PULL_REQUEST_SEPARATOR);
+ }
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentKeyUpdaterDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentKeyUpdaterDao.java
index 5290e3e2a4a..c3a73736926 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentKeyUpdaterDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentKeyUpdaterDao.java
@@ -141,10 +141,14 @@ public class ComponentKeyUpdaterDao implements Dao {
private static String branchBaseKey(String key) {
int index = key.lastIndexOf(ComponentDto.BRANCH_KEY_SEPARATOR);
- if (index == -1) {
- return key;
+ if (index > -1) {
+ return key.substring(0, index);
}
- return key.substring(0, index);
+ index = key.lastIndexOf(ComponentDto.PULL_REQUEST_SEPARATOR);
+ if (index > -1) {
+ return key.substring(0, index);
+ }
+ return key;
}
private static void runBatchUpdateForAllResources(Collection<ResourceDto> resources, String oldKey, String newKey, ComponentKeyUpdaterMapper mapper) {
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java
index 5101dc9dd2e..4560af88e6c 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java
@@ -33,7 +33,7 @@ public interface ComponentMapper {
ComponentDto selectByKey(String key);
@CheckForNull
- ComponentDto selectByKeyAndBranch(@Param("key") String key, @Param("dbKey") String dbKey, @Param("branch") String branch);
+ ComponentDto selectByKeyAndBranchKey(@Param("key") String key, @Param("dbKey") String dbKey, @Param("branch") String branch);
@CheckForNull
ComponentDto selectById(long id);
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/KeyType.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/KeyType.java
new file mode 100644
index 00000000000..f8b2767c0ae
--- /dev/null
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/KeyType.java
@@ -0,0 +1,26 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.db.component;
+
+public enum KeyType {
+ BRANCH,
+
+ PULL_REQUEST
+}
diff --git a/server/sonar-db-dao/src/main/protobuf/db-project-branches.proto b/server/sonar-db-dao/src/main/protobuf/db-project-branches.proto
new file mode 100644
index 00000000000..ac6f5047f4a
--- /dev/null
+++ b/server/sonar-db-dao/src/main/protobuf/db-project-branches.proto
@@ -0,0 +1,38 @@
+/*
+ SonarQube, open source software quality management tool.
+ Copyright (C) 2008-2016 SonarSource
+ mailto:contact AT sonarsource DOT com
+
+ SonarQube 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.
+
+ SonarQube 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.
+*/
+
+// Structure of db column PROJECT_BRANCHES.PULL_REQUEST_DATA
+
+syntax = "proto3";
+
+package sonarqube.db.project_branches;
+
+// The java package can be changed without breaking compatibility.
+// it impacts only the generated Java code.
+option java_package = "org.sonar.db.protobuf";
+option optimize_for = SPEED;
+
+message PullRequestData {
+ string branch = 1;
+ string title = 2;
+ string url = 3;
+
+ map<string, string> attributes = 4;
+}
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml
index f14a166566b..caf89177c7d 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/BranchMapper.xml
@@ -6,8 +6,10 @@
pb.uuid as uuid,
pb.project_uuid as projectUuid,
pb.kee as kee,
+ pb.key_type as keyType,
pb.branch_type as branchType,
- pb.merge_branch_uuid as mergeBranchUuid
+ pb.merge_branch_uuid as mergeBranchUuid,
+ pb.pull_request_binary as pullRequestBinary
</sql>
<insert id="insert" parameterType="map" useGeneratedKeys="false">
@@ -15,16 +17,20 @@
uuid,
project_uuid,
kee,
+ key_type,
branch_type,
merge_branch_uuid,
+ pull_request_binary,
created_at,
updated_at
) values (
#{dto.uuid, jdbcType=VARCHAR},
#{dto.projectUuid, jdbcType=VARCHAR},
#{dto.kee, jdbcType=VARCHAR},
+ #{dto.keyType, jdbcType=VARCHAR},
#{dto.branchType, jdbcType=VARCHAR},
#{dto.mergeBranchUuid, jdbcType=VARCHAR},
+ #{dto.pullRequestBinary, jdbcType=BINARY},
#{now, jdbcType=BIGINT},
#{now, jdbcType=BIGINT}
)
@@ -43,6 +49,7 @@
update project_branches
set
merge_branch_uuid = #{dto.mergeBranchUuid, jdbcType=VARCHAR},
+ pull_request_binary = #{dto.pullRequestBinary, jdbcType=BINARY},
updated_at = #{now, jdbcType=BIGINT}
where
uuid = #{dto.uuid, jdbcType=VARCHAR}
@@ -53,7 +60,8 @@
from project_branches pb
where
pb.project_uuid = #{projectUuid, jdbcType=VARCHAR} and
- pb.kee = #{key, jdbcType=VARCHAR}
+ pb.kee = #{key, jdbcType=VARCHAR} and
+ pb.key_type = #{keyType, jdbcType=VARCHAR}
</select>
<select id="selectByProjectUuid" parameterType="string" resultType="org.sonar.db.component.BranchDto">
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
index e415632ecf7..0ba7f68f4e8 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
@@ -36,7 +36,7 @@
p.kee=#{key,jdbcType=VARCHAR}
</select>
- <select id="selectByKeyAndBranch" parameterType="String" resultType="Component">
+ <select id="selectByKeyAndBranchKey" parameterType="String" resultType="Component">
SELECT
<include refid="componentColumns"/>
FROM projects p
@@ -666,7 +666,7 @@
ON p.uuid = i.component_uuid
JOIN project_branches b
ON i.project_uuid = b.uuid
- AND b.branch_type = 'SHORT'
+ AND (b.branch_type = 'SHORT' OR b.branch_type = 'PULL_REQUEST')
AND b.merge_branch_uuid = #{mergeBranchUuid,jdbcType=VARCHAR}
AND i.status != 'CLOSED'
</select>
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java
index 6a37a316ceb..795b3f20738 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDaoTest.java
@@ -26,6 +26,7 @@ import org.sonar.api.utils.System2;
import org.sonar.api.utils.internal.TestSystem2;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
+import org.sonar.db.protobuf.DbProjectBranches;
import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
@@ -37,7 +38,7 @@ public class BranchDaoTest {
private static final long NOW = 1_000L;
private static final String SELECT_FROM = "select project_uuid as \"projectUuid\", uuid as \"uuid\", branch_type as \"branchType\", " +
- "kee as \"kee\", merge_branch_uuid as \"mergeBranchUuid\", created_at as \"createdAt\", updated_at as \"updatedAt\" " +
+ "kee as \"kee\", merge_branch_uuid as \"mergeBranchUuid\", pull_request_binary as \"pullRequestBinary\", created_at as \"createdAt\", updated_at as \"updatedAt\" " +
"from project_branches ";
private System2 system2 = new TestSystem2().setNow(NOW);
@@ -64,6 +65,7 @@ public class BranchDaoTest {
entry("branchType", "SHORT"),
entry("kee", "feature/foo"),
entry("mergeBranchUuid", null),
+ entry("pullRequestBinary", null),
entry("createdAt", 1_000L),
entry("updatedAt", 1_000L));
}
@@ -85,7 +87,7 @@ public class BranchDaoTest {
underTest.insert(dbSession, dto2);
underTest.updateMainBranchName(dbSession, "U1", "master");
- BranchDto loaded = underTest.selectByKey(dbSession, "U1", "master").get();
+ BranchDto loaded = underTest.selectByBranchKey(dbSession, "U1", "master").get();
assertThat(loaded.getMergeBranchUuid()).isNull();
assertThat(loaded.getProjectUuid()).isEqualTo("U1");
assertThat(loaded.getBranchType()).isEqualTo(BranchType.LONG);
@@ -110,7 +112,76 @@ public class BranchDaoTest {
}
@Test
- public void upsert() {
+ public void insert_pull_request_branch_with_only_non_null_fields() {
+ String projectUuid = "U1";
+ String uuid = "U2";
+ BranchType branchType = BranchType.PULL_REQUEST;
+ String kee = "123";
+
+ BranchDto dto = new BranchDto();
+ dto.setProjectUuid(projectUuid);
+ dto.setUuid(uuid);
+ dto.setBranchType(branchType);
+ dto.setKey(kee);
+
+ underTest.insert(dbSession, dto);
+
+ BranchDto loaded = underTest.selectByUuid(dbSession, dto.getUuid()).get();
+
+ assertThat(loaded.getProjectUuid()).isEqualTo(projectUuid);
+ assertThat(loaded.getUuid()).isEqualTo(uuid);
+ assertThat(loaded.getBranchType()).isEqualTo(branchType);
+ assertThat(loaded.getKey()).isEqualTo(kee);
+ assertThat(loaded.getMergeBranchUuid()).isNull();
+ assertThat(loaded.getPullRequestData()).isNull();
+ }
+
+ @Test
+ public void insert_pull_request_branch_with_all_fields() {
+ String projectUuid = "U1";
+ String uuid = "U2";
+ BranchType branchType = BranchType.PULL_REQUEST;
+ String kee = "123";
+
+ String branch = "feature/pr1";
+ String title = "Dummy Feature Title";
+ String url = "http://example.com/pullRequests/pr1";
+ String tokenAttributeName = "token";
+ String tokenAttributeValue = "dummy token";
+ DbProjectBranches.PullRequestData pullRequestData = DbProjectBranches.PullRequestData.newBuilder()
+ .setBranch(branch)
+ .setTitle(title)
+ .setUrl(url)
+ .putAttributes(tokenAttributeName, tokenAttributeValue)
+ .build();
+
+ BranchDto dto = new BranchDto();
+ dto.setProjectUuid(projectUuid);
+ dto.setUuid(uuid);
+ dto.setBranchType(branchType);
+ dto.setKey(kee);
+ dto.setPullRequestData(pullRequestData);
+
+ underTest.insert(dbSession, dto);
+
+ BranchDto loaded = underTest.selectByUuid(dbSession, dto.getUuid()).get();
+
+ assertThat(loaded.getProjectUuid()).isEqualTo(projectUuid);
+ assertThat(loaded.getUuid()).isEqualTo(uuid);
+ assertThat(loaded.getBranchType()).isEqualTo(branchType);
+ assertThat(loaded.getKey()).isEqualTo(kee);
+ assertThat(loaded.getMergeBranchUuid()).isNull();
+
+ DbProjectBranches.PullRequestData loadedPullRequestData = loaded.getPullRequestData();
+ assertThat(loadedPullRequestData).isNotNull();
+ assertThat(loadedPullRequestData.getBranch()).isEqualTo(branch);
+ assertThat(loadedPullRequestData.getTitle()).isEqualTo(title);
+ assertThat(loadedPullRequestData.getUrl()).isEqualTo(url);
+ assertThat(loadedPullRequestData.getAttributesMap().get(tokenAttributeName)).isEqualTo(tokenAttributeValue);
+ }
+
+ @Test
+ public void upsert_branch() {
BranchDto dto = new BranchDto();
dto.setProjectUuid("U1");
dto.setUuid("U2");
@@ -126,14 +197,110 @@ public class BranchDaoTest {
dto.setBranchType(BranchType.SHORT);
underTest.upsert(dbSession, dto);
- BranchDto loaded = underTest.selectByKey(dbSession, "U1", "foo").get();
+ BranchDto loaded = underTest.selectByBranchKey(dbSession, "U1", "foo").get();
assertThat(loaded.getMergeBranchUuid()).isEqualTo("U3");
assertThat(loaded.getProjectUuid()).isEqualTo("U1");
assertThat(loaded.getBranchType()).isEqualTo(BranchType.LONG);
}
@Test
- public void selectByKey() {
+ public void upsert_pull_request() {
+ BranchDto dto = new BranchDto();
+ dto.setProjectUuid("U1");
+ dto.setUuid("U2");
+ dto.setBranchType(BranchType.PULL_REQUEST);
+ dto.setKey("foo");
+ underTest.insert(dbSession, dto);
+
+ // the fields that can be updated
+ dto.setMergeBranchUuid("U3");
+
+ String branch = "feature/pr1";
+ String title = "Dummy Feature Title";
+ String url = "http://example.com/pullRequests/pr1";
+ String tokenAttributeName = "token";
+ String tokenAttributeValue = "dummy token";
+ DbProjectBranches.PullRequestData pullRequestData = DbProjectBranches.PullRequestData.newBuilder()
+ .setBranch(branch)
+ .setTitle(title)
+ .setUrl(url)
+ .putAttributes(tokenAttributeName, tokenAttributeValue)
+ .build();
+ dto.setPullRequestData(pullRequestData);
+
+ // the fields that can't be updated. New values are ignored.
+ dto.setProjectUuid("ignored");
+ dto.setBranchType(BranchType.SHORT);
+ underTest.upsert(dbSession, dto);
+
+ BranchDto loaded = underTest.selectByPullRequestKey(dbSession, "U1", "foo").get();
+ assertThat(loaded.getMergeBranchUuid()).isEqualTo("U3");
+ assertThat(loaded.getProjectUuid()).isEqualTo("U1");
+ assertThat(loaded.getBranchType()).isEqualTo(BranchType.PULL_REQUEST);
+
+ DbProjectBranches.PullRequestData loadedPullRequestData = loaded.getPullRequestData();
+ assertThat(loadedPullRequestData).isNotNull();
+ assertThat(loadedPullRequestData.getBranch()).isEqualTo(branch);
+ assertThat(loadedPullRequestData.getTitle()).isEqualTo(title);
+ assertThat(loadedPullRequestData.getUrl()).isEqualTo(url);
+ assertThat(loadedPullRequestData.getAttributesMap().get(tokenAttributeName)).isEqualTo(tokenAttributeValue);
+ }
+
+ @Test
+ public void update_pull_request_data() {
+ BranchDto dto = new BranchDto();
+ dto.setProjectUuid("U1");
+ dto.setUuid("U2");
+ dto.setBranchType(BranchType.PULL_REQUEST);
+ dto.setKey("foo");
+
+ // the fields that can be updated
+ String mergeBranchUuid = "U3";
+ dto.setMergeBranchUuid(mergeBranchUuid + "-dummy-suffix");
+
+ String branch = "feature/pr1";
+ String title = "Dummy Feature Title";
+ String url = "http://example.com/pullRequests/pr1";
+ String tokenAttributeName = "token";
+ String tokenAttributeValue = "dummy token";
+ DbProjectBranches.PullRequestData pullRequestData = DbProjectBranches.PullRequestData.newBuilder()
+ .setBranch(branch + "-dummy-suffix")
+ .setTitle(title + "-dummy-suffix")
+ .setUrl(url + "-dummy-suffix")
+ .putAttributes(tokenAttributeName, tokenAttributeValue + "-dummy-suffix")
+ .build();
+ dto.setPullRequestData(pullRequestData);
+
+ underTest.insert(dbSession, dto);
+
+ // modify pull request data
+
+ dto.setMergeBranchUuid(mergeBranchUuid);
+ pullRequestData = DbProjectBranches.PullRequestData.newBuilder()
+ .setBranch(branch)
+ .setTitle(title)
+ .setUrl(url)
+ .putAttributes(tokenAttributeName, tokenAttributeValue)
+ .build();
+ dto.setPullRequestData(pullRequestData);
+
+ underTest.upsert(dbSession, dto);
+
+ BranchDto loaded = underTest.selectByPullRequestKey(dbSession, "U1", "foo").get();
+ assertThat(loaded.getMergeBranchUuid()).isEqualTo(mergeBranchUuid);
+ assertThat(loaded.getProjectUuid()).isEqualTo("U1");
+ assertThat(loaded.getBranchType()).isEqualTo(BranchType.PULL_REQUEST);
+
+ DbProjectBranches.PullRequestData loadedPullRequestData = loaded.getPullRequestData();
+ assertThat(loadedPullRequestData).isNotNull();
+ assertThat(loadedPullRequestData.getBranch()).isEqualTo(branch);
+ assertThat(loadedPullRequestData.getTitle()).isEqualTo(title);
+ assertThat(loadedPullRequestData.getUrl()).isEqualTo(url);
+ assertThat(loadedPullRequestData.getAttributesMap().get(tokenAttributeName)).isEqualTo(tokenAttributeValue);
+ }
+
+ @Test
+ public void selectByBranchKey() {
BranchDto mainBranch = new BranchDto();
mainBranch.setProjectUuid("U1");
mainBranch.setUuid("U1");
@@ -150,7 +317,7 @@ public class BranchDaoTest {
underTest.insert(dbSession, featureBranch);
// select the feature branch
- BranchDto loaded = underTest.selectByKey(dbSession, "U1", "feature/foo").get();
+ BranchDto loaded = underTest.selectByBranchKey(dbSession, "U1", "feature/foo").get();
assertThat(loaded.getUuid()).isEqualTo(featureBranch.getUuid());
assertThat(loaded.getKey()).isEqualTo(featureBranch.getKey());
assertThat(loaded.getProjectUuid()).isEqualTo(featureBranch.getProjectUuid());
@@ -158,7 +325,37 @@ public class BranchDaoTest {
assertThat(loaded.getMergeBranchUuid()).isEqualTo(featureBranch.getMergeBranchUuid());
// select a branch on another project with same branch name
- assertThat(underTest.selectByKey(dbSession, "U3", "feature/foo")).isEmpty();
+ assertThat(underTest.selectByBranchKey(dbSession, "U3", "feature/foo")).isEmpty();
+ }
+
+ @Test
+ public void selectByPullRequestKey() {
+ BranchDto mainBranch = new BranchDto();
+ mainBranch.setProjectUuid("U1");
+ mainBranch.setUuid("U1");
+ mainBranch.setBranchType(BranchType.LONG);
+ mainBranch.setKey("master");
+ underTest.insert(dbSession, mainBranch);
+
+ String pullRequestId = "123";
+ BranchDto pullRequest = new BranchDto();
+ pullRequest.setProjectUuid("U1");
+ pullRequest.setUuid("U2");
+ pullRequest.setBranchType(BranchType.PULL_REQUEST);
+ pullRequest.setKey(pullRequestId);
+ pullRequest.setMergeBranchUuid("U3");
+ underTest.insert(dbSession, pullRequest);
+
+ // select the feature branch
+ BranchDto loaded = underTest.selectByPullRequestKey(dbSession, "U1", pullRequestId).get();
+ assertThat(loaded.getUuid()).isEqualTo(pullRequest.getUuid());
+ assertThat(loaded.getKey()).isEqualTo(pullRequest.getKey());
+ assertThat(loaded.getProjectUuid()).isEqualTo(pullRequest.getProjectUuid());
+ assertThat(loaded.getBranchType()).isEqualTo(pullRequest.getBranchType());
+ assertThat(loaded.getMergeBranchUuid()).isEqualTo(pullRequest.getMergeBranchUuid());
+
+ // select a branch on another project with same branch name
+ assertThat(underTest.selectByPullRequestKey(dbSession, "U3", pullRequestId)).isEmpty();
}
@Test
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDtoTest.java
index b5904e6709f..7da1bce7caa 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDtoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/BranchDtoTest.java
@@ -19,12 +19,18 @@
*/
package org.sonar.db.component;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.protobuf.DbProjectBranches;
import static org.assertj.core.api.Assertions.assertThat;
public class BranchDtoTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
private BranchDto underTest = new BranchDto();
@Test
@@ -42,4 +48,30 @@ public class BranchDtoTest {
assertThat(underTest.isMain()).isFalse();
}
+
+ @Test
+ public void encode_and_decode_pull_request_data() {
+ String branch = "feature/pr1";
+ String title = "Dummy Feature Title";
+ String url = "http://example.com/pullRequests/pr1";
+
+ DbProjectBranches.PullRequestData pullRequestData = DbProjectBranches.PullRequestData.newBuilder()
+ .setBranch(branch)
+ .setTitle(title)
+ .setUrl(url)
+ .build();
+
+ underTest.setPullRequestData(pullRequestData);
+
+ DbProjectBranches.PullRequestData decoded = underTest.getPullRequestData();
+ assertThat(decoded).isNotNull();
+ assertThat(decoded.getBranch()).isEqualTo(branch);
+ assertThat(decoded.getTitle()).isEqualTo(title);
+ assertThat(decoded.getUrl()).isEqualTo(url);
+ }
+
+ @Test
+ public void getPullRequestData_returns_null_when_data_is_null() {
+ assertThat(underTest.getPullRequestData()).isNull();
+ }
}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java
index 709308d9ce6..0b36b57897f 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDtoTest.java
@@ -128,4 +128,15 @@ public class ComponentDtoTest {
assertThat(underTest.getKey()).isEqualTo("my_key");
assertThat(underTest.getBranch()).isNull();
}
+
+ @Test
+ public void getKey_and_getPullRequest() {
+ ComponentDto underTest = new ComponentDto().setDbKey("my_key:PULL_REQUEST:pr-123");
+ assertThat(underTest.getKey()).isEqualTo("my_key");
+ assertThat(underTest.getPullRequest()).isEqualTo("pr-123");
+
+ underTest = new ComponentDto().setDbKey("my_key");
+ assertThat(underTest.getKey()).isEqualTo("my_key");
+ assertThat(underTest.getPullRequest()).isNull();
+ }
}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentKeyUpdaterDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentKeyUpdaterDaoTest.java
index bd1b66fd2f6..3166d3aab8b 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentKeyUpdaterDaoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentKeyUpdaterDaoTest.java
@@ -34,6 +34,7 @@ import org.sonar.db.organization.OrganizationDto;
import static com.google.common.collect.Lists.newArrayList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.entry;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentKeyUpdaterDao.computeNewKey;
import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto;
@@ -106,6 +107,33 @@ public class ComponentKeyUpdaterDaoTest {
}
@Test
+ public void updateKey_updates_pull_requests_too() {
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST));
+ db.components().insertComponent(newFileDto(pullRequest));
+ db.components().insertComponent(newFileDto(pullRequest));
+ int branchComponentCount = 3;
+
+ String oldProjectKey = project.getKey();
+ assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldProjectKey)).hasSize(1);
+
+ String oldBranchKey = pullRequest.getDbKey();
+ assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldBranchKey)).hasSize(branchComponentCount);
+
+ String newProjectKey = "newKey";
+ String newBranchKey = ComponentDto.generatePullRequestKey(newProjectKey, pullRequest.getPullRequest());
+ underTest.updateKey(dbSession, project.uuid(), newProjectKey);
+
+ assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldProjectKey)).isEmpty();
+ assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldBranchKey)).isEmpty();
+
+ assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, newProjectKey)).hasSize(1);
+ assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, newBranchKey)).hasSize(branchComponentCount);
+ db.select(dbSession, "select kee from projects")
+ .forEach(map -> map.values().forEach(k -> assertThat(k.toString()).startsWith(newProjectKey)));
+ }
+
+ @Test
public void bulk_updateKey_updates_branches_too() {
ComponentDto project = db.components().insertMainBranch();
ComponentDto branch = db.components().insertProjectBranch(project);
@@ -133,6 +161,34 @@ public class ComponentKeyUpdaterDaoTest {
.forEach(map -> map.values().forEach(k -> assertThat(k.toString()).startsWith(newProjectKey)));
}
+ @Test
+ public void bulk_updateKey_updates_pull_requests_too() {
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST));
+ ComponentDto module = db.components().insertComponent(prefixDbKeyWithKey(newModuleDto(pullRequest), project.getKey()));
+ db.components().insertComponent(prefixDbKeyWithKey(newFileDto(module), module.getKey()));
+ db.components().insertComponent(prefixDbKeyWithKey(newFileDto(module), module.getKey()));
+ int branchComponentCount = 4;
+
+ String oldProjectKey = project.getKey();
+ assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldProjectKey)).hasSize(1);
+
+ String oldPullRequestKey = pullRequest.getDbKey();
+ assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldPullRequestKey)).hasSize(branchComponentCount);
+
+ String newProjectKey = "newKey";
+ String newPullRequestKey = ComponentDto.generatePullRequestKey(newProjectKey, pullRequest.getPullRequest());
+ underTest.bulkUpdateKey(dbSession, project.uuid(), oldProjectKey, newProjectKey);
+
+ assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldProjectKey)).isEmpty();
+ assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, oldPullRequestKey)).isEmpty();
+
+ assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, newProjectKey)).hasSize(1);
+ assertThat(dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, newPullRequestKey)).hasSize(branchComponentCount);
+ db.select(dbSession, "select kee from projects")
+ .forEach(map -> map.values().forEach(k -> assertThat(k.toString()).startsWith(newProjectKey)));
+ }
+
private ComponentDto prefixDbKeyWithKey(ComponentDto componentDto, String key) {
return componentDto.setDbKey(key + ":" + componentDto.getDbKey());
}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java
index 835ea641197..b0408ddb773 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentTesting.java
@@ -29,6 +29,7 @@ import org.sonar.db.organization.OrganizationDto;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentDto.UUID_PATH_SEPARATOR;
public class ComponentTesting {
@@ -99,7 +100,15 @@ public class ComponentTesting {
private static String generateKey(String key, ComponentDto parentModuleOrProject) {
String branch = parentModuleOrProject.getBranch();
- return branch == null ? key : ComponentDto.generateBranchKey(key, branch);
+ if (branch != null) {
+ return ComponentDto.generateBranchKey(key, branch);
+ }
+ String pullRequest = parentModuleOrProject.getPullRequest();
+ if (pullRequest != null) {
+ return ComponentDto.generatePullRequestKey(key, pullRequest);
+ }
+
+ return key;
}
public static ComponentDto newModuleDto(ComponentDto subProjectOrProject) {
@@ -231,6 +240,7 @@ public class ComponentTesting {
checkArgument(project.qualifier().equals(Qualifiers.PROJECT));
checkArgument(project.getMainBranchProjectUuid() == null);
String branchName = branchDto.getKey();
+ String branchSeparator = branchDto.getBranchType() == PULL_REQUEST ? ":PULL_REQUEST:" : ":BRANCH:";
String uuid = branchDto.getUuid();
return new ComponentDto()
.setUuid(uuid)
@@ -240,7 +250,7 @@ public class ComponentTesting {
.setModuleUuidPath(UUID_PATH_SEPARATOR + uuid + UUID_PATH_SEPARATOR)
.setRootUuid(uuid)
// name of the branch is not mandatory on the main branch
- .setDbKey(branchName != null ? project.getDbKey() + ":BRANCH:" + branchName : project.getKey())
+ .setDbKey(branchName != null ? project.getDbKey() + branchSeparator + branchName : project.getKey())
.setMainBranchProjectUuid(project.uuid())
.setName(project.name())
.setLongName(project.longName())
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/AddKeyTypeInProjectBranches.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/AddKeyTypeInProjectBranches.java
new file mode 100644
index 00000000000..8b4137cf4dc
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/AddKeyTypeInProjectBranches.java
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class AddKeyTypeInProjectBranches extends DdlChange {
+
+ public static final String TABLE_NAME = "project_branches";
+
+ public AddKeyTypeInProjectBranches(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME)
+ .addColumn(VarcharColumnDef.newVarcharColumnDefBuilder()
+ .setColumnName("key_type")
+ .setIsNullable(true)
+ .setLimit(12)
+ .build())
+ .build());
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/AddPullRequestBinaryInProjectBranches.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/AddPullRequestBinaryInProjectBranches.java
new file mode 100644
index 00000000000..2c6872a7fda
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/AddPullRequestBinaryInProjectBranches.java
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.BlobColumnDef;
+import org.sonar.server.platform.db.migration.sql.AddColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class AddPullRequestBinaryInProjectBranches extends DdlChange {
+
+ static final String TABLE_NAME = "project_branches";
+ static final String COLUMN_NAME = "pull_request_binary";
+
+ public AddPullRequestBinaryInProjectBranches(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AddColumnsBuilder(getDialect(), TABLE_NAME)
+ .addColumn(BlobColumnDef.newBlobColumnDefBuilder()
+ .setColumnName(COLUMN_NAME)
+ .setIsNullable(true)
+ .build())
+ .build());
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71.java
index 9c406e6eb99..d8f5d82738c 100644
--- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71.java
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71.java
@@ -43,6 +43,12 @@ public class DbVersion71 implements DbVersion {
.add(2013, "Create WEBHOOKS Table", CreateWebhooksTable.class)
.add(2014, "Migrate webhooks from SETTINGS table to WEBHOOKS table", MigrateWebhooksToWebhooksTable.class)
.add(2015, "Add webhook key to WEBHOOK_DELIVERIES table", AddWebhookKeyToWebhookDeliveriesTable.class)
+ .add(2016, "Increase branch type size in PROJECT_BRANCHES", IncreaseBranchTypeSizeForPullRequest.class)
+ .add(2017, "Add key_type column in PROJECT_BRANCHES", AddKeyTypeInProjectBranches.class)
+ .add(2018, "Fill key_type column in PROJECT_BRANCHES", SetKeyTypeToBranchInProjectBranches.class)
+ .add(2019, "Make key_type not nullable in PROJECT_BRANCHES", MakeKeyTypeNotNullableInProjectBranches.class)
+ .add(2020, "Replace index in PROJECT_BRANCHES", ReplaceIndexInProjectBranches.class)
+ .add(2021, "Add pull_request_data in PROJECT_BRANCHES", AddPullRequestBinaryInProjectBranches.class)
;
}
}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/IncreaseBranchTypeSizeForPullRequest.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/IncreaseBranchTypeSizeForPullRequest.java
new file mode 100644
index 00000000000..a0c2df03090
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/IncreaseBranchTypeSizeForPullRequest.java
@@ -0,0 +1,45 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class IncreaseBranchTypeSizeForPullRequest extends DdlChange {
+ private static final String TABLE_NAME = "project_branches";
+
+ public IncreaseBranchTypeSizeForPullRequest(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AlterColumnsBuilder(getDialect(), TABLE_NAME)
+ .updateColumn(newVarcharColumnDefBuilder()
+ .setColumnName("branch_type")
+ .setLimit(12)
+ .build())
+ .build());
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MakeKeyTypeNotNullableInProjectBranches.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MakeKeyTypeNotNullableInProjectBranches.java
new file mode 100644
index 00000000000..4c4951eaded
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/MakeKeyTypeNotNullableInProjectBranches.java
@@ -0,0 +1,46 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.AlterColumnsBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class MakeKeyTypeNotNullableInProjectBranches extends DdlChange {
+ static final String TABLE_NAME = "project_branches";
+
+ public MakeKeyTypeNotNullableInProjectBranches(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new AlterColumnsBuilder(getDialect(), TABLE_NAME)
+ .updateColumn(newVarcharColumnDefBuilder()
+ .setColumnName("key_type")
+ .setLimit(12)
+ .setIsNullable(false)
+ .build())
+ .build());
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/ReplaceIndexInProjectBranches.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/ReplaceIndexInProjectBranches.java
new file mode 100644
index 00000000000..3fb289afdae
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/ReplaceIndexInProjectBranches.java
@@ -0,0 +1,74 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.def.VarcharColumnDef;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.sql.DropIndexBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class ReplaceIndexInProjectBranches extends DdlChange {
+
+ static final String TABLE_NAME = "project_branches";
+ private static final String OLD_INDEX_NAME = "project_branches_kee";
+ static final String NEW_INDEX_NAME = "project_branches_kee_key_type";
+
+ static final VarcharColumnDef PROJECT_UUID_COLUMN = VarcharColumnDef.newVarcharColumnDefBuilder()
+ .setColumnName("project_uuid")
+ .setIsNullable(false)
+ .setLimit(50)
+ .build();
+
+ static final VarcharColumnDef KEE_COLUMN = VarcharColumnDef.newVarcharColumnDefBuilder()
+ .setColumnName("kee")
+ .setIsNullable(false)
+ .setLimit(255)
+ .build();
+
+ static final VarcharColumnDef KEY_TYPE_COLUMN = VarcharColumnDef.newVarcharColumnDefBuilder()
+ .setColumnName("key_type")
+ .setIsNullable(false)
+ .setLimit(12)
+ .build();
+
+ public ReplaceIndexInProjectBranches(Database db) {
+ super(db);
+ }
+
+ @Override
+ public void execute(Context context) throws SQLException {
+ context.execute(new DropIndexBuilder(getDialect())
+ .setTable(TABLE_NAME)
+ .setName(OLD_INDEX_NAME)
+ .build());
+
+ context.execute(new CreateIndexBuilder(getDialect())
+ .addColumn(PROJECT_UUID_COLUMN)
+ .addColumn(KEE_COLUMN)
+ .addColumn(KEY_TYPE_COLUMN)
+ .setUnique(true)
+ .setTable(TABLE_NAME)
+ .setName(NEW_INDEX_NAME)
+ .build()
+ );
+ }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/SetKeyTypeToBranchInProjectBranches.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/SetKeyTypeToBranchInProjectBranches.java
new file mode 100644
index 00000000000..7f3739fba80
--- /dev/null
+++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/SetKeyTypeToBranchInProjectBranches.java
@@ -0,0 +1,53 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+
+public class SetKeyTypeToBranchInProjectBranches extends DataChange {
+ static final String TABLE_NAME = "project_branches";
+ static final String DEFAULT_KEY_TYPE = "BRANCH";
+
+ private final System2 system2;
+
+ public SetKeyTypeToBranchInProjectBranches(Database db, System2 system2) {
+ super(db);
+ this.system2 = system2;
+ }
+
+ @Override
+ protected void execute(Context context) throws SQLException {
+ long now = system2.now();
+ MassUpdate massUpdate = context.prepareMassUpdate();
+ massUpdate.rowPluralName("branches");
+ massUpdate.select("select uuid from " + TABLE_NAME + " where key_type is null");
+ massUpdate.update("update " + TABLE_NAME + " set key_type=?, updated_at=? where uuid = ?");
+ massUpdate.execute((row, update) -> {
+ update.setString(1, DEFAULT_KEY_TYPE);
+ update.setLong(2, now);
+ update.setString(3, row.getString(1));
+ return true;
+ });
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/AddKeyTypeInProjectBranchesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/AddKeyTypeInProjectBranchesTest.java
new file mode 100644
index 00000000000..b4b9ff02989
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/AddKeyTypeInProjectBranchesTest.java
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.VARCHAR;
+
+public class AddKeyTypeInProjectBranchesTest {
+ public static final String TABLE_NAME = "project_branches";
+
+ @Rule
+ public final CoreDbTester dbTester = CoreDbTester.createForSchema(AddKeyTypeInProjectBranchesTest.class, TABLE_NAME + ".sql");
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private AddKeyTypeInProjectBranches underTest = new AddKeyTypeInProjectBranches(dbTester.database());
+
+ @Test
+ public void column_is_added_to_table() throws SQLException {
+ underTest.execute();
+
+ dbTester.assertColumnDefinition(TABLE_NAME, "key_type", VARCHAR, null, true);
+ }
+
+ @Test
+ public void migration_is_not_reentrant() throws SQLException {
+ underTest.execute();
+
+ expectedException.expect(IllegalStateException.class);
+
+ underTest.execute();
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/AddPullRequestBinaryInProjectBranchesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/AddPullRequestBinaryInProjectBranchesTest.java
new file mode 100644
index 00000000000..970b5ea6d42
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/AddPullRequestBinaryInProjectBranchesTest.java
@@ -0,0 +1,57 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static java.sql.Types.BLOB;
+import static org.sonar.server.platform.db.migration.version.v71.AddPullRequestBinaryInProjectBranches.COLUMN_NAME;
+import static org.sonar.server.platform.db.migration.version.v71.AddPullRequestBinaryInProjectBranches.TABLE_NAME;
+
+public class AddPullRequestBinaryInProjectBranchesTest {
+
+ @Rule
+ public final CoreDbTester dbTester = CoreDbTester.createForSchema(AddPullRequestBinaryInProjectBranchesTest.class, TABLE_NAME + ".sql");
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private AddPullRequestBinaryInProjectBranches underTest = new AddPullRequestBinaryInProjectBranches(dbTester.database());
+
+ @Test
+ public void column_is_added_to_table() throws SQLException {
+ underTest.execute();
+
+ dbTester.assertColumnDefinition(TABLE_NAME, COLUMN_NAME, BLOB, null, true);
+ }
+
+ @Test
+ public void migration_is_not_reentrant() throws SQLException {
+ underTest.execute();
+
+ expectedException.expect(IllegalStateException.class);
+
+ underTest.execute();
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71Test.java
index 6b5041ef512..f50e8910b81 100644
--- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71Test.java
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71Test.java
@@ -36,7 +36,7 @@ public class DbVersion71Test {
@Test
public void verify_migration_count() {
- verifyMigrationCount(underTest, 16);
+ verifyMigrationCount(underTest, 22);
}
}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/IncreaseBranchTypeSizeForPullRequestTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/IncreaseBranchTypeSizeForPullRequestTest.java
new file mode 100644
index 00000000000..d9d121a3035
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/IncreaseBranchTypeSizeForPullRequestTest.java
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+import org.sonar.scanner.protocol.output.ScannerReport;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class IncreaseBranchTypeSizeForPullRequestTest {
+ private static final String TABLE_NAME = "project_branches";
+
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(IncreaseBranchTypeSizeForPullRequestTest.class, "project_branches.sql");
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private IncreaseBranchTypeSizeForPullRequest underTest = new IncreaseBranchTypeSizeForPullRequest(db.database());
+
+ @Test
+ public void cannot_insert_PULL_REQUEST_type_before_migration() {
+ expectedException.expect(IllegalStateException.class);
+
+ insertRow();
+ }
+
+ @Test
+ public void can_insert_PULL_REQUEST_after_execute() throws SQLException {
+ underTest.execute();
+ assertThat(db.countRowsOfTable(TABLE_NAME)).isEqualTo(0);
+ insertRow();
+ assertThat(db.countRowsOfTable(TABLE_NAME)).isEqualTo(1);
+ }
+
+ private void insertRow() {
+ db.executeInsert(
+ "PROJECT_BRANCHES",
+ "UUID", "dummy_uuid",
+ "PROJECT_UUID", "dummy_project_uuid",
+ "KEE", "dummy_key",
+ "CREATED_AT", 456789,
+ "UPDATED_AT", 456789,
+ "BRANCH_TYPE", "PULL_REQUEST");
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/MakeKeyTypeNotNullableInProjectBranchesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/MakeKeyTypeNotNullableInProjectBranchesTest.java
new file mode 100644
index 00000000000..7b7d7f35246
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/MakeKeyTypeNotNullableInProjectBranchesTest.java
@@ -0,0 +1,61 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.rule.RuleScope;
+import org.sonar.db.CoreDbTester;
+
+import static org.sonar.server.platform.db.migration.version.v71.MakeKeyTypeNotNullableInProjectBranches.TABLE_NAME;
+
+public class MakeKeyTypeNotNullableInProjectBranchesTest {
+ @Rule
+ public CoreDbTester db = CoreDbTester.createForSchema(MakeKeyTypeNotNullableInProjectBranchesTest.class, "project_branches.sql");
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private MakeKeyTypeNotNullableInProjectBranches underTest = new MakeKeyTypeNotNullableInProjectBranches(db.database());
+
+ @Test
+ public void execute_makes_column_not_null() throws SQLException {
+ db.assertColumnDefinition(TABLE_NAME, "key_type", Types.VARCHAR, null, true);
+ insertRow();
+
+ underTest.execute();
+
+ db.assertColumnDefinition(TABLE_NAME, "key_type", Types.VARCHAR, null, false);
+ }
+
+ private void insertRow() {
+ db.executeInsert(
+ "PROJECT_BRANCHES",
+ "UUID", "dummy_uuid",
+ "PROJECT_UUID", "dummy_project_uuid",
+ "KEE", "dummy_key",
+ "KEY_TYPE", "BRANCH",
+ "CREATED_AT", 456789,
+ "UPDATED_AT", 456789,
+ "BRANCH_TYPE", "BRANCH");
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/ReplaceIndexInProjectBranchesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/ReplaceIndexInProjectBranchesTest.java
new file mode 100644
index 00000000000..70a051073bc
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/ReplaceIndexInProjectBranchesTest.java
@@ -0,0 +1,87 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static org.sonar.server.platform.db.migration.version.v71.ReplaceIndexInProjectBranches.NEW_INDEX_NAME;
+import static org.sonar.server.platform.db.migration.version.v71.ReplaceIndexInProjectBranches.KEE_COLUMN;
+import static org.sonar.server.platform.db.migration.version.v71.ReplaceIndexInProjectBranches.KEY_TYPE_COLUMN;
+import static org.sonar.server.platform.db.migration.version.v71.ReplaceIndexInProjectBranches.PROJECT_UUID_COLUMN;
+import static org.sonar.server.platform.db.migration.version.v71.ReplaceIndexInProjectBranches.TABLE_NAME;
+
+public class ReplaceIndexInProjectBranchesTest {
+ @Rule
+ public final CoreDbTester dbTester = CoreDbTester.createForSchema(ReplaceIndexInProjectBranchesTest.class, "project_branches.sql");
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private ReplaceIndexInProjectBranches underTest = new ReplaceIndexInProjectBranches(dbTester.database());
+
+ @Test
+ public void column_is_part_of_index() throws SQLException {
+ underTest.execute();
+
+ dbTester.assertUniqueIndex(TABLE_NAME, NEW_INDEX_NAME, PROJECT_UUID_COLUMN.getName(), KEE_COLUMN.getName(), KEY_TYPE_COLUMN.getName());
+ }
+
+ @Test
+ public void adding_pr_with_same_key_as_existing_branch_fails_before_migration() {
+ expectedException.expect(IllegalStateException.class);
+
+ String key = "feature/foo";
+ insertBranch(1, key);
+ insertPullRequest(2, key);
+ }
+
+ @Test
+ public void adding_pr_with_same_key_as_existing_branch_works_after_migration() throws SQLException {
+ underTest.execute();
+
+ String key = "feature/foo";
+ insertBranch(1, key);
+ insertPullRequest(2, key);
+ }
+
+ private void insertBranch(int id, String name) {
+ insertRow(id, "SHORT", name, "BRANCH");
+ }
+
+ private void insertPullRequest(int id, String pullRequestId) {
+ insertRow(id, "PULL_REQUEST", pullRequestId, "PULL_REQUEST");
+ }
+
+ private void insertRow(int id, String branchType, String key, String keyType) {
+ dbTester.executeInsert(
+ "PROJECT_BRANCHES",
+ "UUID", "dummy_uuid" + id,
+ "PROJECT_UUID", "dummy_project_uuid",
+ "KEE", key,
+ "KEY_TYPE", keyType,
+ "CREATED_AT", 456789 + id,
+ "UPDATED_AT", 456789 + id,
+ "BRANCH_TYPE", branchType);
+ }
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/SetKeyTypeToBranchInProjectBranchesTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/SetKeyTypeToBranchInProjectBranchesTest.java
new file mode 100644
index 00000000000..c8438784529
--- /dev/null
+++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/SetKeyTypeToBranchInProjectBranchesTest.java
@@ -0,0 +1,108 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import javax.annotation.Nullable;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.db.CoreDbTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.server.platform.db.migration.version.v71.SetKeyTypeToBranchInProjectBranches.DEFAULT_KEY_TYPE;
+import static org.sonar.server.platform.db.migration.version.v71.SetKeyTypeToBranchInProjectBranches.TABLE_NAME;
+
+public class SetKeyTypeToBranchInProjectBranchesTest {
+ private static final long PAST = 10_000_000_000L;
+ private static final long NOW = 50_000_000_000L;
+
+ private System2 system2 = new TestSystem2().setNow(NOW);
+
+ @Rule
+ public final CoreDbTester dbTester = CoreDbTester.createForSchema(SetKeyTypeToBranchInProjectBranchesTest.class, "project_branches.sql");
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ private SetKeyTypeToBranchInProjectBranches underTest = new SetKeyTypeToBranchInProjectBranches(dbTester.database(), system2);
+
+ @Test
+ public void has_no_effect_if_table_project_branches_is_empty() throws SQLException {
+ underTest.execute();
+
+ assertThat(dbTester.countRowsOfTable(TABLE_NAME)).isEqualTo(0);
+ }
+
+ @Test
+ public void updates_rows_to_BRANCH() throws SQLException {
+ insertRow(1, "SHORT");
+ insertRow(2, "LONG");
+ insertRow(3, "SHORT");
+ insertRow(4, "LONG");
+
+ String countUpdatedAtSQL = "select count(uuid) from " + TABLE_NAME + " where updated_at = ";
+
+ assertThat(countRowsWithValue(null)).isEqualTo(4);
+ assertThat(countRowsWithValue(DEFAULT_KEY_TYPE)).isEqualTo(0);
+ assertThat(dbTester.countSql(countUpdatedAtSQL + PAST)).isEqualTo(4);
+
+ underTest.execute();
+
+ assertThat(countRowsWithValue(null)).isEqualTo(0);
+ assertThat(countRowsWithValue(DEFAULT_KEY_TYPE)).isEqualTo(4);
+ assertThat(dbTester.countSql(countUpdatedAtSQL + NOW)).isEqualTo(4);
+ }
+
+ @Test
+ public void execute_is_reentreant() throws SQLException {
+ insertRow(1, "SHORT");
+ insertRow(2, "LONG");
+ insertRow(3, "SHORT");
+ insertRow(4, "LONG");
+
+ underTest.execute();
+
+ underTest.execute();
+
+ assertThat(countRowsWithValue(null)).isEqualTo(0);
+ assertThat(countRowsWithValue(DEFAULT_KEY_TYPE)).isEqualTo(4);
+ }
+
+ private int countRowsWithValue(@Nullable String value) {
+ if (value == null) {
+ return dbTester.countSql("select count(1) from " + TABLE_NAME + " where key_type is null");
+ }
+ return dbTester.countSql("select count(1) from " + TABLE_NAME + " where key_type = '" + value + "'");
+ }
+
+ private void insertRow(int id, String branchType) {
+ dbTester.executeInsert(
+ "PROJECT_BRANCHES",
+ "UUID", "dummy_uuid" + id,
+ "PROJECT_UUID", "dummy_project_uuid" + id,
+ "KEE", "dummy_key" + id,
+ "CREATED_AT", PAST,
+ "UPDATED_AT", PAST,
+ "BRANCH_TYPE", branchType);
+ }
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/AddKeyTypeInProjectBranchesTest/project_branches.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/AddKeyTypeInProjectBranchesTest/project_branches.sql
new file mode 100644
index 00000000000..7f1ef8adc6d
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/AddKeyTypeInProjectBranchesTest/project_branches.sql
@@ -0,0 +1,11 @@
+CREATE TABLE "PROJECT_BRANCHES" (
+ "UUID" VARCHAR(50) NOT NULL PRIMARY KEY,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "KEE" VARCHAR(255) NOT NULL,
+ "BRANCH_TYPE" VARCHAR(12),
+ "MERGE_BRANCH_UUID" VARCHAR(50),
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID");
+CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/AddPullRequestBinaryInProjectBranchesTest/project_branches.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/AddPullRequestBinaryInProjectBranchesTest/project_branches.sql
new file mode 100644
index 00000000000..d3680968980
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/AddPullRequestBinaryInProjectBranchesTest/project_branches.sql
@@ -0,0 +1,12 @@
+CREATE TABLE "PROJECT_BRANCHES" (
+ "UUID" VARCHAR(50) NOT NULL PRIMARY KEY,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "KEE" VARCHAR(255) NOT NULL,
+ "KEY_TYPE" VARCHAR(12) NOT NULL,
+ "BRANCH_TYPE" VARCHAR(12),
+ "MERGE_BRANCH_UUID" VARCHAR(50),
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID");
+CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE", "KEY_TYPE");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/IncreaseBranchTypeSizeForPullRequestTest/project_branches.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/IncreaseBranchTypeSizeForPullRequestTest/project_branches.sql
new file mode 100644
index 00000000000..e05ff3bd2bb
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/IncreaseBranchTypeSizeForPullRequestTest/project_branches.sql
@@ -0,0 +1,11 @@
+CREATE TABLE "PROJECT_BRANCHES" (
+ "UUID" VARCHAR(50) NOT NULL PRIMARY KEY,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "KEE" VARCHAR(255) NOT NULL,
+ "BRANCH_TYPE" VARCHAR(5),
+ "MERGE_BRANCH_UUID" VARCHAR(50),
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID");
+CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/MakeKeyTypeNotNullableInProjectBranchesTest/project_branches.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/MakeKeyTypeNotNullableInProjectBranchesTest/project_branches.sql
new file mode 100644
index 00000000000..6441ce1ca02
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/MakeKeyTypeNotNullableInProjectBranchesTest/project_branches.sql
@@ -0,0 +1,12 @@
+CREATE TABLE "PROJECT_BRANCHES" (
+ "UUID" VARCHAR(50) NOT NULL PRIMARY KEY,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "KEE" VARCHAR(255) NOT NULL,
+ "KEY_TYPE" VARCHAR(12) NULL,
+ "BRANCH_TYPE" VARCHAR(12),
+ "MERGE_BRANCH_UUID" VARCHAR(50),
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID");
+CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/ReplaceIndexInProjectBranchesTest/project_branches.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/ReplaceIndexInProjectBranchesTest/project_branches.sql
new file mode 100644
index 00000000000..b7553cf6dcc
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/ReplaceIndexInProjectBranchesTest/project_branches.sql
@@ -0,0 +1,12 @@
+CREATE TABLE "PROJECT_BRANCHES" (
+ "UUID" VARCHAR(50) NOT NULL PRIMARY KEY,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "KEE" VARCHAR(255) NOT NULL,
+ "KEY_TYPE" VARCHAR(12) NOT NULL,
+ "BRANCH_TYPE" VARCHAR(12),
+ "MERGE_BRANCH_UUID" VARCHAR(50),
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID");
+CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE");
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/SetKeyTypeToBranchInProjectBranchesTest/project_branches.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/SetKeyTypeToBranchInProjectBranchesTest/project_branches.sql
new file mode 100644
index 00000000000..6441ce1ca02
--- /dev/null
+++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/SetKeyTypeToBranchInProjectBranchesTest/project_branches.sql
@@ -0,0 +1,12 @@
+CREATE TABLE "PROJECT_BRANCHES" (
+ "UUID" VARCHAR(50) NOT NULL PRIMARY KEY,
+ "PROJECT_UUID" VARCHAR(50) NOT NULL,
+ "KEE" VARCHAR(255) NOT NULL,
+ "KEY_TYPE" VARCHAR(12) NULL,
+ "BRANCH_TYPE" VARCHAR(12),
+ "MERGE_BRANCH_UUID" VARCHAR(50),
+ "CREATED_AT" BIGINT NOT NULL,
+ "UPDATED_AT" BIGINT NOT NULL
+);
+CREATE UNIQUE INDEX "PK_PROJECT_BRANCHES" ON "PROJECT_BRANCHES" ("UUID");
+CREATE UNIQUE INDEX "PROJECT_BRANCHES_KEE" ON "PROJECT_BRANCHES" ("PROJECT_UUID", "KEE");
diff --git a/server/sonar-server/src/main/java/org/sonar/ce/settings/ProjectConfigurationFactory.java b/server/sonar-server/src/main/java/org/sonar/ce/settings/ProjectConfigurationFactory.java
index c4f90f0d41d..13b6c4ae023 100644
--- a/server/sonar-server/src/main/java/org/sonar/ce/settings/ProjectConfigurationFactory.java
+++ b/server/sonar-server/src/main/java/org/sonar/ce/settings/ProjectConfigurationFactory.java
@@ -24,10 +24,12 @@ import org.sonar.api.config.Configuration;
import org.sonar.api.config.Settings;
import org.sonar.api.config.internal.ConfigurationBridge;
import org.sonar.db.DbClient;
+import org.sonar.db.component.BranchType;
import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
import org.sonar.server.settings.ChildSettings;
import static org.sonar.db.component.ComponentDto.generateBranchKey;
+import static org.sonar.db.component.ComponentDto.generatePullRequestKey;
@ComputeEngineSide
public class ProjectConfigurationFactory {
@@ -43,7 +45,11 @@ public class ProjectConfigurationFactory {
public Configuration newProjectConfiguration(String projectKey, Branch branch) {
Settings projectSettings = new ChildSettings(globalSettings);
addSettings(projectSettings, projectKey);
- addSettings(projectSettings, generateBranchKey(projectKey, branch.getName()));
+ if (branch.getType() == BranchType.PULL_REQUEST) {
+ addSettings(projectSettings, generatePullRequestKey(projectKey, branch.getPullRequestId()));
+ } else {
+ addSettings(projectSettings, generateBranchKey(projectKey, branch.getName()));
+ }
return new ConfigurationBridge(projectSettings);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/badge/ws/MeasureAction.java b/server/sonar-server/src/main/java/org/sonar/server/badge/ws/MeasureAction.java
index 08cf30bae79..0fbac10bdb5 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/badge/ws/MeasureAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/badge/ws/MeasureAction.java
@@ -55,7 +55,6 @@ import static org.sonar.api.measures.CoreMetrics.SECURITY_RATING_KEY;
import static org.sonar.api.measures.CoreMetrics.SQALE_RATING_KEY;
import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY;
import static org.sonar.api.measures.CoreMetrics.VULNERABILITIES_KEY;
-import static org.sonar.api.measures.CoreMetrics.NCLOC_KEY;
import static org.sonar.api.measures.Metric.Level;
import static org.sonar.api.measures.Metric.ValueType;
import static org.sonar.api.measures.Metric.Level.ERROR;
@@ -73,12 +72,14 @@ import static org.sonar.server.computation.task.projectanalysis.qualitymodel.Rat
import static org.sonar.server.computation.task.projectanalysis.qualitymodel.Rating.valueOf;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
import static org.sonarqube.ws.MediaTypes.SVG;
public class MeasureAction implements ProjectBadgesWsAction {
private static final String PARAM_PROJECT = "project";
private static final String PARAM_BRANCH = "branch";
+ private static final String PARAM_PULL_REQUEST = "pullRequest";
private static final String PARAM_METRIC = "metric";
private static final Map<String, String> METRIC_NAME_BY_KEY = ImmutableMap.<String, String>builder()
@@ -140,6 +141,10 @@ public class MeasureAction implements ProjectBadgesWsAction {
.createParam(PARAM_BRANCH)
.setDescription("Branch key")
.setExampleValue(KEY_BRANCH_EXAMPLE_001);
+ action
+ .createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001);
action.createParam(PARAM_METRIC)
.setDescription("Metric key")
.setRequired(true)
@@ -151,9 +156,10 @@ public class MeasureAction implements ProjectBadgesWsAction {
response.stream().setMediaType(SVG);
String projectKey = request.mandatoryParam(PARAM_PROJECT);
String branch = request.param(PARAM_BRANCH);
+ String pullRequest = request.param(PARAM_PULL_REQUEST);
String metricKey = request.mandatoryParam(PARAM_METRIC);
try (DbSession dbSession = dbClient.openSession(false)) {
- ComponentDto project = componentFinder.getByKeyAndOptionalBranch(dbSession, projectKey, branch);
+ ComponentDto project = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, branch, pullRequest);
userSession.checkComponentPermission(USER, project);
MetricDto metric = dbClient.metricDao().selectByKey(dbSession, metricKey);
checkState(metric != null && metric.isEnabled(), "Metric '%s' hasn't been found", metricKey);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/badge/ws/QualityGateAction.java b/server/sonar-server/src/main/java/org/sonar/server/badge/ws/QualityGateAction.java
index 7b969514073..bdf45ad78c6 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/badge/ws/QualityGateAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/badge/ws/QualityGateAction.java
@@ -41,12 +41,14 @@ import static org.apache.commons.io.IOUtils.write;
import static org.sonar.api.web.UserRole.USER;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
import static org.sonarqube.ws.MediaTypes.SVG;
public class QualityGateAction implements ProjectBadgesWsAction {
private static final String PARAM_PROJECT = "project";
private static final String PARAM_BRANCH = "branch";
+ private static final String PARAM_PULL_REQUEST = "pullRequest";
private final UserSession userSession;
private final DbClient dbClient;
@@ -76,6 +78,10 @@ public class QualityGateAction implements ProjectBadgesWsAction {
.createParam(PARAM_BRANCH)
.setDescription("Branch key")
.setExampleValue(KEY_BRANCH_EXAMPLE_001);
+ action
+ .createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001);
}
@Override
@@ -83,8 +89,9 @@ public class QualityGateAction implements ProjectBadgesWsAction {
response.stream().setMediaType(SVG);
String projectKey = request.mandatoryParam(PARAM_PROJECT);
String branch = request.param(PARAM_BRANCH);
+ String pullRequest = request.param(PARAM_PULL_REQUEST);
try (DbSession dbSession = dbClient.openSession(false)) {
- ComponentDto project = componentFinder.getByKeyAndOptionalBranch(dbSession, projectKey, branch);
+ ComponentDto project = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, projectKey, branch, pullRequest);
userSession.checkComponentPermission(USER, project);
Level qualityGateStatus = getQualityGate(dbSession, project);
write(svgGenerator.generateQualityGate(qualityGateStatus), response.stream().output(), UTF_8);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectAction.java b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectAction.java
index 0332472367f..a67d99799e1 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectAction.java
@@ -33,6 +33,7 @@ import org.sonarqube.ws.Batch.WsProjectResponse.FileData.Builder;
import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
public class ProjectAction implements BatchWsAction {
@@ -41,6 +42,7 @@ public class ProjectAction implements BatchWsAction {
private static final String PARAM_PROFILE = "profile";
private static final String PARAM_ISSUES_MODE = "issues_mode";
private static final String PARAM_BRANCH = "branch";
+ private static final String PARAM_PULL_REQUEST = "pullRequest";
private final ProjectDataLoader projectDataLoader;
@@ -79,6 +81,12 @@ public class ProjectAction implements BatchWsAction {
.setSince("6.6")
.setDescription("Branch key")
.setExampleValue(KEY_BRANCH_EXAMPLE_001);
+
+ action
+ .createParam(PARAM_PULL_REQUEST)
+ .setSince("7.1")
+ .setDescription("Pull request id")
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001);
}
@Override
@@ -87,7 +95,8 @@ public class ProjectAction implements BatchWsAction {
.setModuleKey(wsRequest.mandatoryParam(PARAM_KEY))
.setProfileName(wsRequest.param(PARAM_PROFILE))
.setIssuesMode(wsRequest.mandatoryParamAsBoolean(PARAM_ISSUES_MODE))
- .setBranch(wsRequest.param(PARAM_BRANCH)));
+ .setBranch(wsRequest.param(PARAM_BRANCH))
+ .setPullRequest(wsRequest.param(PARAM_PULL_REQUEST)));
WsProjectResponse projectResponse = buildResponse(data);
writeProtobuf(projectResponse, wsRequest, wsResponse);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectDataLoader.java b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectDataLoader.java
index ea244900d6d..cdd7ec30ba1 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectDataLoader.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectDataLoader.java
@@ -71,13 +71,15 @@ public class ProjectDataLoader {
ProjectRepositories data = new ProjectRepositories();
String moduleKey = query.getModuleKey();
String branch = query.getBranch();
+ String pullRequest = query.getPullRequest();
ComponentDto mainModule = componentFinder.getByKey(session, moduleKey);
checkRequest(isProjectOrModule(mainModule), "Key '%s' belongs to a component which is not a Project", moduleKey);
boolean hasScanPerm = userSession.hasComponentPermission(SCAN_EXECUTION, mainModule) ||
userSession.hasPermission(OrganizationPermission.SCAN, mainModule.getOrganizationUuid());
boolean hasBrowsePerm = userSession.hasComponentPermission(USER, mainModule);
checkPermission(query.isIssuesMode(), hasScanPerm, hasBrowsePerm);
- ComponentDto branchOrMainModule = branch == null ? mainModule : componentFinder.getByKeyAndBranch(session, moduleKey, branch);
+ ComponentDto branchOrMainModule = (branch == null && pullRequest == null) ? mainModule
+ : componentFinder.getByKeyAndOptionalBranchOrPullRequest(session, moduleKey, branch, pullRequest);
ComponentDto project = getProject(branchOrMainModule, session);
if (!project.getKey().equals(branchOrMainModule.getKey())) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectDataQuery.java b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectDataQuery.java
index 472be1f73c5..952a7b99b28 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectDataQuery.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/batch/ProjectDataQuery.java
@@ -28,6 +28,7 @@ public class ProjectDataQuery {
private String profileName;
private boolean issuesMode;
private String branch;
+ private String pullRequest;
private ProjectDataQuery() {
// No direct call
@@ -71,6 +72,16 @@ public class ProjectDataQuery {
return this;
}
+ @CheckForNull
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ public ProjectDataQuery setPullRequest(@Nullable String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
public static ProjectDataQuery create() {
return new ProjectDataQuery();
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/DeleteAction.java
new file mode 100644
index 00000000000..644b75218f3
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/DeleteAction.java
@@ -0,0 +1,93 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.branch.pr.ws;
+
+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.server.ws.WebService.NewController;
+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.server.component.ComponentCleanerService;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.user.UserSession;
+
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
+import static org.sonar.server.branch.pr.ws.PullRequestsWs.addProjectParam;
+import static org.sonar.server.branch.pr.ws.PullRequestsWs.addPullRequestParam;
+import static org.sonar.server.branch.pr.ws.PullRequestsWsParameters.PARAM_PROJECT;
+import static org.sonar.server.branch.pr.ws.PullRequestsWsParameters.PARAM_PULL_REQUEST;
+import static org.sonar.server.branch.ws.ProjectBranchesParameters.ACTION_DELETE;
+
+public class DeleteAction implements PullRequestWsAction {
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final ComponentCleanerService componentCleanerService;
+ private final ComponentFinder componentFinder;
+
+ public DeleteAction(DbClient dbClient, ComponentFinder componentFinder, UserSession userSession, ComponentCleanerService componentCleanerService) {
+ this.dbClient = dbClient;
+ this.componentFinder = componentFinder;
+ this.userSession = userSession;
+ this.componentCleanerService = componentCleanerService;
+ }
+
+ @Override
+ public void define(NewController context) {
+ WebService.NewAction action = context.createAction(ACTION_DELETE)
+ .setSince("7.1")
+ .setDescription("Delete a pull request.<br/>" +
+ "Requires 'Administer' rights on the specified project.")
+ .setPost(true)
+ .setHandler(this);
+
+ addProjectParam(action);
+ addPullRequestParam(action);
+ }
+
+ @Override
+ public void handle(Request request, Response response) throws Exception {
+ userSession.checkLoggedIn();
+ String projectKey = request.mandatoryParam(PARAM_PROJECT);
+ String pullRequestId = request.mandatoryParam(PARAM_PULL_REQUEST);
+
+ try (DbSession dbSession = dbClient.openSession(false)) {
+ ComponentDto project = componentFinder.getRootComponentByUuidOrKey(dbSession, null, projectKey);
+ checkPermission(project);
+
+ BranchDto pullRequest = dbClient.branchDao().selectByPullRequestKey(dbSession, project.uuid(), pullRequestId)
+ .filter(branch -> branch.getBranchType() == PULL_REQUEST)
+ .orElseThrow(() -> new NotFoundException(String.format("Pull request '%s' is not found for project '%s'", pullRequestId, projectKey)));
+
+ ComponentDto branchComponent = componentFinder.getByKeyAndPullRequest(dbSession, projectKey, pullRequest.getKey());
+ componentCleanerService.deleteBranch(dbSession, branchComponent);
+ response.noContent();
+ }
+ }
+
+ private void checkPermission(ComponentDto project) {
+ userSession.checkComponentPermission(UserRole.ADMIN, project);
+ }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/ListAction.java b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/ListAction.java
new file mode 100644
index 00000000000..c6d63a10cfe
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/ListAction.java
@@ -0,0 +1,142 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.branch.pr.ws;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+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.component.SnapshotDto;
+import org.sonar.db.protobuf.DbProjectBranches;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.issue.index.BranchStatistics;
+import org.sonar.server.issue.index.IssueIndex;
+import org.sonar.server.user.UserSession;
+import org.sonarqube.ws.ProjectPullRequests;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+import static org.sonar.api.resources.Qualifiers.PROJECT;
+import static org.sonar.api.utils.DateUtils.formatDateTime;
+import static org.sonar.core.util.Protobuf.setNullable;
+import static org.sonar.core.util.stream.MoreCollectors.toList;
+import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
+import static org.sonar.server.branch.pr.ws.PullRequestsWs.addProjectParam;
+import static org.sonar.server.branch.pr.ws.PullRequestsWsParameters.PARAM_PROJECT;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
+
+public class ListAction implements PullRequestWsAction {
+
+ private final DbClient dbClient;
+ private final UserSession userSession;
+ private final ComponentFinder componentFinder;
+ private final IssueIndex issueIndex;
+
+ public ListAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder, IssueIndex issueIndex) {
+ this.dbClient = dbClient;
+ this.userSession = userSession;
+ this.componentFinder = componentFinder;
+ this.issueIndex = issueIndex;
+ }
+
+ @Override
+ public void define(WebService.NewController context) {
+ WebService.NewAction action = context.createAction("list")
+ .setSince("7.1")
+ .setDescription("List the pull requests of a project.<br/>" +
+ "Requires 'Administer' rights on the specified project.")
+ .setResponseExample(getClass().getResource("list-example.json"))
+ .setHandler(this);
+
+ addProjectParam(action);
+ }
+
+ @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.USER, project);
+ checkArgument(project.isEnabled() && PROJECT.equals(project.qualifier()), "Invalid project key");
+
+ List<BranchDto> pullRequests = dbClient.branchDao().selectByComponent(dbSession, project).stream()
+ .filter(b -> b.getBranchType() == PULL_REQUEST)
+ .collect(toList());
+ List<String> pullRequestUuids = pullRequests.stream().map(BranchDto::getUuid).collect(toList());
+
+ Map<String, BranchDto> mergeBranchesByUuid = dbClient.branchDao()
+ .selectByUuids(dbSession, pullRequests.stream().map(BranchDto::getMergeBranchUuid).filter(Objects::nonNull).collect(toList()))
+ .stream().collect(uniqueIndex(BranchDto::getUuid));
+ Map<String, BranchStatistics> branchStatisticsByBranchUuid = issueIndex.searchBranchStatistics(project.uuid(), pullRequestUuids).stream()
+ .collect(uniqueIndex(BranchStatistics::getBranchUuid, Function.identity()));
+ Map<String, String> analysisDateByBranchUuid = dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, pullRequestUuids).stream()
+ .collect(uniqueIndex(SnapshotDto::getComponentUuid, s -> formatDateTime(s.getCreatedAt())));
+
+ ProjectPullRequests.ListWsResponse.Builder protobufResponse = ProjectPullRequests.ListWsResponse.newBuilder();
+ pullRequests
+ .forEach(b -> addPullRequest(protobufResponse, b, mergeBranchesByUuid, branchStatisticsByBranchUuid.get(b.getUuid()),
+ analysisDateByBranchUuid.get(b.getUuid())));
+ writeProtobuf(protobufResponse.build(), request, response);
+ }
+ }
+
+ private static void addPullRequest(ProjectPullRequests.ListWsResponse.Builder response, BranchDto branch, Map<String, BranchDto> mergeBranchesByUuid,
+ BranchStatistics branchStatistics, @Nullable String analysisDate) {
+ Optional<BranchDto> mergeBranch = Optional.ofNullable(mergeBranchesByUuid.get(branch.getMergeBranchUuid()));
+
+ ProjectPullRequests.PullRequest.Builder builder = ProjectPullRequests.PullRequest.newBuilder();
+ builder.setKey(branch.getKey());
+
+ DbProjectBranches.PullRequestData pullRequestData = requireNonNull(branch.getPullRequestData(), "Pull request data should be available for branch type PULL_REQUEST");
+ builder.setBranch(pullRequestData.getBranch());
+ builder.setUrl(pullRequestData.getUrl());
+ builder.setTitle(pullRequestData.getTitle());
+
+ if (mergeBranch.isPresent()) {
+ String mergeBranchKey = mergeBranch.get().getKey();
+ builder.setBase(mergeBranchKey);
+ } else {
+ builder.setIsOrphan(true);
+ }
+ setNullable(analysisDate, builder::setAnalysisDate);
+ setBranchStatus(builder, branchStatistics);
+ response.addPullRequests(builder);
+ }
+
+ private static void setBranchStatus(ProjectPullRequests.PullRequest.Builder builder, @Nullable BranchStatistics branchStatistics) {
+ ProjectPullRequests.Status.Builder statusBuilder = ProjectPullRequests.Status.newBuilder();
+ statusBuilder.setBugs(branchStatistics == null ? 0L : branchStatistics.getBugs());
+ statusBuilder.setVulnerabilities(branchStatistics == null ? 0L : branchStatistics.getVulnerabilities());
+ statusBuilder.setCodeSmells(branchStatistics == null ? 0L : branchStatistics.getCodeSmells());
+ builder.setStatus(statusBuilder);
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestWsAction.java b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestWsAction.java
new file mode 100644
index 00000000000..2543d5d2d53
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestWsAction.java
@@ -0,0 +1,26 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.branch.pr.ws;
+
+import org.sonar.server.ws.WsAction;
+
+public interface PullRequestWsAction extends WsAction {
+ // marker interface
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestWsModule.java b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestWsModule.java
new file mode 100644
index 00000000000..35cc5226da4
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestWsModule.java
@@ -0,0 +1,32 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.branch.pr.ws;
+
+import org.sonar.core.platform.Module;
+
+public class PullRequestWsModule extends Module {
+ @Override
+ protected void configureModule() {
+ add(
+ ListAction.class,
+ DeleteAction.class,
+ PullRequestsWs.class);
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestsWs.java b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestsWs.java
new file mode 100644
index 00000000000..0e63e78ff22
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestsWs.java
@@ -0,0 +1,61 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.branch.pr.ws;
+
+import org.sonar.api.server.ws.WebService;
+
+import static java.util.Arrays.stream;
+import static org.sonar.server.branch.pr.ws.PullRequestsWsParameters.PARAM_PROJECT;
+import static org.sonar.server.branch.pr.ws.PullRequestsWsParameters.PARAM_PULL_REQUEST;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+
+public class PullRequestsWs implements WebService {
+ private final PullRequestWsAction[] actions;
+
+ public PullRequestsWs(PullRequestWsAction... actions) {
+ this.actions = actions;
+ }
+
+ @Override
+ public void define(Context context) {
+ NewController controller = context.createController("api/project_pull_requests")
+ .setSince("7.1")
+ .setDescription("Manage pull request (only available when the Branch plugin is installed)");
+ stream(actions).forEach(action -> action.define(controller));
+ controller.done();
+ }
+
+ static void addProjectParam(NewAction action) {
+ action
+ .createParam(PARAM_PROJECT)
+ .setDescription("Project key")
+ .setExampleValue(KEY_PROJECT_EXAMPLE_001)
+ .setRequired(true);
+ }
+
+ static void addPullRequestParam(NewAction action) {
+ action
+ .createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setExampleValue("1543")
+ .setRequired(true);
+ }
+
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestsWsParameters.java b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestsWsParameters.java
new file mode 100644
index 00000000000..809aef3616e
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/PullRequestsWsParameters.java
@@ -0,0 +1,31 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.branch.pr.ws;
+
+public class PullRequestsWsParameters {
+
+ public static final String PARAM_PROJECT = "project";
+ public static final String PARAM_COMPONENT = "component";
+ public static final String PARAM_PULL_REQUEST = "pullRequest";
+
+ private PullRequestsWsParameters() {
+ // static utility class
+ }
+}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/package-info.java b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/package-info.java
new file mode 100644
index 00000000000..e96a6dab0f8
--- /dev/null
+++ b/server/sonar-server/src/main/java/org/sonar/server/branch/pr/ws/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.branch.pr.ws;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/branch/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/branch/ws/DeleteAction.java
index c4e34de7503..cde6921b059 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/branch/ws/DeleteAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/branch/ws/DeleteAction.java
@@ -19,7 +19,6 @@
*/
package org.sonar.server.branch.ws;
-import com.google.common.io.Resources;
import org.sonar.api.server.ws.Request;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
@@ -35,10 +34,10 @@ import org.sonar.server.user.UserSession;
import static org.sonar.server.branch.ws.BranchesWs.addBranchParam;
import static org.sonar.server.branch.ws.BranchesWs.addProjectParam;
-import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
import static org.sonar.server.branch.ws.ProjectBranchesParameters.ACTION_DELETE;
import static org.sonar.server.branch.ws.ProjectBranchesParameters.PARAM_BRANCH;
import static org.sonar.server.branch.ws.ProjectBranchesParameters.PARAM_PROJECT;
+import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
public class DeleteAction implements BranchWsAction {
private final DbClient dbClient;
@@ -59,7 +58,6 @@ public class DeleteAction implements BranchWsAction {
.setSince("6.6")
.setDescription("Delete a non-main branch of a project.<br/>" +
"Requires 'Administer' rights on the specified project.")
- .setResponseExample(Resources.getResource(getClass(), "list-example.json"))
.setPost(true)
.setHandler(this);
@@ -78,7 +76,7 @@ public class DeleteAction implements BranchWsAction {
checkPermission(project);
BranchDto branch = checkFoundWithOptional(
- dbClient.branchDao().selectByKey(dbSession, project.uuid(), branchKey),
+ dbClient.branchDao().selectByBranchKey(dbSession, project.uuid(), branchKey),
"Branch '%s' not found for project '%s'", branchKey, projectKey);
if (branch.isMain()) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/branch/ws/ListAction.java b/server/sonar-server/src/main/java/org/sonar/server/branch/ws/ListAction.java
index 4583eb92be9..472a2a4175f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/branch/ws/ListAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/branch/ws/ListAction.java
@@ -21,17 +21,18 @@ package org.sonar.server.branch.ws;
import com.google.common.io.Resources;
import java.util.Collection;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;
-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.core.util.Protobuf;
+import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDto;
@@ -99,30 +100,34 @@ public class ListAction implements BranchWsAction {
checkPermission(project);
checkArgument(project.isEnabled() && PROJECT.equals(project.qualifier()), "Invalid project key");
- Collection<BranchDto> branches = dbClient.branchDao().selectByComponent(dbSession, project);
+ Collection<BranchDto> branches = dbClient.branchDao().selectByComponent(dbSession, project).stream()
+ .filter(b -> b.getBranchType() == SHORT || b.getBranchType() == LONG)
+ .collect(MoreCollectors.toList());
+ List<String> branchUuids = branches.stream().map(BranchDto::getUuid).collect(toList());
+
Map<String, BranchDto> mergeBranchesByUuid = dbClient.branchDao()
.selectByUuids(dbSession, branches.stream().map(BranchDto::getMergeBranchUuid).filter(Objects::nonNull).collect(toList()))
.stream().collect(uniqueIndex(BranchDto::getUuid));
Map<String, LiveMeasureDto> qualityGateMeasuresByComponentUuids = dbClient.liveMeasureDao()
- .selectByComponentUuidsAndMetricKeys(dbSession, branches.stream().map(BranchDto::getUuid).collect(toList()), singletonList(ALERT_STATUS_KEY))
- .stream().collect(uniqueIndex(LiveMeasureDto::getComponentUuid));
+ .selectByComponentUuidsAndMetricKeys(dbSession, branchUuids, singletonList(ALERT_STATUS_KEY)).stream()
+ .collect(uniqueIndex(LiveMeasureDto::getComponentUuid));
Map<String, BranchStatistics> branchStatisticsByBranchUuid = issueIndex.searchBranchStatistics(project.uuid(), branches.stream()
.filter(b -> b.getBranchType().equals(SHORT))
- .map(BranchDto::getUuid).collect(toList()))
- .stream().collect(uniqueIndex(BranchStatistics::getBranchUuid, Function.identity()));
+ .map(BranchDto::getUuid).collect(toList())).stream()
+ .collect(uniqueIndex(BranchStatistics::getBranchUuid, Function.identity()));
Map<String, String> analysisDateByBranchUuid = dbClient.snapshotDao()
- .selectLastAnalysesByRootComponentUuids(dbSession, branches.stream().map(BranchDto::getUuid).collect(Collectors.toList()))
- .stream().collect(uniqueIndex(SnapshotDto::getComponentUuid, s -> formatDateTime(s.getCreatedAt())));
+ .selectLastAnalysesByRootComponentUuids(dbSession, branchUuids).stream()
+ .collect(uniqueIndex(SnapshotDto::getComponentUuid, s -> formatDateTime(s.getCreatedAt())));
ProjectBranches.ListWsResponse.Builder protobufResponse = ProjectBranches.ListWsResponse.newBuilder();
branches.forEach(b -> addBranch(protobufResponse, b, mergeBranchesByUuid, qualityGateMeasuresByComponentUuids.get(b.getUuid()), branchStatisticsByBranchUuid.get(b.getUuid()),
- analysisDateByBranchUuid.get(b.getUuid())));
+ analysisDateByBranchUuid.get(b.getUuid())));
WsUtils.writeProtobuf(protobufResponse.build(), request, response);
}
}
private static void addBranch(ProjectBranches.ListWsResponse.Builder response, BranchDto branch, Map<String, BranchDto> mergeBranchesByUuid,
- @Nullable LiveMeasureDto qualityGateMeasure, BranchStatistics branchStatistics, @Nullable String analysisDate) {
+ @Nullable LiveMeasureDto qualityGateMeasure, BranchStatistics branchStatistics, @Nullable String analysisDate) {
ProjectBranches.Branch.Builder builder = toBranchBuilder(branch, Optional.ofNullable(mergeBranchesByUuid.get(branch.getMergeBranchUuid())));
setBranchStatus(builder, branch, qualityGateMeasure, branchStatistics);
if (analysisDate != null) {
@@ -137,7 +142,7 @@ public class ListAction implements BranchWsAction {
setNullable(branchKey, builder::setName);
builder.setIsMain(branch.isMain());
builder.setType(Common.BranchType.valueOf(branch.getBranchType().name()));
- if (branch.getBranchType().equals(SHORT)) {
+ if (branch.getBranchType() == SHORT) {
if (mergeBranch.isPresent()) {
String mergeBranchKey = mergeBranch.get().getKey();
builder.setMergeBranch(mergeBranchKey);
@@ -149,8 +154,8 @@ public class ListAction implements BranchWsAction {
}
private static void setBranchStatus(ProjectBranches.Branch.Builder builder, BranchDto branch, @Nullable LiveMeasureDto qualityGateMeasure,
- @Nullable BranchStatistics branchStatistics) {
- ProjectBranches.Branch.Status.Builder statusBuilder = ProjectBranches.Branch.Status.newBuilder();
+ @Nullable BranchStatistics branchStatistics) {
+ ProjectBranches.Status.Builder statusBuilder = ProjectBranches.Status.newBuilder();
if (branch.getBranchType() == LONG && qualityGateMeasure != null) {
Protobuf.setNullable(qualityGateMeasure.getDataAsString(), statusBuilder::setQualityGateStatus);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/branch/ws/RenameAction.java b/server/sonar-server/src/main/java/org/sonar/server/branch/ws/RenameAction.java
index fa433fbec57..b2efad6dacf 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/branch/ws/RenameAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/branch/ws/RenameAction.java
@@ -77,7 +77,7 @@ public class RenameAction implements BranchWsAction {
ComponentDto project = componentFinder.getRootComponentByUuidOrKey(dbSession, null, projectKey);
checkPermission(project);
- Optional<BranchDto> existingBranch = dbClient.branchDao().selectByKey(dbSession, project.uuid(), newBranchName);
+ Optional<BranchDto> existingBranch = dbClient.branchDao().selectByBranchKey(dbSession, project.uuid(), newBranchName);
checkArgument(!existingBranch.filter(b -> !b.isMain()).isPresent(),
"Impossible to update branch name: a branch with name \"%s\" already exists in the project.", newBranchName);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java
index 0d8bf400b83..dcbe6290f97 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/ActivityAction.java
@@ -100,7 +100,8 @@ public class ActivityAction implements CeWsAction {
.setChangelog(
new Change("5.5", "it's no more possible to specify the page parameter."),
new Change("6.1", "field \"logs\" is deprecated and its value is always false"),
- new Change("6.6", "fields \"branch\" and \"branchType\" added"))
+ new Change("6.6", "fields \"branch\" and \"branchType\" added"),
+ new Change("7.1", "fields \"pullRequest\" and \"pullRequestTitle\" added"))
.setSince("5.2");
action.createParam(PARAM_COMPONENT_ID)
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/TaskFormatter.java b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/TaskFormatter.java
index fe4a1e3bc68..23f1ba3e091 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ce/ws/TaskFormatter.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ce/ws/TaskFormatter.java
@@ -89,7 +89,7 @@ public class TaskFormatter {
builder.setSubmittedAt(formatDateTime(new Date(dto.getCreatedAt())));
setNullable(dto.getStartedAt(), builder::setStartedAt, DateUtils::formatDateTime);
setNullable(computeExecutionTimeMs(dto), builder::setExecutionTimeMs);
- setBranch(builder, dto.getUuid(), componentDtoCache);
+ setBranchOrPullRequest(builder, dto.getUuid(), componentDtoCache);
return builder.build();
}
@@ -115,10 +115,8 @@ public class TaskFormatter {
builder.setLogs(false);
setNullable(dto.getComponentUuid(), uuid -> setComponent(builder, uuid, componentDtoCache).setComponentId(uuid));
String analysisUuid = dto.getAnalysisUuid();
- if (analysisUuid != null) {
- builder.setAnalysisId(analysisUuid);
- }
- setBranch(builder, dto.getUuid(), componentDtoCache);
+ setNullable(analysisUuid, builder::setAnalysisId);
+ setBranchOrPullRequest(builder, dto.getUuid(), componentDtoCache);
setNullable(analysisUuid, builder::setAnalysisId);
setNullable(dto.getSubmitterLogin(), builder::setSubmitterLogin);
builder.setSubmittedAt(formatDateTime(new Date(dto.getSubmittedAt())));
@@ -144,13 +142,22 @@ public class TaskFormatter {
return builder;
}
- private static Ce.Task.Builder setBranch(Ce.Task.Builder builder, String taskUuid, DtoCache componentDtoCache) {
- componentDtoCache.getBranchName(taskUuid).ifPresent(
+ private static Ce.Task.Builder setBranchOrPullRequest(Ce.Task.Builder builder, String taskUuid, DtoCache componentDtoCache) {
+ componentDtoCache.getBranchKey(taskUuid).ifPresent(
b -> {
- builder.setBranch(b);
- builder.setBranchType(componentDtoCache.getBranchType(taskUuid)
- .orElseThrow(() -> new IllegalStateException(format("Could not find branch type of task '%s'", taskUuid))));
+ Common.BranchType branchType = componentDtoCache.getBranchType(taskUuid)
+ .orElseThrow(() -> new IllegalStateException(format("Could not find branch type of task '%s'", taskUuid)));
+ switch (branchType) {
+ case LONG:
+ case SHORT:
+ builder.setBranchType(branchType);
+ builder.setBranch(b);
+ break;
+ default:
+ throw new IllegalStateException(String.format("Unknown branch type '%s'", branchType));
+ }
});
+ componentDtoCache.getPullRequest(taskUuid).ifPresent(builder::setPullRequest);
return builder;
}
@@ -237,7 +244,7 @@ public class TaskFormatter {
return organizationDto.getKey();
}
- Optional<String> getBranchName(String taskUuid) {
+ Optional<String> getBranchKey(String taskUuid) {
return characteristicsByTaskUuid.get(taskUuid).stream()
.filter(c -> c.getKey().equals(CeTaskCharacteristicDto.BRANCH_KEY))
.map(CeTaskCharacteristicDto::getValue)
@@ -250,6 +257,13 @@ public class TaskFormatter {
.map(c -> Common.BranchType.valueOf(c.getValue()))
.findAny();
}
+
+ Optional<String> getPullRequest(String taskUuid) {
+ return characteristicsByTaskUuid.get(taskUuid).stream()
+ .filter(c -> c.getKey().equals(CeTaskCharacteristicDto.PULL_REQUEST))
+ .map(CeTaskCharacteristicDto::getValue)
+ .findAny();
+ }
}
/**
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java
index 5a2568bdd7c..a1e6d94c628 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentFinder.java
@@ -154,8 +154,24 @@ public class ComponentFinder {
throw new NotFoundException(format("Component '%s' on branch '%s' not found", key, branch));
}
- public ComponentDto getByKeyAndOptionalBranch(DbSession dbSession, String key, @Nullable String branch) {
- return branch == null ? getByKey(dbSession, key) : getByKeyAndBranch(dbSession, key, branch);
+ public ComponentDto getByKeyAndPullRequest(DbSession dbSession, String key, String pullRequest) {
+ java.util.Optional<ComponentDto> componentDto = dbClient.componentDao().selectByKeyAndPullRequest(dbSession, key, pullRequest);
+ if (componentDto.isPresent() && componentDto.get().isEnabled()) {
+ return componentDto.get();
+ }
+ throw new NotFoundException(format("Component '%s' of pull request '%s' not found", key, pullRequest));
+ }
+
+ public ComponentDto getByKeyAndOptionalBranchOrPullRequest(DbSession dbSession, String key, @Nullable String branch, @Nullable String pullRequest) {
+ checkArgument(branch == null || pullRequest == null, "Either branch or pull request can be provided, not both");
+ if (branch != null) {
+ return getByKeyAndBranch(dbSession, key, branch);
+ }
+ if (pullRequest != null) {
+ return getByKeyAndPullRequest(dbSession, key, pullRequest);
+ }
+
+ return getByKey(dbSession, key);
}
public enum ParamNames {
@@ -165,7 +181,7 @@ public class ComponentFinder {
UUID_AND_KEY("uuid", "key"),
ID_AND_KEY("id", "key"),
COMPONENT_ID_AND_KEY("componentId", "componentKey"),
- BASE_COMPONENT_ID_AND_KEY("baseComponentId", "baseComponentKey"),
+ BASE_COMPONENT_ID_AND_KEY("baseComponentId", "component"),
DEVELOPER_ID_AND_KEY("developerId", "developerKey"),
COMPONENT_ID_AND_COMPONENT("componentId", "component"),
PROJECT_ID_AND_PROJECT("projectId", "project"),
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java
index 562ffd31f7a..f9ca3b8e556 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/AppAction.java
@@ -57,9 +57,11 @@ import static org.sonar.api.measures.CoreMetrics.VIOLATIONS;
import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_COMPONENT;
+import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH;
+import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
-import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
public class AppAction implements ComponentsWsAction {
@@ -110,6 +112,12 @@ public class AppAction implements ComponentsWsAction {
.setSince("6.6")
.setInternal(true)
.setExampleValue(KEY_BRANCH_EXAMPLE_001);
+
+ action.createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setSince("7.1")
+ .setInternal(true)
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001);
}
@Override
@@ -131,12 +139,14 @@ public class AppAction implements ComponentsWsAction {
private ComponentDto loadComponent(DbSession dbSession, Request request) {
String componentUuid = request.param(PARAM_COMPONENT_ID);
- String branch = request.param("branch");
- checkArgument(componentUuid == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", PARAM_COMPONENT_ID, PARAM_BRANCH);
- if (branch == null) {
+ String branch = request.param(PARAM_BRANCH);
+ String pullRequest = request.param(PARAM_PULL_REQUEST);
+ checkArgument(componentUuid == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'", PARAM_COMPONENT_ID,
+ PARAM_BRANCH, PARAM_PULL_REQUEST);
+ if (branch == null && pullRequest == null) {
return componentFinder.getByUuidOrKey(dbSession, componentUuid, request.param(PARAM_COMPONENT), COMPONENT_ID_AND_COMPONENT);
}
- return componentFinder.getByKeyAndOptionalBranch(dbSession, request.mandatoryParam(PARAM_COMPONENT), branch);
+ return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, request.mandatoryParam(PARAM_COMPONENT), branch, pullRequest);
}
private void appendComponent(JsonWriter json, ComponentDto component, UserSession userSession, DbSession session) {
@@ -168,6 +178,10 @@ public class AppAction implements ComponentsWsAction {
if (branch != null) {
json.prop("branch", branch);
}
+ String pullRequest = project.getPullRequest();
+ if (pullRequest != null) {
+ json.prop("pullRequest", pullRequest);
+ }
json.prop("fav", isFavourite);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java
index b03f5d26d1d..5adde10f4f4 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ComponentDtoToWsComponent.java
@@ -62,6 +62,7 @@ class ComponentDtoToWsComponent {
.setName(dto.name())
.setQualifier(dto.qualifier());
setNullable(emptyToNull(dto.getBranch()), wsComponent::setBranch);
+ setNullable(emptyToNull(dto.getPullRequest()), wsComponent::setPullRequest);
setNullable(emptyToNull(dto.path()), wsComponent::setPath);
setNullable(emptyToNull(dto.description()), wsComponent::setDescription);
setNullable(emptyToNull(dto.language()), wsComponent::setLanguage);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/MeasuresWsParameters.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/MeasuresWsParameters.java
index d101548ab74..19de6f14947 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/MeasuresWsParameters.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/MeasuresWsParameters.java
@@ -35,6 +35,7 @@ public class MeasuresWsParameters {
public static final String DEPRECATED_PARAM_BASE_COMPONENT_KEY = "baseComponentKey";
public static final String PARAM_COMPONENT = "component";
public static final String PARAM_BRANCH = "branch";
+ public static final String PARAM_PULL_REQUEST = "pullRequest";
public static final String PARAM_STRATEGY = "strategy";
public static final String PARAM_QUALIFIERS = "qualifiers";
public static final String PARAM_METRICS = "metrics";
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java
index 92631450f7f..6da99f6b353 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/ShowAction.java
@@ -22,6 +22,8 @@ package org.sonar.server.component.ws;
import java.util.List;
import java.util.Optional;
import java.util.stream.IntStream;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import org.sonar.api.server.ws.Change;
import org.sonar.api.server.ws.Response;
import org.sonar.api.server.ws.WebService;
@@ -35,21 +37,21 @@ import org.sonar.server.component.ComponentFinder;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.Components.ShowWsResponse;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-
import static com.google.common.base.Preconditions.checkArgument;
import static java.lang.String.format;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_COMPONENT;
import static org.sonar.server.component.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
+import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH;
+import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
+import static org.sonar.server.ws.WsUtils.checkRequest;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_SHOW;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT_ID;
-import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH;
public class ShowAction implements ComponentsWsAction {
private final UserSession userSession;
@@ -97,6 +99,12 @@ public class ShowAction implements ComponentsWsAction {
.setExampleValue(KEY_BRANCH_EXAMPLE_001)
.setInternal(true)
.setSince("6.6");
+
+ action.createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001)
+ .setInternal(true)
+ .setSince("7.1");
}
@Override
@@ -110,6 +118,7 @@ public class ShowAction implements ComponentsWsAction {
private ShowWsResponse doHandle(Request request) {
try (DbSession dbSession = dbClient.openSession(false)) {
ComponentDto component = loadComponent(dbSession, request);
+ userSession.checkComponentPermission(UserRole.USER, component);
Optional<SnapshotDto> lastAnalysis = dbClient.snapshotDao().selectLastAnalysisByComponentUuid(dbSession, component.projectUuid());
List<ComponentDto> ancestors = dbClient.componentDao().selectAncestors(dbSession, component);
OrganizationDto organizationDto = componentFinder.getOrganization(dbSession, component);
@@ -121,12 +130,14 @@ public class ShowAction implements ComponentsWsAction {
String componentId = request.getId();
String componentKey = request.getKey();
String branch = request.getBranch();
- checkArgument(componentId == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", PARAM_COMPONENT_ID, PARAM_BRANCH);
- ComponentDto component = branch == null
- ? componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_COMPONENT)
- : componentFinder.getByKeyAndBranch(dbSession, componentKey, branch);
- userSession.checkComponentPermission(UserRole.USER, component);
- return component;
+ String pullRequest = request.getPullRequest();
+ checkArgument(componentId == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'", PARAM_COMPONENT_ID,
+ PARAM_BRANCH, PARAM_PULL_REQUEST);
+ if (branch == null && pullRequest == null) {
+ return componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_COMPONENT);
+ }
+ checkRequest(componentKey!=null, "The '%s' parameter is missing", PARAM_COMPONENT);
+ return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, branch, pullRequest);
}
private static ShowWsResponse buildResponse(ComponentDto component, OrganizationDto organizationDto, List<ComponentDto> orderedAncestors, Optional<SnapshotDto> lastAnalysis) {
@@ -144,13 +155,15 @@ public class ShowAction implements ComponentsWsAction {
return new Request()
.setId(request.param(PARAM_COMPONENT_ID))
.setKey(request.param(PARAM_COMPONENT))
- .setBranch(request.param(PARAM_BRANCH));
+ .setBranch(request.param(PARAM_BRANCH))
+ .setPullRequest(request.param(PARAM_PULL_REQUEST));
}
private static class Request {
private String id;
private String key;
private String branch;
+ private String pullRequest;
@CheckForNull
public String getId() {
@@ -181,5 +194,15 @@ public class ShowAction implements ComponentsWsAction {
this.branch = branch;
return this;
}
+
+ @CheckForNull
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ public Request setPullRequest(@Nullable String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java b/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java
index 5187b0cc797..ef86ba3c9c7 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/component/ws/TreeAction.java
@@ -66,15 +66,18 @@ import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID
import static org.sonar.server.component.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
-import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
+import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter;
+import static org.sonar.server.ws.WsUtils.checkRequest;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.ACTION_TREE;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT_ID;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PULL_REQUEST;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_QUALIFIERS;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_STRATEGY;
-import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH;
public class TreeAction implements ComponentsWsAction {
@@ -138,6 +141,12 @@ public class TreeAction implements ComponentsWsAction {
.setInternal(true)
.setSince("6.6");
+ action.createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001)
+ .setInternal(true)
+ .setSince("7.1");
+
action.createSortParams(SORTS, NAME_SORT, true)
.setDescription("Comma-separated list of sort fields")
.setExampleValue(NAME_SORT + ", " + PATH_SORT);
@@ -190,12 +199,16 @@ public class TreeAction implements ComponentsWsAction {
private ComponentDto loadComponent(DbSession dbSession, Request request) {
String componentId = request.getBaseComponentId();
- String componentKey = request.getBaseComponentKey();
+ String componentKey = request.getComponent();
String branch = request.getBranch();
- checkArgument(componentId == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", PARAM_COMPONENT_ID, PARAM_BRANCH);
- return branch == null
- ? componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_COMPONENT)
- : componentFinder.getByKeyAndBranch(dbSession, componentKey, branch);
+ String pullRequest = request.getPullRequest();
+ checkArgument(componentId == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'", PARAM_COMPONENT_ID,
+ PARAM_BRANCH, PARAM_PULL_REQUEST);
+ if (branch == null && pullRequest == null) {
+ return componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_COMPONENT);
+ }
+ checkRequest(componentKey != null, "The '%s' parameter is missing", PARAM_COMPONENT);
+ return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, branch, pullRequest);
}
private Map<String, ComponentDto> searchReferenceComponentsByUuid(DbSession dbSession, List<ComponentDto> components) {
@@ -285,8 +298,9 @@ public class TreeAction implements ComponentsWsAction {
private static Request toTreeWsRequest(org.sonar.api.server.ws.Request request) {
return new Request()
.setBaseComponentId(request.param(PARAM_COMPONENT_ID))
- .setBaseComponentKey(request.param(PARAM_COMPONENT))
+ .setComponent(request.param(PARAM_COMPONENT))
.setBranch(request.param(PARAM_BRANCH))
+ .setPullRequest(request.param(PARAM_PULL_REQUEST))
.setStrategy(request.mandatoryParam(PARAM_STRATEGY))
.setQuery(request.param(Param.TEXT_QUERY))
.setQualifiers(request.paramAsStrings(PARAM_QUALIFIERS))
@@ -335,9 +349,9 @@ public class TreeAction implements ComponentsWsAction {
private static class Request {
private String baseComponentId;
- private String baseComponentKey;
private String component;
private String branch;
+ private String pullRequest;
private String strategy;
private List<String> qualifiers;
private String query;
@@ -364,24 +378,6 @@ public class TreeAction implements ComponentsWsAction {
return this;
}
- /**
- * @deprecated since 6.4, please use {@link #getComponent()} instead
- */
- @Deprecated
- @CheckForNull
- private String getBaseComponentKey() {
- return baseComponentKey;
- }
-
- /**
- * @deprecated since 6.4, please use {@link #setComponent(String)} instead
- */
- @Deprecated
- private Request setBaseComponentKey(@Nullable String baseComponentKey) {
- this.baseComponentKey = baseComponentKey;
- return this;
- }
-
public Request setComponent(@Nullable String component) {
this.component = component;
return this;
@@ -403,6 +399,16 @@ public class TreeAction implements ComponentsWsAction {
}
@CheckForNull
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ public Request setPullRequest(@Nullable String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ @CheckForNull
private String getStrategy() {
return strategy;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolder.java
index 97159e9875d..77738cc6ffb 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolder.java
@@ -83,6 +83,13 @@ public interface AnalysisMetadataHolder {
boolean isLongLivingBranch();
/**
+ * Convenience method equivalent to do the check using {@link #getBranch()}
+ *
+ * @throws IllegalStateException if branch has not been set
+ */
+ boolean isPullRequest();
+
+ /**
* @throws IllegalStateException if cross project duplication flag has not been set
*/
boolean isCrossProjectDuplicationEnabled();
@@ -93,6 +100,13 @@ public interface AnalysisMetadataHolder {
Branch getBranch();
/**
+ * In a pull request analysis, return the ID of the pull request
+ *
+ * @throws IllegalStateException if current analysis is not a pull request
+ */
+ String getPullRequestId();
+
+ /**
* The project as represented by the main branch. It is used to load settings
* like Quality gates, webhooks and configuration.
*
@@ -119,5 +133,4 @@ public interface AnalysisMetadataHolder {
* Plugins used during the analysis on scanner side
*/
Map<String, ScannerPlugin> getScannerPluginsByKey();
-
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java
index c26f123efdf..6fee4c5f70c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImpl.java
@@ -39,6 +39,7 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder
private final InitializedProperty<Analysis> baseProjectSnapshot = new InitializedProperty<>();
private final InitializedProperty<Boolean> crossProjectDuplicationEnabled = new InitializedProperty<>();
private final InitializedProperty<Branch> branch = new InitializedProperty<>();
+ private final InitializedProperty<String> pullRequestId = new InitializedProperty<>();
private final InitializedProperty<Project> project = new InitializedProperty<>();
private final InitializedProperty<Integer> rootComponentRef = new InitializedProperty<>();
private final InitializedProperty<Map<String, QualityProfile>> qProfilesPerLanguage = new InitializedProperty<>();
@@ -149,6 +150,19 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder
}
@Override
+ public MutableAnalysisMetadataHolder setPullRequestId(String pullRequestId) {
+ checkState(!this.pullRequestId.isInitialized(), "Pull request id has already been set");
+ this.pullRequestId.setProperty(pullRequestId);
+ return this;
+ }
+
+ @Override
+ public String getPullRequestId() {
+ checkState(pullRequestId.isInitialized(), "Pull request id has not been set");
+ return pullRequestId.getProperty();
+ }
+
+ @Override
public MutableAnalysisMetadataHolder setProject(Project project) {
checkState(!this.project.isInitialized(), "Project has already been set");
this.project.setProperty(project);
@@ -201,16 +215,25 @@ public class AnalysisMetadataHolderImpl implements MutableAnalysisMetadataHolder
return pluginsByKey.getProperty();
}
+ @Override
public boolean isShortLivingBranch() {
checkState(this.branch.isInitialized(), BRANCH_NOT_SET);
Branch prop = branch.getProperty();
return prop != null && prop.getType() == BranchType.SHORT;
}
+ @Override
public boolean isLongLivingBranch() {
checkState(this.branch.isInitialized(), BRANCH_NOT_SET);
Branch prop = branch.getProperty();
return prop != null && prop.getType() == BranchType.LONG;
}
+ @Override
+ public boolean isPullRequest() {
+ checkState(this.branch.isInitialized(), BRANCH_NOT_SET);
+ Branch prop = branch.getProperty();
+ return prop != null && prop.getType() == BranchType.PULL_REQUEST;
+ }
+
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Branch.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Branch.java
index f3ebcc87e80..bc7fff0f769 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Branch.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/Branch.java
@@ -53,4 +53,9 @@ public interface Branch extends ComponentKeyGenerator {
* or not.
*/
boolean supportsCrossProjectCpd();
+
+ /**
+ * @throws IllegalStateException if this branch configuration is not a pull request.
+ */
+ String getPullRequestId();
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java
index b4e4e541cfd..09acc83d648 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolder.java
@@ -61,6 +61,11 @@ public interface MutableAnalysisMetadataHolder extends AnalysisMetadataHolder {
MutableAnalysisMetadataHolder setBranch(Branch branch);
/**
+ * @throws IllegalStateException if pull request id has already been set
+ */
+ MutableAnalysisMetadataHolder setPullRequestId(String pullRequestId);
+
+ /**
* @throws IllegalStateException if project has already been set
*/
MutableAnalysisMetadataHolder setProject(Project project);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java
index 8af751c52bc..e7f179b283c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutor.java
@@ -54,6 +54,7 @@ import static java.util.Optional.of;
import static java.util.Optional.ofNullable;
import static org.sonar.api.ce.posttask.CeTask.Status.FAILED;
import static org.sonar.api.ce.posttask.CeTask.Status.SUCCESS;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
/**
* Responsible for calling {@link PostProjectAnalysisTask} implementations (if any).
@@ -180,7 +181,8 @@ public class PostProjectAnalysisTasksExecutor implements ComputationStepExecutor
private BranchImpl createBranch() {
org.sonar.server.computation.task.projectanalysis.analysis.Branch analysisBranch = analysisMetadataHolder.getBranch();
if (!analysisBranch.isLegacyFeature()) {
- return new BranchImpl(analysisBranch.isMain(), analysisBranch.getName(), Branch.Type.valueOf(analysisBranch.getType().name()));
+ String branchKey = analysisBranch.getType() == PULL_REQUEST ? analysisBranch.getPullRequestId() : analysisBranch.getName();
+ return new BranchImpl(analysisBranch.isMain(), branchKey, Branch.Type.valueOf(analysisBranch.getType().name()));
}
return null;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterImpl.java
index 1ed44701a51..a58a8ca987a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterImpl.java
@@ -25,7 +25,9 @@ import org.sonar.api.utils.System2;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.BranchDto;
+import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
+import org.sonar.db.protobuf.DbProjectBranches;
import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.server.computation.task.projectanalysis.analysis.Branch;
@@ -55,12 +57,11 @@ public class BranchPersisterImpl implements BranchPersister {
if (branch.isMain()) {
checkState(branchComponentDtoOpt.isPresent(), "Project has been deleted by end-user during analysis");
branchComponentDto = branchComponentDtoOpt.get();
-
} else {
// inserts new row in table projects if it's the first time branch is analyzed
branchComponentDto = branchComponentDtoOpt.or(() -> insertIntoProjectsTable(dbSession, branchUuid));
-
}
+
// insert or update in table project_branches
dbClient.branchDao().upsert(dbSession, toBranchDto(branchComponentDto, branch));
}
@@ -75,15 +76,29 @@ public class BranchPersisterImpl implements BranchPersister {
return (first != null) ? first : second;
}
- private static BranchDto toBranchDto(ComponentDto componentDto, Branch branch) {
+ private BranchDto toBranchDto(ComponentDto componentDto, Branch branch) {
BranchDto dto = new BranchDto();
dto.setUuid(componentDto.uuid());
+
// MainBranchProjectUuid will be null if it's a main branch
dto.setProjectUuid(firstNonNull(componentDto.getMainBranchProjectUuid(), componentDto.projectUuid()));
- dto.setKey(branch.getName());
dto.setBranchType(branch.getType());
+
// merge branch is only present if it's a short living branch
dto.setMergeBranchUuid(branch.getMergeBranchUuid().orElse(null));
+
+ if (branch.getType() == BranchType.PULL_REQUEST) {
+ dto.setKey(analysisMetadataHolder.getPullRequestId());
+
+ DbProjectBranches.PullRequestData pullRequestData = DbProjectBranches.PullRequestData.newBuilder()
+ .setBranch(branch.getName())
+ .setTitle(branch.getName())
+ .build();
+ dto.setPullRequestData(pullRequestData);
+ } else {
+ dto.setKey(branch.getName());
+ }
+
return dto;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/DefaultBranchImpl.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/DefaultBranchImpl.java
index d3e76444576..63b49e588ef 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/DefaultBranchImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/DefaultBranchImpl.java
@@ -86,6 +86,11 @@ public class DefaultBranchImpl implements Branch {
}
@Override
+ public String getPullRequestId() {
+ throw new IllegalStateException("Only a branch of type PULL_REQUEST can have a pull request id.");
+ }
+
+ @Override
public String generateKey(ScannerReport.Component module, @Nullable ScannerReport.Component fileOrDir) {
String moduleWithBranch = module.getKey();
if (isLegacyBranch) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MergeBranchComponentUuids.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MergeBranchComponentUuids.java
index 5d12477afea..1ec0b9e1d3b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MergeBranchComponentUuids.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/MergeBranchComponentUuids.java
@@ -31,7 +31,7 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.server.computation.task.projectanalysis.analysis.AnalysisMetadataHolder;
import static com.google.common.base.Preconditions.checkState;
-import static org.sonar.db.component.ComponentDto.removeBranchFromKey;
+import static org.sonar.db.component.ComponentDto.removeBranchAndPullRequestFromKey;
/**
* Cache a map between component keys and uuids in the merge branch
@@ -74,7 +74,7 @@ public class MergeBranchComponentUuids {
@CheckForNull
public String getUuid(String dbKey) {
lazyInit();
- String cleanComponentKey = removeBranchFromKey(dbKey);
+ String cleanComponentKey = removeBranchAndPullRequestFromKey(dbKey);
return uuidsByKey.get(cleanComponentKey);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ShortBranchComponentsWithIssues.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ShortBranchComponentsWithIssues.java
index eb799f1ad8f..71d97f257c5 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ShortBranchComponentsWithIssues.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/component/ShortBranchComponentsWithIssues.java
@@ -29,7 +29,7 @@ import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.KeyWithUuidDto;
-import static org.sonar.db.component.ComponentDto.removeBranchFromKey;
+import static org.sonar.db.component.ComponentDto.removeBranchAndPullRequestFromKey;
/**
* Cache a map of component key -> uuid in short branches that have issues with status either RESOLVED or CONFIRMED.
@@ -51,7 +51,7 @@ public class ShortBranchComponentsWithIssues {
try (DbSession dbSession = dbClient.openSession(false)) {
List<KeyWithUuidDto> components = dbClient.componentDao().selectComponentKeysHavingIssuesToMerge(dbSession, uuid);
for (KeyWithUuidDto dto : components) {
- uuidsByKey.computeIfAbsent(removeBranchFromKey(dto.key()), s -> new HashSet<>()).add(dto.uuid());
+ uuidsByKey.computeIfAbsent(removeBranchAndPullRequestFromKey(dto.key()), s -> new HashSet<>()).add(dto.uuid());
}
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegator.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegator.java
index e9403cf007a..c60c9abade4 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegator.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegator.java
@@ -44,7 +44,7 @@ public class IssueTrackingDelegator {
}
public TrackingResult track(Component component) {
- if (analysisMetadataHolder.isShortLivingBranch()) {
+ if (analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) {
return standardResult(shortBranchTracker.track(component));
} else if (isFirstAnalysisSecondaryLongLivingBranch()) {
Tracking<DefaultIssue, DefaultIssue> tracking = mergeBranchTracker.track(component);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java
index a69113a554d..4d86d87b61e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/issue/ShortBranchIssuesLoader.java
@@ -50,7 +50,7 @@ public class ShortBranchIssuesLoader {
}
public Collection<ShortBranchIssue> loadCandidateIssuesForMergingInTargetBranch(Component component) {
- String componentKey = ComponentDto.removeBranchFromKey(component.getKey());
+ String componentKey = ComponentDto.removeBranchAndPullRequestFromKey(component.getKey());
Set<String> uuids = shortBranchComponentsWithIssues.getUuids(componentKey);
if (uuids.isEmpty()) {
return Collections.emptyList();
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStep.java
index 7838e9091c2..89b97b839c0 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStep.java
@@ -67,7 +67,7 @@ public class LoadQualityGateStep implements ComputationStep {
}
private Optional<QualityGate> getShortLivingBranchQualityGate() {
- if (analysisMetadataHolder.isShortLivingBranch()) {
+ if (analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) {
Optional<QualityGate> qualityGate = qualityGateService.findById(ShortLivingBranchQualityGate.ID);
if (qualityGate.isPresent()) {
return qualityGate;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStep.java
index fe9b452b603..d9bbf514fc5 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStep.java
@@ -69,8 +69,8 @@ public class QualityGateEventsStep implements ComputationStep {
@Override
public void execute() {
- // no notification on short living branch as there is no real Quality Gate on those
- if (analysisMetadataHolder.isShortLivingBranch()) {
+ // no notification on short living branch and pull request as there is no real Quality Gate on those
+ if (analysisMetadataHolder.isShortLivingBranch() || analysisMetadataHolder.isPullRequest()) {
return;
}
new DepthTraversalTypeAwareCrawler(
diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStep.java
index 5addea9d4ca..744a49f7291 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStep.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStep.java
@@ -28,6 +28,7 @@ import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
+import javax.annotation.CheckForNull;
import org.sonar.api.issue.Issue;
import org.sonar.api.utils.Duration;
import org.sonar.core.issue.DefaultIssue;
@@ -49,6 +50,7 @@ import org.sonar.server.issue.notification.NewIssuesNotificationFactory;
import org.sonar.server.issue.notification.NewIssuesStatistics;
import org.sonar.server.notification.NotificationService;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.POST_ORDER;
/**
@@ -127,7 +129,7 @@ public class SendIssueNotificationsStep implements ComputationStep {
IssueChangeNotification changeNotification = new IssueChangeNotification();
changeNotification.setRuleName(rules.getByKey(issue.ruleKey()).getName());
changeNotification.setIssue(issue);
- changeNotification.setProject(project.getPublicKey(), project.getName(), getBranchName());
+ changeNotification.setProject(project.getPublicKey(), project.getName(), getBranchName(), getPullRequest());
getComponentKey(issue).ifPresent(c -> changeNotification.setComponent(c.getPublicKey(), c.getName()));
service.deliver(changeNotification);
}
@@ -136,7 +138,7 @@ public class SendIssueNotificationsStep implements ComputationStep {
NewIssuesStatistics.Stats globalStatistics = statistics.globalStatistics();
NewIssuesNotification notification = newIssuesNotificationFactory
.newNewIssuesNotication()
- .setProject(project.getPublicKey(), project.getName(), getBranchName())
+ .setProject(project.getPublicKey(), project.getName(), getBranchName(), getPullRequest())
.setProjectVersion(project.getReportAttributes().getVersion())
.setAnalysisDate(new Date(analysisDate))
.setStatistics(project.getName(), globalStatistics)
@@ -155,7 +157,7 @@ public class SendIssueNotificationsStep implements ComputationStep {
.newMyNewIssuesNotification()
.setAssignee(assignee);
myNewIssuesNotification
- .setProject(project.getPublicKey(), project.getName(), getBranchName())
+ .setProject(project.getPublicKey(), project.getName(), getBranchName(), getPullRequest())
.setProjectVersion(project.getReportAttributes().getVersion())
.setAnalysisDate(new Date(analysisDate))
.setStatistics(project.getName(), assigneeStatistics)
@@ -185,9 +187,16 @@ public class SendIssueNotificationsStep implements ComputationStep {
return "Send issue notifications";
}
+ @CheckForNull
private String getBranchName() {
Branch branch = analysisMetadataHolder.getBranch();
- return branch.isMain() ? null : branch.getName();
+ return branch.isMain() || branch.getType() == PULL_REQUEST ? null : branch.getName();
+ }
+
+ @CheckForNull
+ private String getPullRequest() {
+ Branch branch = analysisMetadataHolder.getBranch();
+ return branch.getType() == PULL_REQUEST ? analysisMetadataHolder.getPullRequestId() : null;
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsParser.java b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsParser.java
index a94371a998e..1b9fb9908fe 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsParser.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/DuplicationsParser.java
@@ -52,7 +52,7 @@ public class DuplicationsParser {
this.componentDao = componentDao;
}
- public List<Block> parse(DbSession session, ComponentDto component, @Nullable String branch, @Nullable String duplicationsData) {
+ public List<Block> parse(DbSession session, ComponentDto component, @Nullable String branch, @Nullable String pullRequest, @Nullable String duplicationsData) {
Map<String, ComponentDto> componentsByKey = newHashMap();
List<Block> blocks = newArrayList();
if (duplicationsData != null) {
@@ -69,7 +69,7 @@ public class DuplicationsParser {
String size = bCursor.getAttrValue("l");
String componentKey = bCursor.getAttrValue("r");
if (from != null && size != null && componentKey != null) {
- duplications.add(createDuplication(componentsByKey, branch, from, size, componentKey, session));
+ duplications.add(createDuplication(componentsByKey, branch, pullRequest, from, size, componentKey, session));
}
}
Collections.sort(duplications, new DuplicationComparator(component.uuid(), component.projectUuid()));
@@ -83,12 +83,19 @@ public class DuplicationsParser {
return blocks;
}
- private Duplication createDuplication(Map<String, ComponentDto> componentsByKey, @Nullable String branch, String from, String size, String componentDbKey, DbSession session) {
+ private Duplication createDuplication(Map<String, ComponentDto> componentsByKey, @Nullable String branch, @Nullable String pullRequest, String from, String size,
+ String componentDbKey, DbSession session) {
String componentKey = convertToKey(componentDbKey);
ComponentDto component = componentsByKey.get(componentKey);
if (component == null) {
- Optional<ComponentDto> componentDtoOptional = branch == null ? componentDao.selectByKey(session, componentKey)
- : Optional.fromNullable(componentDao.selectByKeyAndBranch(session, componentKey, branch).orElseGet(null));
+ Optional<ComponentDto> componentDtoOptional;
+ if (branch != null) {
+ componentDtoOptional = Optional.fromNullable(componentDao.selectByKeyAndBranch(session, componentKey, branch).orElseGet(null));
+ } else if (pullRequest != null) {
+ componentDtoOptional = Optional.fromNullable(componentDao.selectByKeyAndPullRequest(session, componentKey, pullRequest).orElseGet(null));
+ } else {
+ componentDtoOptional = componentDao.selectByKey(session, componentKey);
+ }
component = componentDtoOptional.isPresent() ? componentDtoOptional.get() : null;
componentsByKey.put(componentKey, component);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java
index df342e5d032..1889105b1b6 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowAction.java
@@ -37,11 +37,15 @@ import org.sonar.server.user.UserSession;
import static com.google.common.base.Preconditions.checkArgument;
import static org.sonar.server.component.ComponentFinder.ParamNames.UUID_AND_KEY;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
-import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH;
public class ShowAction implements DuplicationsWsAction {
+ private static final String PARAM_KEY = "key";
+ private static final String PARAM_UUID = "uuid";
+ private static final String PARAM_BRANCH = "branch";
+ private static final String PARAM_PULL_REQUEST = "pullRequest";
private final DbClient dbClient;
private final DuplicationsParser parser;
private final ShowResponseBuilder responseBuilder;
@@ -68,21 +72,28 @@ public class ShowAction implements DuplicationsWsAction {
new Change("6.5", "The fields 'uuid', 'projectUuid', 'subProjectUuid' are deprecated in the response."));
action
- .createParam("key")
+ .createParam(PARAM_KEY)
.setDescription("File key")
.setExampleValue("my_project:/src/foo/Bar.php");
action
- .createParam("uuid")
+ .createParam(PARAM_UUID)
.setDeprecatedSince("6.5")
.setDescription("File ID. If provided, 'key' must not be provided.")
.setExampleValue("584a89f2-8037-4f7b-b82c-8b45d2d63fb2");
action
- .createParam("branch")
+ .createParam(PARAM_BRANCH)
.setDescription("Branch key")
.setInternal(true)
.setExampleValue(KEY_BRANCH_EXAMPLE_001);
+
+ action
+ .createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setInternal(true)
+ .setSince("7.1")
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001);
}
@Override
@@ -92,19 +103,22 @@ public class ShowAction implements DuplicationsWsAction {
userSession.checkComponentPermission(UserRole.CODEVIEWER, component);
String duplications = findDataFromComponent(dbSession, component);
String branch = component.getBranch();
- List<DuplicationsParser.Block> blocks = parser.parse(dbSession, component, branch, duplications);
- writeProtobuf(responseBuilder.build(dbSession, blocks, branch), request, response);
+ String pullRequest = component.getPullRequest();
+ List<DuplicationsParser.Block> blocks = parser.parse(dbSession, component, branch, pullRequest, duplications);
+ writeProtobuf(responseBuilder.build(dbSession, blocks, branch, pullRequest), request, response);
}
}
private ComponentDto loadComponent(DbSession dbSession, Request request) {
- String componentUuid = request.param("uuid");
- String branch = request.param("branch");
- checkArgument(componentUuid == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", "uuid", PARAM_BRANCH);
- if (branch == null) {
- return componentFinder.getByUuidOrKey(dbSession, componentUuid, request.param("key"), UUID_AND_KEY);
+ String componentUuid = request.param(PARAM_UUID);
+ String branch = request.param(PARAM_BRANCH);
+ String pullRequest = request.param(PARAM_PULL_REQUEST);
+ checkArgument(componentUuid == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'", PARAM_UUID,
+ PARAM_BRANCH, PARAM_PULL_REQUEST);
+ if (branch == null && pullRequest == null) {
+ return componentFinder.getByUuidOrKey(dbSession, componentUuid, request.param(PARAM_KEY), UUID_AND_KEY);
}
- return componentFinder.getByKeyAndOptionalBranch(dbSession, request.mandatoryParam("key"), branch);
+ return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, request.mandatoryParam(PARAM_KEY), branch, pullRequest);
}
@CheckForNull
diff --git a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowResponseBuilder.java b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowResponseBuilder.java
index 1c56c039247..f6050bc6527 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowResponseBuilder.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/duplication/ws/ShowResponseBuilder.java
@@ -49,14 +49,14 @@ public class ShowResponseBuilder {
this.componentDao = componentDao;
}
- ShowResponse build(DbSession session, List<DuplicationsParser.Block> blocks, @Nullable String branch) {
+ ShowResponse build(DbSession session, List<DuplicationsParser.Block> blocks, @Nullable String branch, @Nullable String pullRequest) {
ShowResponse.Builder response = ShowResponse.newBuilder();
Map<String, String> refByComponentKey = newHashMap();
blocks.stream()
.map(block -> toWsDuplication(block, refByComponentKey))
.forEach(response::addDuplications);
- writeFiles(session, response, refByComponentKey, branch);
+ writeFiles(session, response, refByComponentKey, branch, pullRequest);
return response.build();
}
@@ -86,7 +86,8 @@ public class ShowResponseBuilder {
return block;
}
- private static Duplications.File toWsFile(ComponentDto file, @Nullable ComponentDto project, @Nullable ComponentDto subProject, @Nullable String branch) {
+ private static Duplications.File toWsFile(ComponentDto file, @Nullable ComponentDto project, @Nullable ComponentDto subProject, @Nullable String branch,
+ @Nullable String pullRequest) {
Duplications.File.Builder wsFile = Duplications.File.newBuilder();
wsFile.setKey(file.getKey());
wsFile.setUuid(file.uuid());
@@ -102,15 +103,14 @@ public class ShowResponseBuilder {
wsFile.setSubProjectUuid(subProject.uuid());
wsFile.setSubProjectName(subProject.longName());
}
- if (branch != null) {
- wsFile.setBranch(branch);
- }
+ setNullable(branch, wsFile::setBranch);
+ setNullable(pullRequest, wsFile::setPullRequest);
return wsFile;
});
return wsFile.build();
}
- private void writeFiles(DbSession session, ShowResponse.Builder response, Map<String, String> refByComponentKey, @Nullable String branch) {
+ private void writeFiles(DbSession session, ShowResponse.Builder response, Map<String, String> refByComponentKey, @Nullable String branch, @Nullable String pullRequest) {
Map<String, ComponentDto> projectsByUuid = newHashMap();
Map<String, ComponentDto> parentModulesByUuid = newHashMap();
Map<String, Duplications.File> filesByRef = response.getMutableFiles();
@@ -124,7 +124,7 @@ public class ShowResponseBuilder {
ComponentDto project = getProject(file.projectUuid(), projectsByUuid, session);
ComponentDto parentModule = getParentProject(file.getRootUuid(), parentModulesByUuid, session);
- filesByRef.put(ref, toWsFile(file, project, parentModule, branch));
+ filesByRef.put(ref, toWsFile(file, project, parentModule, branch, pullRequest));
}
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java
index 18dd1eef55a..2bc84dddb82 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/IssueQueryFactory.java
@@ -198,6 +198,7 @@ public class IssueQueryFactory {
Collection<String> componentRootUuids = request.getComponentRootUuids();
Collection<String> componentRoots = request.getComponentRoots();
String branch = request.getBranch();
+ String pullRequest = request.getPullRequest();
boolean effectiveOnComponentOnly = false;
@@ -208,15 +209,15 @@ public class IssueQueryFactory {
if (componentRootUuids != null) {
allComponents.addAll(getComponentsFromUuids(session, componentRootUuids));
} else if (componentRoots != null) {
- allComponents.addAll(getComponentsFromKeys(session, componentRoots, branch));
+ allComponents.addAll(getComponentsFromKeys(session, componentRoots, branch, pullRequest));
} else if (components != null) {
- allComponents.addAll(getComponentsFromKeys(session, components, branch));
+ allComponents.addAll(getComponentsFromKeys(session, components, branch, pullRequest));
effectiveOnComponentOnly = true;
} else if (componentUuids != null) {
allComponents.addAll(getComponentsFromUuids(session, componentUuids));
effectiveOnComponentOnly = BooleanUtils.isTrue(onComponentOnly);
} else if (componentKeys != null) {
- allComponents.addAll(getComponentsFromKeys(session, componentKeys, branch));
+ allComponents.addAll(getComponentsFromKeys(session, componentKeys, branch, pullRequest));
effectiveOnComponentOnly = BooleanUtils.isTrue(onComponentOnly);
}
@@ -229,13 +230,11 @@ public class IssueQueryFactory {
.count() <= 1;
}
- private void addComponentParameters(IssueQuery.Builder builder, DbSession session, boolean onComponentOnly,
- List<ComponentDto> components, SearchRequest request) {
-
+ private void addComponentParameters(IssueQuery.Builder builder, DbSession session, boolean onComponentOnly, List<ComponentDto> components, SearchRequest request) {
builder.onComponentOnly(onComponentOnly);
if (onComponentOnly) {
builder.componentUuids(components.stream().map(ComponentDto::uuid).collect(toList()));
- setBranch(builder, components.get(0), request.getBranch());
+ setBranch(builder, components.get(0), request.getBranch(), request.getPullRequest());
return;
}
@@ -246,9 +245,9 @@ public class IssueQueryFactory {
if (projectUuids != null) {
builder.projectUuids(projectUuids);
} else if (projectKeys != null) {
- List<ComponentDto> projects = getComponentsFromKeys(session, projectKeys, request.getBranch());
+ List<ComponentDto> projects = getComponentsFromKeys(session, projectKeys, request.getBranch(), request.getPullRequest());
builder.projectUuids(projects.stream().map(IssueQueryFactory::toProjectUuid).collect(toList()));
- setBranch(builder, projects.get(0), request.getBranch());
+ setBranch(builder, projects.get(0), request.getBranch(), request.getPullRequest());
}
builder.moduleUuids(request.getModuleUuids());
builder.directories(request.getDirectories());
@@ -269,7 +268,7 @@ public class IssueQueryFactory {
Set<String> qualifiers = components.stream().map(ComponentDto::qualifier).collect(toHashSet());
checkArgument(qualifiers.size() == 1, "All components must have the same qualifier, found %s", String.join(",", qualifiers));
- setBranch(builder, components.get(0), request.getBranch());
+ setBranch(builder, components.get(0), request.getBranch(), request.getPullRequest());
String qualifier = qualifiers.iterator().next();
switch (qualifier) {
case Qualifiers.VIEW:
@@ -345,10 +344,15 @@ public class IssueQueryFactory {
builder.directories(directoryPaths);
}
- private List<ComponentDto> getComponentsFromKeys(DbSession dbSession, Collection<String> componentKeys, @Nullable String branch) {
- List<ComponentDto> componentDtos = branch == null
- ? dbClient.componentDao().selectByKeys(dbSession, componentKeys)
- : dbClient.componentDao().selectByKeysAndBranch(dbSession, componentKeys, branch);
+ private List<ComponentDto> getComponentsFromKeys(DbSession dbSession, Collection<String> componentKeys, @Nullable String branch, @Nullable String pullRequest) {
+ List<ComponentDto> componentDtos;
+ if (branch != null) {
+ componentDtos = dbClient.componentDao().selectByKeysAndBranch(dbSession, componentKeys, branch);
+ } else if (pullRequest != null) {
+ componentDtos = dbClient.componentDao().selectByKeysAndPullRequest(dbSession, componentKeys, pullRequest);
+ } else {
+ componentDtos = dbClient.componentDao().selectByKeys(dbSession, componentKeys);
+ }
if (!componentKeys.isEmpty() && componentDtos.isEmpty()) {
return singletonList(UNKNOWN_COMPONENT);
}
@@ -376,8 +380,11 @@ public class IssueQueryFactory {
return mainBranchProjectUuid == null ? componentDto.projectUuid() : mainBranchProjectUuid;
}
- private static void setBranch(IssueQuery.Builder builder, ComponentDto component, @Nullable String branch) {
- builder.branchUuid(branch == null ? null : component.projectUuid());
- builder.mainBranch(branch == null || component.equals(UNKNOWN_COMPONENT) || !branch.equals(component.getBranch()));
+ private static void setBranch(IssueQuery.Builder builder, ComponentDto component, @Nullable String branch, @Nullable String pullRequest) {
+ builder.branchUuid(branch == null && pullRequest == null ? null : component.projectUuid());
+ builder.mainBranch(UNKNOWN_COMPONENT.equals(component)
+ || (branch == null && pullRequest == null)
+ || (branch != null && !branch.equals(component.getBranch()))
+ || (pullRequest != null && !pullRequest.equals(component.getPullRequest())));
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/SearchRequest.java b/server/sonar-server/src/main/java/org/sonar/server/issue/SearchRequest.java
index 83a424bcb00..8c9b7d4af11 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/SearchRequest.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/SearchRequest.java
@@ -48,6 +48,7 @@ public class SearchRequest {
private List<String> moduleUuids;
private Boolean onComponentOnly;
private String branch;
+ private String pullRequest;
private String organization;
private Integer page;
private Integer pageSize;
@@ -453,4 +454,14 @@ public class SearchRequest {
this.branch = branch;
return this;
}
+
+ @CheckForNull
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ public SearchRequest setPullRequest(@Nullable String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/AbstractNewIssuesEmailTemplate.java b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/AbstractNewIssuesEmailTemplate.java
index 1746f383dfb..d8bfbe925e9 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/AbstractNewIssuesEmailTemplate.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/AbstractNewIssuesEmailTemplate.java
@@ -54,6 +54,7 @@ public abstract class AbstractNewIssuesEmailTemplate extends EmailTemplate {
static final String FIELD_PROJECT_VERSION = "projectVersion";
static final String FIELD_ASSIGNEE = "assignee";
static final String FIELD_BRANCH = "branch";
+ static final String FIELD_PULL_REQUEST = "pullRequest";
protected final EmailSettings settings;
protected final I18n i18n;
@@ -78,12 +79,16 @@ public abstract class AbstractNewIssuesEmailTemplate extends EmailTemplate {
}
String projectName = checkNotNull(notification.getFieldValue(FIELD_PROJECT_NAME));
String branchName = notification.getFieldValue(FIELD_BRANCH);
+ String pullRequest = notification.getFieldValue(FIELD_PULL_REQUEST);
StringBuilder message = new StringBuilder();
message.append("Project: ").append(projectName).append(NEW_LINE);
if (branchName != null) {
message.append("Branch: ").append(branchName).append(NEW_LINE);
}
+ if (pullRequest!= null) {
+ message.append("Pull request: ").append(pullRequest).append(NEW_LINE);
+ }
String version = notification.getFieldValue(FIELD_PROJECT_VERSION);
if (version != null) {
message.append("Version: ").append(version).append(NEW_LINE);
@@ -203,6 +208,10 @@ public abstract class AbstractNewIssuesEmailTemplate extends EmailTemplate {
if (branchName != null) {
url += "&branch=" + encode(branchName);
}
+ String pullRequest = notification.getFieldValue(FIELD_PULL_REQUEST);
+ if (pullRequest != null) {
+ url += "&pullRequest=" + encode(pullRequest);
+ }
url += "&createdAt=" + encode(DateUtils.formatDateTime(date));
message
.append("More details at: ")
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangeNotification.java b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangeNotification.java
index 517b0e10fb9..d25e8adb45e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangeNotification.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangeNotification.java
@@ -29,6 +29,11 @@ import org.sonar.core.issue.DefaultIssue;
import org.sonar.core.issue.FieldDiffs;
import org.sonar.db.component.ComponentDto;
+import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_BRANCH;
+import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PROJECT_KEY;
+import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PROJECT_NAME;
+import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PULL_REQUEST;
+
public class IssueChangeNotification extends Notification {
public static final String TYPE = "issue-changes";
@@ -54,14 +59,17 @@ public class IssueChangeNotification extends Notification {
}
public IssueChangeNotification setProject(ComponentDto project) {
- return setProject(project.getKey(), project.name(), project.getBranch());
+ return setProject(project.getKey(), project.name(), project.getBranch(), project.getPullRequest());
}
- public IssueChangeNotification setProject(String projectKey, String projectName, @Nullable String branch) {
- setFieldValue("projectName", projectName);
- setFieldValue("projectKey", projectKey);
+ public IssueChangeNotification setProject(String projectKey, String projectName, @Nullable String branch, @Nullable String pullRequest) {
+ setFieldValue(FIELD_PROJECT_NAME, projectName);
+ setFieldValue(FIELD_PROJECT_KEY, projectKey);
if (branch != null) {
- setFieldValue("branch", branch);
+ setFieldValue(FIELD_BRANCH, branch);
+ }
+ if (pullRequest != null) {
+ setFieldValue(FIELD_PULL_REQUEST, pullRequest);
}
return this;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangesEmailTemplate.java b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangesEmailTemplate.java
index b6b9d239ad2..487c7fd4c2c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangesEmailTemplate.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/IssueChangesEmailTemplate.java
@@ -33,6 +33,8 @@ import org.sonar.plugins.emailnotifications.api.EmailMessage;
import org.sonar.plugins.emailnotifications.api.EmailTemplate;
import static java.net.URLEncoder.encode;
+import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_BRANCH;
+import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PULL_REQUEST;
/**
* Creates email message for notification "issue-changes".
@@ -99,10 +101,14 @@ public class IssueChangesEmailTemplate extends EmailTemplate {
private static void appendHeader(Notification notif, StringBuilder sb) {
appendLine(sb, StringUtils.defaultString(notif.getFieldValue("componentName"), notif.getFieldValue("componentKey")));
- String branchName = notif.getFieldValue("branch");
+ String branchName = notif.getFieldValue(FIELD_BRANCH);
if (branchName != null) {
appendField(sb, "Branch", null, branchName);
}
+ String pullRequest = notif.getFieldValue(FIELD_PULL_REQUEST);
+ if (pullRequest != null) {
+ appendField(sb, "Pull request", null, pullRequest);
+ }
appendField(sb, "Rule", null, notif.getFieldValue("ruleName"));
appendField(sb, "Message", null, notif.getFieldValue("message"));
}
@@ -114,10 +120,14 @@ public class IssueChangesEmailTemplate extends EmailTemplate {
.append("/project/issues?id=").append(encode(notification.getFieldValue("projectKey"), "UTF-8"))
.append("&issues=").append(issueKey)
.append("&open=").append(issueKey);
- String branchName = notification.getFieldValue("branch");
+ String branchName = notification.getFieldValue(FIELD_BRANCH);
if (branchName != null) {
sb.append("&branch=").append(branchName);
}
+ String pullRequest = notification.getFieldValue(FIELD_PULL_REQUEST);
+ if (pullRequest != null) {
+ sb.append("&pullRequest=").append(pullRequest);
+ }
sb.append(NEW_LINE);
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException("Encoding not supported", e);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesEmailTemplate.java b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesEmailTemplate.java
index cebeeb6f2ed..ed50922ed7e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesEmailTemplate.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/MyNewIssuesEmailTemplate.java
@@ -65,10 +65,14 @@ public class MyNewIssuesEmailTemplate extends AbstractNewIssuesEmailTemplate {
settings.getServerBaseURL(),
encode(projectKey),
encode(assignee));
- String branchName = notification.getFieldValue("branch");
+ String branchName = notification.getFieldValue(FIELD_BRANCH);
if (branchName != null) {
url += "&branch=" + encode(branchName);
}
+ String pullRequest = notification.getFieldValue(FIELD_PULL_REQUEST);
+ if (pullRequest != null) {
+ url += "&pullRequest=" + encode(pullRequest);
+ }
url += "&createdAt=" + encode(DateUtils.formatDateTime(date));
message
.append("More details at: ")
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java
index e7e819ec86b..df8c6bb943b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/notification/NewIssuesNotification.java
@@ -46,6 +46,7 @@ import org.sonar.server.user.index.UserIndex;
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_BRANCH;
import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PROJECT_VERSION;
+import static org.sonar.server.issue.notification.AbstractNewIssuesEmailTemplate.FIELD_PULL_REQUEST;
import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.FIELD_PROJECT_DATE;
import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.FIELD_PROJECT_KEY;
import static org.sonar.server.issue.notification.NewIssuesEmailTemplate.FIELD_PROJECT_NAME;
@@ -79,12 +80,15 @@ public class NewIssuesNotification extends Notification {
return this;
}
- public NewIssuesNotification setProject(String projectKey, String projectName, @Nullable String branchName) {
+ public NewIssuesNotification setProject(String projectKey, String projectName, @Nullable String branchName, @Nullable String pullRequest) {
setFieldValue(FIELD_PROJECT_NAME, projectName);
setFieldValue(FIELD_PROJECT_KEY, projectKey);
if (branchName != null) {
setFieldValue(FIELD_BRANCH, branchName);
}
+ if (pullRequest != null) {
+ setFieldValue(FIELD_PULL_REQUEST, pullRequest);
+ }
return this;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
index a76a989689e..3bd129ba174 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchAction.java
@@ -72,6 +72,7 @@ import static org.sonar.core.util.stream.MoreCollectors.toSet;
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.ACTION_SEARCH;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.DEPRECATED_FACET_MODE_DEBT;
@@ -106,6 +107,7 @@ import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PLANNED;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECTS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECT_KEYS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECT_UUIDS;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PULL_REQUEST;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_REPORTERS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLUTIONS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_RESOLVED;
@@ -319,6 +321,12 @@ public class SearchAction implements IssuesWsAction {
.setInternal(true)
.setSince("6.6");
+ action.createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001)
+ .setInternal(true)
+ .setSince("7.1");
+
action.createParam(PARAM_ORGANIZATION)
.setDescription("Organization key")
.setRequired(false)
@@ -568,6 +576,7 @@ public class SearchAction implements IssuesWsAction {
.setModuleUuids(request.paramAsStrings(PARAM_MODULE_UUIDS))
.setOnComponentOnly(request.paramAsBoolean(PARAM_ON_COMPONENT_ONLY))
.setBranch(request.param(PARAM_BRANCH))
+ .setPullRequest(request.param(PARAM_PULL_REQUEST))
.setOrganization(request.param(PARAM_ORGANIZATION))
.setPage(request.mandatoryParamAsInt(Param.PAGE))
.setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE))
diff --git a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
index e41a988935c..a59cd7f5eb2 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/issue/ws/SearchResponseFormat.java
@@ -162,6 +162,7 @@ public class SearchResponseFormat {
issueBuilder.setOrganization(data.getOrganizationKey(component.getOrganizationUuid()));
issueBuilder.setComponent(component.getKey());
setNullable(component.getBranch(), issueBuilder::setBranch);
+ setNullable(component.getPullRequest(), issueBuilder::setPullRequest);
ComponentDto project = data.getComponentByUuid(dto.getProjectUuid());
if (project != null) {
issueBuilder.setProject(project.getKey());
@@ -309,6 +310,7 @@ public class SearchResponseFormat {
.setLongName(nullToEmpty(dto.longName()))
.setEnabled(dto.isEnabled());
setNullable(dto.getBranch(), builder::setBranch);
+ setNullable(dto.getPullRequest(), builder::setPullRequest);
String path = dto.path();
// path is not applicable to the components that are not files.
// Value must not be "" in this case.
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/live/LiveQualityGateComputerImpl.java b/server/sonar-server/src/main/java/org/sonar/server/measure/live/LiveQualityGateComputerImpl.java
index b7cf692c7e8..431821f0095 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/measure/live/LiveQualityGateComputerImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/measure/live/LiveQualityGateComputerImpl.java
@@ -63,7 +63,7 @@ public class LiveQualityGateComputerImpl implements LiveQualityGateComputer {
@Override
public QualityGate loadQualityGate(DbSession dbSession, OrganizationDto organization, ComponentDto project, BranchDto branch) {
- if (branch.getBranchType() == BranchType.SHORT) {
+ if (branch.getBranchType() == BranchType.SHORT || branch.getBranchType() == BranchType.PULL_REQUEST) {
return ShortLivingBranchQualityGate.GATE;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java
index 8ede31ea203..f6257345fad 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentAction.java
@@ -58,17 +58,7 @@ import static java.util.Collections.emptyMap;
import static java.util.Collections.singletonList;
import static java.util.Collections.singletonMap;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
-import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_COMPONENT;
-import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
-import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter;
-import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createDeveloperParameters;
-import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter;
-import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric;
-import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods;
-import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
-import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
-import static org.sonar.server.ws.WsUtils.checkRequest;
-import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import static org.sonar.server.component.ComponentFinder.ParamNames.COMPONENT_ID_AND_KEY;
import static org.sonar.server.component.ws.MeasuresWsParameters.ACTION_COMPONENT;
import static org.sonar.server.component.ws.MeasuresWsParameters.ADDITIONAL_METRICS;
import static org.sonar.server.component.ws.MeasuresWsParameters.ADDITIONAL_PERIODS;
@@ -80,6 +70,18 @@ import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_COMPONENT
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_DEVELOPER_ID;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_DEVELOPER_KEY;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_KEYS;
+import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST;
+import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
+import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter;
+import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createDeveloperParameters;
+import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter;
+import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric;
+import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods;
+import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
+import static org.sonar.server.ws.WsUtils.checkRequest;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
public class ComponentAction implements MeasuresWsAction {
private static final Set<String> QUALIFIERS_ELIGIBLE_FOR_BEST_VALUE = ImmutableSortedSet.of(Qualifiers.FILE, Qualifiers.UNIT_TEST_FILE);
@@ -123,6 +125,12 @@ public class ComponentAction implements MeasuresWsAction {
.setInternal(true)
.setSince("6.6");
+ action.createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001)
+ .setInternal(true)
+ .setSince("7.1");
+
createMetricKeysParameter(action);
createAdditionalFieldsParameter(action);
createDeveloperParameters(action);
@@ -156,10 +164,14 @@ public class ComponentAction implements MeasuresWsAction {
String componentKey = request.getComponent();
String componentId = request.getComponentId();
String branch = request.getBranch();
- checkArgument(componentId == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", DEPRECATED_PARAM_COMPONENT_ID, PARAM_BRANCH);
- return branch == null
- ? componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_COMPONENT)
- : componentFinder.getByKeyAndBranch(dbSession, componentKey, branch);
+ String pullRequest = request.getPullRequest();
+ checkArgument(componentId == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'",
+ DEPRECATED_PARAM_COMPONENT_ID, PARAM_BRANCH, PARAM_PULL_REQUEST);
+ if (branch == null && pullRequest == null) {
+ return componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, COMPONENT_ID_AND_KEY);
+ }
+ checkRequest(componentKey != null, "The '%s' parameter is missing", PARAM_COMPONENT);
+ return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, branch, pullRequest);
}
private Optional<ComponentDto> getReferenceComponent(DbSession dbSession, ComponentDto component) {
@@ -251,6 +263,7 @@ public class ComponentAction implements MeasuresWsAction {
.setComponentId(request.param(DEPRECATED_PARAM_COMPONENT_ID))
.setComponent(request.param(PARAM_COMPONENT))
.setBranch(request.param(PARAM_BRANCH))
+ .setPullRequest(request.param(PARAM_PULL_REQUEST))
.setAdditionalFields(request.paramAsStrings(PARAM_ADDITIONAL_FIELDS))
.setMetricKeys(request.mandatoryParamAsStrings(PARAM_METRIC_KEYS));
checkRequest(!componentRequest.getMetricKeys().isEmpty(), "At least one metric key must be provided");
@@ -265,6 +278,7 @@ public class ComponentAction implements MeasuresWsAction {
private String componentId;
private String component;
private String branch;
+ private String pullRequest;
private List<String> metricKeys;
private List<String> additionalFields;
private String developerId;
@@ -308,6 +322,16 @@ public class ComponentAction implements MeasuresWsAction {
return this;
}
+ @CheckForNull
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ public ComponentRequest setPullRequest(@Nullable String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
private List<String> getMetricKeys() {
return metricKeys;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java
index a0e4839f2c6..6129f9af528 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentDtoToWsComponent.java
@@ -59,6 +59,7 @@ class ComponentDtoToWsComponent {
.setName(component.name())
.setQualifier(component.qualifier());
Protobuf.setNullable(component.getBranch(), wsComponent::setBranch);
+ Protobuf.setNullable(component.getPullRequest(), wsComponent::setPullRequest);
Protobuf.setNullable(component.path(), wsComponent::setPath);
Protobuf.setNullable(component.description(), wsComponent::setDescription);
Protobuf.setNullable(component.language(), wsComponent::setLanguage);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java
index d593764c2a9..5ba9c3f9228 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeAction.java
@@ -84,20 +84,6 @@ import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
import static org.sonar.db.component.ComponentTreeQuery.Strategy.CHILDREN;
import static org.sonar.db.component.ComponentTreeQuery.Strategy.LEAVES;
import static org.sonar.server.component.ComponentFinder.ParamNames.BASE_COMPONENT_ID_AND_KEY;
-import static org.sonar.server.component.ComponentFinder.ParamNames.DEVELOPER_ID_AND_KEY;
-import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
-import static org.sonar.server.measure.ws.MeasureDtoToWsMeasure.updateMeasureBuilder;
-import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter;
-import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createDeveloperParameters;
-import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter;
-import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric;
-import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods;
-import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
-import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
-import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter;
-import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
-import static org.sonar.server.ws.WsUtils.checkRequest;
-import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonar.server.component.ws.MeasuresWsParameters.ACTION_COMPONENT_TREE;
import static org.sonar.server.component.ws.MeasuresWsParameters.ADDITIONAL_METRICS;
import static org.sonar.server.component.ws.MeasuresWsParameters.ADDITIONAL_PERIODS;
@@ -112,8 +98,23 @@ import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_KE
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_PERIOD_SORT;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_SORT;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_SORT_FILTER;
+import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_QUALIFIERS;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_STRATEGY;
+import static org.sonar.server.measure.ws.ComponentDtoToWsComponent.componentDtoToWsComponent;
+import static org.sonar.server.measure.ws.MeasureDtoToWsMeasure.updateMeasureBuilder;
+import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createAdditionalFieldsParameter;
+import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createDeveloperParameters;
+import static org.sonar.server.measure.ws.MeasuresWsParametersBuilder.createMetricKeysParameter;
+import static org.sonar.server.measure.ws.MetricDtoToWsMetric.metricDtoToWsMetric;
+import static org.sonar.server.measure.ws.SnapshotDtoToWsPeriods.snapshotToWsPeriods;
+import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
+import static org.sonar.server.ws.WsParameterBuilder.QualifierParameterContext.newQualifierParameterContext;
+import static org.sonar.server.ws.WsParameterBuilder.createQualifiersParameter;
+import static org.sonar.server.ws.WsUtils.checkRequest;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
/**
* <p>Navigate through components based on different strategy with specified measures.
@@ -216,6 +217,12 @@ public class ComponentTreeAction implements MeasuresWsAction {
.setInternal(true)
.setSince("6.6");
+ action.createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001)
+ .setInternal(true)
+ .setSince("7.1");
+
action.createParam(PARAM_METRIC_SORT)
.setDescription(
format("Metric key to sort by. The '%s' parameter must contain the '%s' or '%s' value. It must be part of the '%s' parameter", Param.SORT, METRIC_SORT, METRIC_PERIOD_SORT,
@@ -343,6 +350,7 @@ public class ComponentTreeAction implements MeasuresWsAction {
.setBaseComponentId(request.param(DEPRECATED_PARAM_BASE_COMPONENT_ID))
.setComponent(request.param(PARAM_COMPONENT))
.setBranch(request.param(PARAM_BRANCH))
+ .setPullRequest(request.param(PARAM_PULL_REQUEST))
.setMetricKeys(metricKeys)
.setStrategy(request.mandatoryParam(PARAM_STRATEGY))
.setQualifiers(request.paramAsStrings(PARAM_QUALIFIERS))
@@ -428,13 +436,17 @@ public class ComponentTreeAction implements MeasuresWsAction {
}
private ComponentDto loadComponent(DbSession dbSession, ComponentTreeRequest request) {
- String componentKey = request.getComponent();
String componentId = request.getBaseComponentId();
+ String componentKey = request.getComponent();
String branch = request.getBranch();
- checkArgument(componentId == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", DEPRECATED_PARAM_BASE_COMPONENT_ID, PARAM_BRANCH);
- return branch == null
- ? componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, BASE_COMPONENT_ID_AND_KEY)
- : componentFinder.getByKeyAndBranch(dbSession, componentKey, branch);
+ String pullRequest = request.getPullRequest();
+ checkArgument(componentId == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'",
+ DEPRECATED_PARAM_BASE_COMPONENT_ID, PARAM_BRANCH, PARAM_PULL_REQUEST);
+ if (branch == null && pullRequest == null) {
+ return componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, BASE_COMPONENT_ID_AND_KEY);
+ }
+ checkRequest(componentKey != null, "The '%s' parameter is missing", PARAM_COMPONENT);
+ return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, branch, pullRequest);
}
private Map<String, ComponentDto> searchReferenceComponentsById(DbSession dbSession, List<ComponentDto> components) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java
index 6791e894cdc..31b2525c06e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/ComponentTreeRequest.java
@@ -28,6 +28,7 @@ class ComponentTreeRequest {
private String baseComponentId;
private String component;
private String branch;
+ private String pullRequest;
private String strategy;
private List<String> qualifiers;
private List<String> additionalFields;
@@ -82,6 +83,16 @@ class ComponentTreeRequest {
}
@CheckForNull
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ public ComponentTreeRequest setPullRequest(@Nullable String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ @CheckForNull
public String getStrategy() {
return strategy;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java
index df6766e218f..4f3af4cbbc0 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/measure/ws/SearchHistoryAction.java
@@ -25,6 +25,8 @@ import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;
+import javax.annotation.CheckForNull;
+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;
@@ -46,22 +48,21 @@ import org.sonar.server.user.UserSession;
import org.sonar.server.ws.KeyExamples;
import org.sonarqube.ws.Measures.SearchHistoryResponse;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-
import static java.lang.String.format;
import static org.sonar.api.utils.DateUtils.parseEndingDateOrDateTime;
import static org.sonar.api.utils.DateUtils.parseStartingDateOrDateTime;
import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.db.component.SnapshotDto.STATUS_PROCESSED;
-import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
-import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonar.server.component.ws.MeasuresWsParameters.ACTION_SEARCH_HISTORY;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_COMPONENT;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_FROM;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRICS;
+import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_TO;
+import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
public class SearchHistoryAction implements MeasuresWsAction {
@@ -78,18 +79,6 @@ public class SearchHistoryAction implements MeasuresWsAction {
this.userSession = userSession;
}
- private static SearchHistoryRequest toWsRequest(Request request) {
- return SearchHistoryRequest.builder()
- .setComponent(request.mandatoryParam(PARAM_COMPONENT))
- .setBranch(request.param(PARAM_BRANCH))
- .setMetrics(request.mandatoryParamAsStrings(PARAM_METRICS))
- .setFrom(request.param(PARAM_FROM))
- .setTo(request.param(PARAM_TO))
- .setPage(request.mandatoryParamAsInt(Param.PAGE))
- .setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE))
- .build();
- }
-
@Override
public void define(WebService.NewController context) {
WebService.NewAction action = context.createAction(ACTION_SEARCH_HISTORY)
@@ -112,6 +101,12 @@ public class SearchHistoryAction implements MeasuresWsAction {
.setInternal(true)
.setExampleValue(KEY_BRANCH_EXAMPLE_001);
+ action.createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setSince("7.1")
+ .setInternal(true)
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001);
+
action.createParam(PARAM_METRICS)
.setDescription("Comma-separated list of metric keys")
.setRequired(true)
@@ -141,6 +136,19 @@ public class SearchHistoryAction implements MeasuresWsAction {
writeProtobuf(searchHistoryResponse, request, response);
}
+ private static SearchHistoryRequest toWsRequest(Request request) {
+ return SearchHistoryRequest.builder()
+ .setComponent(request.mandatoryParam(PARAM_COMPONENT))
+ .setBranch(request.param(PARAM_BRANCH))
+ .setPullRequest(request.param(PARAM_PULL_REQUEST))
+ .setMetrics(request.mandatoryParamAsStrings(PARAM_METRICS))
+ .setFrom(request.param(PARAM_FROM))
+ .setTo(request.param(PARAM_TO))
+ .setPage(request.mandatoryParamAsInt(Param.PAGE))
+ .setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE))
+ .build();
+ }
+
private Function<SearchHistoryRequest, SearchHistoryResult> search() {
return request -> {
try (DbSession dbSession = dbClient.openSession(false)) {
@@ -199,15 +207,14 @@ public class SearchHistoryAction implements MeasuresWsAction {
private ComponentDto loadComponent(DbSession dbSession, SearchHistoryRequest request) {
String componentKey = request.getComponent();
String branch = request.getBranch();
- if (branch != null) {
- return componentFinder.getByKeyAndBranch(dbSession, componentKey, branch);
- }
- return componentFinder.getByKey(dbSession, componentKey);
+ String pullRequest = request.getPullRequest();
+ return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, branch, pullRequest);
}
static class SearchHistoryRequest {
private final String component;
private final String branch;
+ private final String pullRequest;
private final List<String> metrics;
private final String from;
private final String to;
@@ -217,6 +224,7 @@ public class SearchHistoryAction implements MeasuresWsAction {
public SearchHistoryRequest(Builder builder) {
this.component = builder.component;
this.branch = builder.branch;
+ this.pullRequest = builder.pullRequest;
this.metrics = builder.metrics;
this.from = builder.from;
this.to = builder.to;
@@ -233,6 +241,11 @@ public class SearchHistoryAction implements MeasuresWsAction {
return branch;
}
+ @CheckForNull
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
public List<String> getMetrics() {
return metrics;
}
@@ -263,6 +276,7 @@ public class SearchHistoryAction implements MeasuresWsAction {
static class Builder {
private String component;
private String branch;
+ private String pullRequest;
private List<String> metrics;
private String from;
private String to;
@@ -283,6 +297,11 @@ public class SearchHistoryAction implements MeasuresWsAction {
return this;
}
+ public Builder setPullRequest(@Nullable String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
public Builder setMetrics(List<String> metrics) {
this.metrics = metrics;
return this;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
index 7de860aff00..21d6a286cb1 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java
@@ -38,6 +38,7 @@ import org.sonar.server.authentication.LogOAuthWarning;
import org.sonar.server.badge.ws.ProjectBadgesWsModule;
import org.sonar.server.batch.BatchWsModule;
import org.sonar.server.branch.BranchFeatureProxyImpl;
+import org.sonar.server.branch.pr.ws.PullRequestWsModule;
import org.sonar.server.branch.ws.BranchWsModule;
import org.sonar.server.ce.ws.CeWsModule;
import org.sonar.server.component.ComponentCleanerService;
@@ -401,6 +402,7 @@ public class PlatformLevel4 extends PlatformLevel {
// components
BranchWsModule.class,
+ PullRequestWsModule.class,
ProjectsWsModule.class,
ProjectsEsModule.class,
ProjectTagsWsModule.class,
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/ProjectAnalysesWsParameters.java b/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/ProjectAnalysesWsParameters.java
index 2eb9f12db75..a2c56bd7fbd 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/ProjectAnalysesWsParameters.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/ProjectAnalysesWsParameters.java
@@ -28,6 +28,7 @@ public class ProjectAnalysesWsParameters {
public static final String PARAM_FROM = "from";
public static final String PARAM_TO = "to";
public static final String PARAM_BRANCH = "branch";
+ public static final String PARAM_PULL_REQUEST = "pullRequest";
private ProjectAnalysesWsParameters() {
// static access only
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchAction.java
index c6ace6ced7f..49105d01b43 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchAction.java
@@ -47,15 +47,17 @@ import static org.sonar.api.utils.DateUtils.parseStartingDateOrDateTime;
import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.db.component.SnapshotQuery.SORT_FIELD.BY_DATE;
import static org.sonar.db.component.SnapshotQuery.SORT_ORDER.DESC;
-import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
-import static org.sonar.server.ws.WsUtils.writeProtobuf;
import static org.sonar.server.projectanalysis.ws.EventCategory.OTHER;
import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_BRANCH;
import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_CATEGORY;
import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_FROM;
import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_PROJECT;
+import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_TO;
import static org.sonar.server.projectanalysis.ws.SearchRequest.DEFAULT_PAGE_SIZE;
+import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
public class SearchAction implements ProjectAnalysesWsAction {
private static final Set<String> ALLOWED_QUALIFIERS = ImmutableSet.of(Qualifiers.PROJECT, Qualifiers.APP, Qualifiers.VIEW);
@@ -92,6 +94,12 @@ public class SearchAction implements ProjectAnalysesWsAction {
.setInternal(true)
.setExampleValue(KEY_BRANCH_EXAMPLE_001);
+ action.createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setSince("7.1")
+ .setInternal(true)
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001);
+
action.createParam(PARAM_CATEGORY)
.setDescription("Event category. Filter analyses that have at least one event of the category specified.")
.setPossibleValues(EnumSet.allOf(EventCategory.class))
@@ -123,6 +131,7 @@ public class SearchAction implements ProjectAnalysesWsAction {
return SearchRequest.builder()
.setProject(request.mandatoryParam(PARAM_PROJECT))
.setBranch(request.param(PARAM_BRANCH))
+ .setPullRequest(request.param(PARAM_PULL_REQUEST))
.setCategory(category == null ? null : EventCategory.valueOf(category))
.setPage(request.mandatoryParamAsInt(Param.PAGE))
.setPageSize(request.mandatoryParamAsInt(Param.PAGE_SIZE))
@@ -170,10 +179,8 @@ public class SearchAction implements ProjectAnalysesWsAction {
private ComponentDto loadComponent(DbSession dbSession, SearchRequest request) {
String project = request.getProject();
String branch = request.getBranch();
- if (branch != null) {
- return componentFinder.getByKeyAndBranch(dbSession, project, branch);
- }
- return componentFinder.getByKey(dbSession, project);
+ String pullRequest = request.getPullRequest();
+ return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, project, branch, pullRequest);
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchRequest.java b/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchRequest.java
index 83c118c5356..be9a5eade9a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchRequest.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/projectanalysis/ws/SearchRequest.java
@@ -31,6 +31,7 @@ class SearchRequest {
private final String project;
private final String branch;
+ private final String pullRequest;
private final EventCategory category;
private final int page;
private final int pageSize;
@@ -39,7 +40,8 @@ class SearchRequest {
private SearchRequest(Builder builder) {
this.project = builder.project;
- this.branch= builder.branch;
+ this.branch = builder.branch;
+ this.pullRequest = builder.pullRequest;
this.category = builder.category;
this.page = builder.page;
this.pageSize = builder.pageSize;
@@ -57,6 +59,11 @@ class SearchRequest {
}
@CheckForNull
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ @CheckForNull
public EventCategory getCategory() {
return category;
}
@@ -86,6 +93,7 @@ class SearchRequest {
public static class Builder {
private String project;
private String branch;
+ private String pullRequest;
private EventCategory category;
private int page = 1;
private int pageSize = DEFAULT_PAGE_SIZE;
@@ -106,6 +114,11 @@ class SearchRequest {
return this;
}
+ public Builder setPullRequest(@Nullable String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
public Builder setCategory(@Nullable EventCategory category) {
this.category = category;
return this;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGate.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGate.java
index b086408aa92..801dd6ffa73 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGate.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/ShortLivingBranchQualityGate.java
@@ -28,7 +28,7 @@ import org.sonar.api.measures.CoreMetrics;
import static org.sonar.db.qualitygate.QualityGateConditionDto.OPERATOR_GREATER_THAN;
/**
- * Offers constants describing the Hardcoded Quality Gate for short living branches.
+ * Offers constants describing the Hardcoded Quality Gate for short living branches and pull requests.
*/
public final class ShortLivingBranchQualityGate {
public static final long ID = -1_963_456_987L;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java
index 88fa20eb41a..7f7ce140c69 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListener.java
@@ -40,6 +40,12 @@ public interface QGChangeEventListener {
}
enum Status {
- OPEN, CONFIRMED, REOPENED, RESOLVED, CLOSED
+ OPEN,
+ CONFIRMED,
+ REOPENED,
+ RESOLVED_FP,
+ RESOLVED_WF,
+ RESOLVED_FIXED
}
+
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java
index b2fe05c836f..d46a318820f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImpl.java
@@ -23,7 +23,9 @@ import com.google.common.collect.Multimap;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
+import java.util.Objects;
import java.util.Set;
+import org.sonar.api.issue.Issue;
import org.sonar.api.rules.RuleType;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
@@ -95,17 +97,47 @@ public class QGChangeEventListenersImpl implements QGChangeEventListeners {
}
}
- private static class ChangedIssueImpl implements ChangedIssue {
+ static class ChangedIssueImpl implements ChangedIssue {
private final String key;
private final QGChangeEventListener.Status status;
private final RuleType type;
private ChangedIssueImpl(DefaultIssue issue) {
this.key = issue.key();
- this.status = QGChangeEventListener.Status.valueOf(issue.getStatus());
+ this.status = statusOf(issue);
this.type = issue.type();
}
+ static QGChangeEventListener.Status statusOf(DefaultIssue issue) {
+ switch (issue.status()) {
+ case Issue.STATUS_OPEN:
+ return QGChangeEventListener.Status.OPEN;
+ case Issue.STATUS_CONFIRMED:
+ return QGChangeEventListener.Status.CONFIRMED;
+ case Issue.STATUS_REOPENED:
+ return QGChangeEventListener.Status.REOPENED;
+ case Issue.STATUS_RESOLVED:
+ return statusOfResolved(issue);
+ default:
+ throw new IllegalStateException("Unexpected status: " + issue.status());
+ }
+ }
+
+ private static QGChangeEventListener.Status statusOfResolved(DefaultIssue issue) {
+ String resolution = issue.resolution();
+ Objects.requireNonNull(resolution, "A resolved issue should have a resolution");
+ switch (resolution) {
+ case Issue.RESOLUTION_FALSE_POSITIVE:
+ return QGChangeEventListener.Status.RESOLVED_FP;
+ case Issue.RESOLUTION_WONT_FIX:
+ return QGChangeEventListener.Status.RESOLVED_WF;
+ case Issue.RESOLUTION_FIXED:
+ return QGChangeEventListener.Status.RESOLVED_FIXED;
+ default:
+ throw new IllegalStateException("Unexpected resolution for a resolved issue: " + resolution);
+ }
+ }
+
@Override
public String getKey() {
return key;
diff --git a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ListDefinitionsAction.java b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ListDefinitionsAction.java
index 5118f8368d9..c7ed814898f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ListDefinitionsAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ListDefinitionsAction.java
@@ -21,6 +21,8 @@ package org.sonar.server.setting.ws;
import java.util.List;
import java.util.Optional;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.config.PropertyFieldDefinition;
@@ -42,6 +44,7 @@ import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.server.setting.ws.SettingsWs.SETTING_ON_BRANCHES;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_BRANCH;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_COMPONENT;
+import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
@@ -81,6 +84,7 @@ public class ListDefinitionsAction implements SettingsWsAction {
.setDescription("Component key")
.setExampleValue(KEY_PROJECT_EXAMPLE_001);
settingsWsSupport.addBranchParam(action);
+ settingsWsSupport.addPullRequestParam(action);
}
@Override
@@ -107,7 +111,8 @@ public class ListDefinitionsAction implements SettingsWsAction {
private static ListDefinitionsRequest toWsRequest(Request request) {
return new ListDefinitionsRequest()
.setComponent(request.param(PARAM_COMPONENT))
- .setBranch(request.param(PARAM_BRANCH));
+ .setBranch(request.param(PARAM_BRANCH))
+ .setPullRequest(request.param(PARAM_PULL_REQUEST));
}
private static Optional<String> getQualifier(Optional<ComponentDto> component) {
@@ -120,7 +125,7 @@ public class ListDefinitionsAction implements SettingsWsAction {
if (componentKey == null) {
return Optional.empty();
}
- ComponentDto component = componentFinder.getByKeyAndOptionalBranch(dbSession, componentKey, request.getBranch());
+ ComponentDto component = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, request.getBranch(), request.getPullRequest());
userSession.checkComponentPermission(USER, component);
return Optional.of(component);
}
@@ -164,23 +169,36 @@ public class ListDefinitionsAction implements SettingsWsAction {
private String branch;
private String component;
+ private String pullRequest;
- public ListDefinitionsRequest setBranch(String branch) {
+ public ListDefinitionsRequest setComponent(@Nullable String component) {
+ this.component = component;
+ return this;
+ }
+
+ @CheckForNull
+ public String getComponent() {
+ return component;
+ }
+
+ public ListDefinitionsRequest setBranch(@Nullable String branch) {
this.branch = branch;
return this;
}
+ @CheckForNull
public String getBranch() {
return branch;
}
- public ListDefinitionsRequest setComponent(String component) {
- this.component = component;
+ public ListDefinitionsRequest setPullRequest(@Nullable String pullRequest) {
+ this.pullRequest = pullRequest;
return this;
}
- public String getComponent() {
- return component;
+ @CheckForNull
+ public String getPullRequest() {
+ return pullRequest;
}
}
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ResetAction.java b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ResetAction.java
index 3c35e729f02..8a65b530f88 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ResetAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ResetAction.java
@@ -23,6 +23,8 @@ import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.server.ws.Change;
@@ -42,8 +44,10 @@ import static java.util.Collections.emptyList;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_BRANCH;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_COMPONENT;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_KEYS;
+import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
public class ResetAction implements SettingsWsAction {
@@ -92,6 +96,11 @@ public class ResetAction implements SettingsWsAction {
.setExampleValue(KEY_BRANCH_EXAMPLE_001)
.setInternal(true)
.setSince("6.6");
+ action.createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001)
+ .setInternal(true)
+ .setSince("7.1");
}
@Override
@@ -129,9 +138,10 @@ public class ResetAction implements SettingsWsAction {
private static ResetRequest toWsRequest(Request request) {
return new ResetRequest()
- .setKeys(request.paramAsStrings(PARAM_KEYS))
+ .setKeys(request.mandatoryParamAsStrings(PARAM_KEYS))
.setComponent(request.param(PARAM_COMPONENT))
- .setBranch(request.param(PARAM_BRANCH));
+ .setBranch(request.param(PARAM_BRANCH))
+ .setPullRequest(request.param(PARAM_PULL_REQUEST));
}
private Optional<ComponentDto> getComponent(DbSession dbSession, ResetRequest request) {
@@ -139,7 +149,7 @@ public class ResetAction implements SettingsWsAction {
if (componentKey == null) {
return Optional.empty();
}
- return Optional.of(componentFinder.getByKeyAndOptionalBranch(dbSession, componentKey, request.getBranch()));
+ return Optional.of(componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, request.getBranch(), request.getPullRequest()));
}
private void checkPermissions(Optional<ComponentDto> component) {
@@ -153,23 +163,36 @@ public class ResetAction implements SettingsWsAction {
private static class ResetRequest {
private String branch;
+ private String pullRequest;
private String component;
private List<String> keys;
- public ResetRequest setBranch(String branch) {
+ public ResetRequest setBranch(@Nullable String branch) {
this.branch = branch;
return this;
}
+ @CheckForNull
public String getBranch() {
return branch;
}
- public ResetRequest setComponent(String component) {
+ public ResetRequest setPullRequest(@Nullable String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ @CheckForNull
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ public ResetRequest setComponent(@Nullable String component) {
this.component = component;
return this;
}
+ @CheckForNull
public String getComponent() {
return component;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SetAction.java b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SetAction.java
index ed4a9943cd6..2113ff2070f 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SetAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SetAction.java
@@ -34,6 +34,7 @@ import java.util.Set;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
+import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.PropertyType;
@@ -63,6 +64,7 @@ import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_BRANCH;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_COMPONENT;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_FIELD_VALUES;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_KEY;
+import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_VALUE;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_VALUES;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
@@ -133,7 +135,9 @@ public class SetAction implements SettingsWsAction {
.setDescription("Component key")
.setDeprecatedKey("componentKey", "6.3")
.setExampleValue(KEY_PROJECT_EXAMPLE_001);
+
settingsWsSupport.addBranchParam(action);
+ settingsWsSupport.addPullRequestParam(action);
}
@Override
@@ -293,7 +297,8 @@ public class SetAction implements SettingsWsAction {
.setValues(request.multiParam(PARAM_VALUES))
.setFieldValues(request.multiParam(PARAM_FIELD_VALUES))
.setComponent(request.param(PARAM_COMPONENT))
- .setBranch(request.param(PARAM_BRANCH));
+ .setBranch(request.param(PARAM_BRANCH))
+ .setPullRequest(request.param(PARAM_PULL_REQUEST));
checkArgument(isNotEmpty(set.getKey()), "Setting key is mandatory and must not be empty");
checkArgument(set.getValues() != null, "Setting values must not be null");
checkArgument(set.getFieldValues() != null, "Setting fields values must not be null");
@@ -316,7 +321,7 @@ public class SetAction implements SettingsWsAction {
if (componentKey == null) {
return Optional.empty();
}
- return Optional.of(componentFinder.getByKeyAndOptionalBranch(dbSession, componentKey, request.getBranch()));
+ return Optional.of(componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, request.getBranch(), request.getPullRequest()));
}
private PropertyDto toProperty(SetRequest request, Optional<ComponentDto> component) {
@@ -351,26 +356,39 @@ public class SetAction implements SettingsWsAction {
private static class SetRequest {
private String branch;
+ private String pullRequest;
private String component;
private List<String> fieldValues;
private String key;
private String value;
private List<String> values;
- public SetRequest setBranch(String branch) {
+ public SetRequest setBranch(@Nullable String branch) {
this.branch = branch;
return this;
}
+ @CheckForNull
public String getBranch() {
return branch;
}
- public SetRequest setComponent(String component) {
+ public SetRequest setPullRequest(@Nullable String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ @CheckForNull
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ public SetRequest setComponent(@Nullable String component) {
this.component = component;
return this;
}
+ @CheckForNull
public String getComponent() {
return component;
}
@@ -393,16 +411,17 @@ public class SetAction implements SettingsWsAction {
return key;
}
- public SetRequest setValue(String value) {
+ public SetRequest setValue(@Nullable String value) {
this.value = value;
return this;
}
+ @CheckForNull
public String getValue() {
return value;
}
- public SetRequest setValues(List<String> values) {
+ public SetRequest setValues(@Nullable List<String> values) {
this.values = values;
return this;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsParameters.java b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsParameters.java
index 61cdd2b3c0f..d46cb8d0c1c 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsParameters.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsParameters.java
@@ -43,6 +43,7 @@ public class SettingsWsParameters {
public static final String PARAM_COMPONENT = "component";
public static final String PARAM_BRANCH = "branch";
+ public static final String PARAM_PULL_REQUEST = "pullRequest";
public static final String PARAM_KEYS = "keys";
public static final String PARAM_KEY = "key";
public static final String PARAM_VALUE = "value";
diff --git a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsSupport.java b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsSupport.java
index c53bf41b24b..ab3d550195a 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsSupport.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/SettingsWsSupport.java
@@ -39,7 +39,9 @@ import static org.sonar.api.PropertyType.LICENSE;
import static org.sonar.api.web.UserRole.ADMIN;
import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_BRANCH;
+import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
@ServerSide
public class SettingsWsSupport {
@@ -108,4 +110,12 @@ public class SettingsWsSupport {
.setInternal(true)
.setSince("6.6");
}
+
+ WebService.NewParam addPullRequestParam(WebService.NewAction action) {
+ return action.createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request. Only available on following settings : %s", SettingsWs.SETTING_ON_BRANCHES.stream().collect(COMMA_JOINER))
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001)
+ .setInternal(true)
+ .setSince("7.1");
+ }
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ValuesAction.java b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ValuesAction.java
index 57fefaeb3a0..ec2d3a0ee23 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ValuesAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/setting/ws/ValuesAction.java
@@ -30,6 +30,8 @@ import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
import org.sonar.api.config.PropertyDefinition;
import org.sonar.api.config.PropertyDefinitions;
import org.sonar.api.server.ws.Change;
@@ -56,8 +58,8 @@ import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_BRANCH;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_COMPONENT;
import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_KEYS;
+import static org.sonar.server.setting.ws.SettingsWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
-import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.server.ws.WsUtils.writeProtobuf;
@@ -108,11 +110,8 @@ public class ValuesAction implements SettingsWsAction {
action.createParam(PARAM_COMPONENT)
.setDescription("Component key")
.setExampleValue(KEY_PROJECT_EXAMPLE_001);
- action.createParam(PARAM_BRANCH)
- .setDescription("Branch key")
- .setExampleValue(KEY_BRANCH_EXAMPLE_001)
- .setInternal(true)
- .setSince("6.6");
+ settingsWsSupport.addBranchParam(action);
+ settingsWsSupport.addPullRequestParam(action);
}
@Override
@@ -136,7 +135,8 @@ public class ValuesAction implements SettingsWsAction {
private static ValuesRequest toWsRequest(Request request) {
ValuesRequest result = new ValuesRequest()
.setComponent(request.param(PARAM_COMPONENT))
- .setBranch(request.param(PARAM_BRANCH));
+ .setBranch(request.param(PARAM_BRANCH))
+ .setPullRequest(request.param(PARAM_PULL_REQUEST));
if (request.hasParam(PARAM_KEYS)) {
result.setKeys(request.paramAsStrings(PARAM_KEYS));
}
@@ -154,11 +154,11 @@ public class ValuesAction implements SettingsWsAction {
if (componentKey == null) {
return Optional.empty();
}
- ComponentDto component = componentFinder.getByKeyAndOptionalBranch(dbSession, componentKey, valuesRequest.getBranch());
+ ComponentDto component = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, valuesRequest.getBranch(), valuesRequest.getPullRequest());
if (!userSession.hasComponentPermission(USER, component) &&
- !userSession.hasComponentPermission(SCAN_EXECUTION, component) &&
- !userSession.hasPermission(OrganizationPermission.SCAN, component.getOrganizationUuid())) {
- throw insufficientPrivilegesException();
+ !userSession.hasComponentPermission(SCAN_EXECUTION, component) &&
+ !userSession.hasPermission(OrganizationPermission.SCAN, component.getOrganizationUuid())) {
+ throw insufficientPrivilegesException();
}
return Optional.of(component);
}
@@ -303,32 +303,46 @@ public class ValuesAction implements SettingsWsAction {
private static class ValuesRequest {
private String branch;
+ private String pullRequest;
private String component;
private List<String> keys;
- public ValuesRequest setBranch(String branch) {
+ public ValuesRequest setBranch(@Nullable String branch) {
this.branch = branch;
return this;
}
+ @CheckForNull
public String getBranch() {
return branch;
}
- public ValuesRequest setComponent(String component) {
+ public ValuesRequest setPullRequest(@Nullable String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ @CheckForNull
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ public ValuesRequest setComponent(@Nullable String component) {
this.component = component;
return this;
}
+ @CheckForNull
public String getComponent() {
return component;
}
- public ValuesRequest setKeys(List<String> keys) {
+ public ValuesRequest setKeys(@Nullable List<String> keys) {
this.keys = keys;
return this;
}
+ @CheckForNull
public List<String> getKeys() {
return keys;
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/ws/LinesAction.java b/server/sonar-server/src/main/java/org/sonar/server/source/ws/LinesAction.java
index 334a71e9212..263f4c7539e 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/source/ws/LinesAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/source/ws/LinesAction.java
@@ -43,7 +43,9 @@ import static com.google.common.base.Preconditions.checkArgument;
import static org.sonar.server.component.ComponentFinder.ParamNames.UUID_AND_KEY;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_FILE_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
+import static org.sonar.server.ws.WsUtils.checkRequest;
public class LinesAction implements SourcesWsAction {
@@ -52,6 +54,7 @@ public class LinesAction implements SourcesWsAction {
private static final String PARAM_FROM = "from";
private static final String PARAM_TO = "to";
private static final String PARAM_BRANCH = "branch";
+ private static final String PARAM_PULL_REQUEST = "pullRequest";
private final ComponentFinder componentFinder;
private final SourceService sourceService;
@@ -91,7 +94,7 @@ public class LinesAction implements SourcesWsAction {
"has been renamed \"lineHits\", \"conditions\" and \"coveredConditions\""),
new Change("6.2", "fields \"itLineHits\", \"itConditions\" and \"itCoveredConditions\" " +
"are no more returned"),
- new Change("6.6", "fields \"branch\" added"))
+ new Change("6.6", "field \"branch\" added"))
.setHandler(this);
action
@@ -111,6 +114,12 @@ public class LinesAction implements SourcesWsAction {
.setExampleValue(KEY_BRANCH_EXAMPLE_001);
action
+ .createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setInternal(true)
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001);
+
+ action
.createParam(PARAM_FROM)
.setDescription("First line to return. Starts from 1")
.setExampleValue("10")
@@ -146,11 +155,15 @@ public class LinesAction implements SourcesWsAction {
String componentKey = wsRequest.param(PARAM_KEY);
String componentId = wsRequest.param(PARAM_UUID);
String branch = wsRequest.param(PARAM_BRANCH);
- checkArgument(componentId == null || branch == null, "'%s' and '%s' parameters cannot be used at the same time", PARAM_UUID,
- PARAM_BRANCH);
- return branch == null
- ? componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, UUID_AND_KEY)
- : componentFinder.getByKeyAndBranch(dbSession, componentKey, branch);
+ String pullRequest = wsRequest.param(PARAM_PULL_REQUEST);
+ checkArgument(componentId == null || (branch == null && pullRequest == null), "Parameter '%s' cannot be used at the same time as '%s' or '%s'",
+ PARAM_UUID, PARAM_BRANCH, PARAM_PULL_REQUEST);
+ if (branch == null && pullRequest == null) {
+ return componentFinder.getByUuidOrKey(dbSession, componentId, componentKey, UUID_AND_KEY);
+ }
+
+ checkRequest(componentKey!=null, "The '%s' parameter is missing", PARAM_KEY);
+ return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentKey, branch, pullRequest);
}
private void writeSource(Iterable<DbFileSources.Line> lines, JsonWriter json) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/source/ws/RawAction.java b/server/sonar-server/src/main/java/org/sonar/server/source/ws/RawAction.java
index 34a391c9a7d..0591acd2a45 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/source/ws/RawAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/source/ws/RawAction.java
@@ -36,9 +36,13 @@ import org.sonar.server.source.SourceService;
import org.sonar.server.user.UserSession;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
public class RawAction implements SourcesWsAction {
+ private static final String PARAM_KEY = "key";
+ private static final String PARAM_BRANCH = "branch";
+ private static final String PARAM_PULL_REQUEST = "pullRequest";
private final DbClient dbClient;
private final SourceService sourceService;
private final UserSession userSession;
@@ -60,24 +64,31 @@ public class RawAction implements SourcesWsAction {
.setHandler(this);
action
- .createParam("key")
+ .createParam(PARAM_KEY)
.setRequired(true)
.setDescription("File key")
.setExampleValue("my_project:src/foo/Bar.php");
action
- .createParam("branch")
+ .createParam(PARAM_BRANCH)
.setDescription("Branch key")
.setInternal(true)
.setExampleValue(KEY_BRANCH_EXAMPLE_001);
+
+ action
+ .createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setInternal(true)
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001);
}
@Override
public void handle(Request request, Response response) {
- String fileKey = request.mandatoryParam("key");
- String branch = request.param("branch");
+ String fileKey = request.mandatoryParam(PARAM_KEY);
+ String branch = request.param(PARAM_BRANCH);
+ String pullRequest = request.param(PARAM_PULL_REQUEST);
try (DbSession dbSession = dbClient.openSession(false)) {
- ComponentDto file = componentFinder.getByKeyAndOptionalBranch(dbSession, fileKey, branch);
+ ComponentDto file = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, fileKey, branch, pullRequest);
userSession.checkComponentPermission(UserRole.CODEVIEWER, file);
Optional<Iterable<String>> lines = sourceService.getLinesAsRawText(dbSession, file.uuid(), 1, Integer.MAX_VALUE);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/test/ws/ListAction.java b/server/sonar-server/src/main/java/org/sonar/server/test/ws/ListAction.java
index 27f733238f9..ab1110af674 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/test/ws/ListAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/test/ws/ListAction.java
@@ -54,6 +54,7 @@ import static org.sonar.api.web.UserRole.CODEVIEWER;
import static org.sonar.core.util.Protobuf.setNullable;
import static org.sonar.server.es.SearchOptions.MAX_LIMIT;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
import static org.sonar.server.ws.WsUtils.checkFoundWithOptional;
public class ListAction implements TestsWsAction {
@@ -64,6 +65,7 @@ public class ListAction implements TestsWsAction {
public static final String SOURCE_FILE_KEY = "sourceFileKey";
public static final String SOURCE_FILE_LINE_NUMBER = "sourceFileLineNumber";
public static final String PARAM_BRANCH = "branch";
+ public static final String PARAM_PULL_REQUEST = "pullRequest";
private final DbClient dbClient;
private final TestIndex testIndex;
@@ -98,6 +100,7 @@ public class ListAction implements TestsWsAction {
.setDeprecatedSince("5.6")
.setHandler(this)
.setChangelog(new Change("6.6", "\"fileBranch\" field is now returned"))
+ .setChangelog(new Change("7.1", "\"filePullRequest\" field is now returned"))
.addPagingParams(100, MAX_LIMIT);
action
@@ -136,6 +139,12 @@ public class ListAction implements TestsWsAction {
.setSince("6.6")
.setInternal(true)
.setExampleValue(KEY_BRANCH_EXAMPLE_001);
+
+ action.createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setSince("7.1")
+ .setInternal(true)
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001);
}
@Override
@@ -146,6 +155,7 @@ public class ListAction implements TestsWsAction {
String sourceFileUuid = request.param(SOURCE_FILE_ID);
String sourceFileKey = request.param(SOURCE_FILE_KEY);
String branch = request.param(PARAM_BRANCH);
+ String pullRequest = request.param(PARAM_PULL_REQUEST);
Integer sourceFileLineNumber = request.paramAsInt(SOURCE_FILE_LINE_NUMBER);
SearchOptions searchOptions = new SearchOptions().setPage(
request.mandatoryParamAsInt(PAGE),
@@ -154,7 +164,7 @@ public class ListAction implements TestsWsAction {
SearchResult<TestDoc> tests;
Map<String, ComponentDto> componentsByTestFileUuid;
try (DbSession dbSession = dbClient.openSession(false)) {
- tests = searchTests(dbSession, testUuid, testFileUuid, testFileKey, sourceFileUuid, sourceFileKey, branch, sourceFileLineNumber, searchOptions);
+ tests = searchTests(dbSession, testUuid, testFileUuid, testFileKey, sourceFileUuid, sourceFileKey, branch, pullRequest, sourceFileLineNumber, searchOptions);
componentsByTestFileUuid = buildComponentsByTestFileUuid(dbSession, tests.getDocs());
}
@@ -175,6 +185,7 @@ public class ListAction implements TestsWsAction {
testBuilder.setFileKey(component.getKey());
testBuilder.setFileName(component.longName());
setNullable(component.getBranch(), testBuilder::setFileBranch);
+ setNullable(component.getPullRequest(), testBuilder::setFilePullRequest);
}
testBuilder.setStatus(Tests.TestStatus.valueOf(testDoc.status()));
if (testDoc.durationInMs() != null) {
@@ -209,7 +220,8 @@ public class ListAction implements TestsWsAction {
}
private SearchResult<TestDoc> searchTests(DbSession dbSession, @Nullable String testUuid, @Nullable String testFileUuid, @Nullable String testFileKey,
- @Nullable String sourceFileUuid, @Nullable String sourceFileKey, @Nullable String branch, @Nullable Integer sourceFileLineNumber, SearchOptions searchOptions) {
+ @Nullable String sourceFileUuid, @Nullable String sourceFileKey, @Nullable String branch, @Nullable String pullRequest,
+ @Nullable Integer sourceFileLineNumber, SearchOptions searchOptions) {
if (testUuid != null) {
TestDoc testDoc = checkFoundWithOptional(testIndex.getNullableByTestUuid(testUuid), "Test with id '%s' is not found", testUuid);
checkComponentUuidPermission(dbSession, testDoc.fileUuid());
@@ -220,7 +232,7 @@ public class ListAction implements TestsWsAction {
return testIndex.searchByTestFileUuid(testFileUuid, searchOptions);
}
if (testFileKey != null) {
- ComponentDto testFile = componentFinder.getByKeyAndOptionalBranch(dbSession, testFileKey, branch);
+ ComponentDto testFile = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, testFileKey, branch, pullRequest);
userSession.checkComponentPermission(CODEVIEWER, testFile);
return testIndex.searchByTestFileUuid(testFile.uuid(), searchOptions);
}
@@ -230,7 +242,7 @@ public class ListAction implements TestsWsAction {
return testIndex.searchBySourceFileUuidAndLineNumber(sourceFile.uuid(), sourceFileLineNumber, searchOptions);
}
if (sourceFileKey != null && sourceFileLineNumber != null) {
- ComponentDto sourceFile = componentFinder.getByKeyAndOptionalBranch(dbSession, sourceFileKey, branch);
+ ComponentDto sourceFile = componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, sourceFileKey, branch, pullRequest);
userSession.checkComponentPermission(CODEVIEWER, sourceFile);
return testIndex.searchBySourceFileUuidAndLineNumber(sourceFile.uuid(), sourceFileLineNumber, searchOptions);
}
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java b/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java
index 580a62cd94a..f0c22150fe7 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java
@@ -68,11 +68,13 @@ import static org.sonar.db.permission.OrganizationPermission.ADMINISTER_QUALITY_
import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
import static org.sonar.server.ws.KeyExamples.KEY_BRANCH_EXAMPLE_001;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.KeyExamples.KEY_PULL_REQUEST_EXAMPLE_001;
public class ComponentAction implements NavigationWsAction {
static final String PARAM_COMPONENT = "component";
static final String PARAM_BRANCH = "branch";
+ static final String PARAM_PULL_REQUEST = "pullRequest";
private static final String PROPERTY_CONFIGURABLE = "configurable";
private static final String PROPERTY_HAS_ROLE_POLICY = "hasRolePolicy";
@@ -124,6 +126,12 @@ public class ComponentAction implements NavigationWsAction {
.setDescription("Branch key")
.setInternal(true)
.setExampleValue(KEY_BRANCH_EXAMPLE_001);
+
+ projectNavigation
+ .createParam(PARAM_PULL_REQUEST)
+ .setDescription("Pull request id")
+ .setInternal(true)
+ .setExampleValue(KEY_PULL_REQUEST_EXAMPLE_001);
}
@Override
@@ -131,7 +139,8 @@ public class ComponentAction implements NavigationWsAction {
String componentKey = request.mandatoryParam(PARAM_COMPONENT);
try (DbSession session = dbClient.openSession(false)) {
String branch = request.param(PARAM_BRANCH);
- ComponentDto component = componentFinder.getByKeyAndOptionalBranch(session, componentKey, branch);
+ String pullRequest = request.param(PARAM_PULL_REQUEST);
+ ComponentDto component = componentFinder.getByKeyAndOptionalBranchOrPullRequest(session, componentKey, branch, pullRequest);
if (!userSession.hasComponentPermission(USER, component) &&
!userSession.hasComponentPermission(ADMIN, component) &&
!userSession.isSystemAdministrator()) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/user/ws/CreateAction.java
index c4ab3e786ff..96f91e244ae 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/ws/CreateAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/ws/CreateAction.java
@@ -80,6 +80,7 @@ public class CreateAction implements UsersWsAction {
new Change("6.3", "The password is only mandatory when creating local users, and should not be set on non local users"),
new Change("6.3", "The 'infos' message is no more returned when a user is reactivated"))
.setPost(true)
+ .setResponseExample(getClass().getResource("create-example.json"))
.setHandler(this);
action.createParam(PARAM_LOGIN)
@@ -104,13 +105,13 @@ public class CreateAction implements UsersWsAction {
.setExampleValue("myname@email.com");
action.createParam(PARAM_SCM_ACCOUNTS)
- .setDescription("This parameter is deprecated, please use '%s' instead", PARAM_SCM_ACCOUNT)
+ .setDescription("Comma-separated list of SCM accounts. This parameter is deprecated, please use '%s' instead", PARAM_SCM_ACCOUNT)
.setDeprecatedKey(PARAM_SCM_ACCOUNTS_DEPRECATED, "6.0")
.setDeprecatedSince("6.1")
.setExampleValue("myscmaccount1,myscmaccount2");
action.createParam(PARAM_SCM_ACCOUNT)
- .setDescription("SCM accounts. To set several values, the parameter must be called once for each value.")
+ .setDescription("List of SCM accounts. To set several values, the parameter must be called once for each value.")
.setExampleValue("scmAccount=firstValue&scmAccount=secondValue&scmAccount=thirdValue");
action.createParam(PARAM_LOCAL)
diff --git a/server/sonar-server/src/main/java/org/sonar/server/user/ws/SetHomepageAction.java b/server/sonar-server/src/main/java/org/sonar/server/user/ws/SetHomepageAction.java
index d9fb7b1fd8e..92bebf6eb56 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/user/ws/SetHomepageAction.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/user/ws/SetHomepageAction.java
@@ -122,7 +122,7 @@ public class SetHomepageAction implements UsersWsAction {
switch (type) {
case PROJECT:
checkArgument(isNotBlank(componentParameter), PARAMETER_REQUIRED, type.name(), PARAM_COMPONENT);
- return componentFinder.getByKeyAndOptionalBranch(dbSession, componentParameter, branchParameter).uuid();
+ return componentFinder.getByKeyAndOptionalBranchOrPullRequest(dbSession, componentParameter, branchParameter, null).uuid();
case PORTFOLIO:
case APPLICATION:
checkArgument(isNotBlank(componentParameter), PARAMETER_REQUIRED, type.name(), PARAM_COMPONENT);
diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/Branch.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/Branch.java
index c17c7de7f86..2d62e584785 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/webhook/Branch.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/Branch.java
@@ -49,7 +49,7 @@ public final class Branch {
}
public enum Type {
- LONG, SHORT
+ LONG, SHORT, PULL_REQUEST
}
@Override
diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookPayloadFactoryImpl.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookPayloadFactoryImpl.java
index 96c24d254fb..39f6d102c5b 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookPayloadFactoryImpl.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookPayloadFactoryImpl.java
@@ -123,10 +123,16 @@ public class WebhookPayloadFactoryImpl implements WebhookPayloadFactory {
}
return format("%s/dashboard?branch=%s&id=%s",
server.getPublicRootUrl(), encode(branch.getName().orElse("")), encode(project.getKey()));
- } else {
+ }
+ if (branch.getType() == Branch.Type.SHORT) {
return format("%s/project/issues?branch=%s&id=%s&resolved=false",
server.getPublicRootUrl(), encode(branch.getName().orElse("")), encode(project.getKey()));
}
+ if (branch.getType() == Branch.Type.PULL_REQUEST) {
+ return format("%s/project/issues?pullRequest=%s&id=%s&resolved=false",
+ server.getPublicRootUrl(), encode(branch.getName().orElse("")), encode(project.getKey()));
+ }
+ return projectUrlOf(project);
}
private static void writeQualityGate(JsonWriter writer, EvaluatedQualityGate gate) {
diff --git a/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java b/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java
index 2afa9edbc14..dea020eec4d 100644
--- a/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java
+++ b/server/sonar-server/src/main/java/org/sonar/server/ws/KeyExamples.java
@@ -30,6 +30,7 @@ public class KeyExamples {
public static final String KEY_ORG_EXAMPLE_002 = "foo-company";
public static final String KEY_BRANCH_EXAMPLE_001 = "feature/my_branch";
+ public static final String KEY_PULL_REQUEST_EXAMPLE_001 = "5461";
public static final String NAME_WEBHOOK_EXAMPLE_001 = "My Webhook";
public static final String URL_WEBHOOK_EXAMPLE_001 = "https://www.my-webhook-listener.com/sonar";
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/branch/pr/ws/list-example.json b/server/sonar-server/src/main/resources/org/sonar/server/branch/pr/ws/list-example.json
new file mode 100644
index 00000000000..a925c1f901a
--- /dev/null
+++ b/server/sonar-server/src/main/resources/org/sonar/server/branch/pr/ws/list-example.json
@@ -0,0 +1,17 @@
+{
+ "pullRequests": [
+ {
+ "key": "123",
+ "title": "Add feature X",
+ "branch": "feature/bar",
+ "base": "feature/foo",
+ "status": {
+ "bugs": 0,
+ "vulnerabilities": 0,
+ "codeSmells": 0
+ },
+ "analysisDate": "2017-04-01T02:15:42+0200",
+ "url": "https://github.com/SonarSource/sonar-core-plugins/pull/32"
+ }
+ ]
+}
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/user/ws/create-example.json b/server/sonar-server/src/main/resources/org/sonar/server/user/ws/create-example.json
new file mode 100644
index 00000000000..b4bdfda4fe5
--- /dev/null
+++ b/server/sonar-server/src/main/resources/org/sonar/server/user/ws/create-example.json
@@ -0,0 +1,10 @@
+{
+ "user": {
+ "login": "ada.lovelace",
+ "name": "Ada Lovelace",
+ "email": "ada.lovelace@noteg.com",
+ "scmAccounts": ["ada.lovelace"],
+ "active": true,
+ "local": true
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/badge/ws/MeasureActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/badge/ws/MeasureActionTest.java
index 0f7a82d7c52..034441c567d 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/badge/ws/MeasureActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/badge/ws/MeasureActionTest.java
@@ -286,6 +286,7 @@ public class MeasureActionTest {
.containsExactlyInAnyOrder(
tuple("project", true),
tuple("branch", false),
+ tuple("pullRequest", false),
tuple("metric", true));
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/badge/ws/QualityGateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/badge/ws/QualityGateActionTest.java
index 56da90807b7..2071e6c2c6f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/badge/ws/QualityGateActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/badge/ws/QualityGateActionTest.java
@@ -189,7 +189,8 @@ public class QualityGateActionTest {
.extracting(Param::key, Param::isRequired)
.containsExactlyInAnyOrder(
tuple("project", true),
- tuple("branch", false));
+ tuple("branch", false),
+ tuple("pullRequest", false));
}
private MetricDto createQualityGateMetric() {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/DeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/DeleteActionTest.java
new file mode 100644
index 00000000000..1301ef7692b
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/DeleteActionTest.java
@@ -0,0 +1,147 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.branch.pr.ws;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.ArgumentCaptor;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.server.component.ComponentCleanerService;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.component.TestComponentFinder;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.exceptions.UnauthorizedException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsActionTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
+
+public class DeleteActionTest {
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+
+ private ComponentCleanerService componentCleanerService = mock(ComponentCleanerService.class);
+ private ComponentFinder componentFinder = TestComponentFinder.from(db);
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ public WsActionTester ws = new WsActionTester(new DeleteAction(db.getDbClient(), componentFinder, userSession, componentCleanerService));
+
+ @Test
+ public void definition() {
+ WebService.Action definition = ws.getDef();
+
+ assertThat(definition.key()).isEqualTo("delete");
+ assertThat(definition.isPost()).isTrue();
+ assertThat(definition.isInternal()).isFalse();
+ assertThat(definition.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("project", "pullRequest");
+ assertThat(definition.since()).isEqualTo("7.1");
+ }
+
+ @Test
+ public void delete_pull_request() {
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("1984").setBranchType(PULL_REQUEST));
+
+ userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+
+ ws.newRequest()
+ .setParam("project", project.getKey())
+ .setParam("pullRequest", "1984")
+ .execute();
+ verifyDeletedKey(branch.getDbKey());
+ }
+
+ @Test
+ public void fail_if_missing_project_parameter() {
+ userSession.logIn();
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'project' parameter is missing");
+
+ ws.newRequest().execute();
+ }
+
+ @Test
+ public void fail_if_missing_pull_request_parameter() {
+ userSession.logIn();
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'pullRequest' parameter is missing");
+
+ ws.newRequest().setParam("project", "projectName").execute();
+ }
+
+ @Test
+ public void fail_if_not_logged_in() {
+ expectedException.expect(UnauthorizedException.class);
+ expectedException.expectMessage("Authentication is required");
+
+ ws.newRequest().execute();
+ }
+
+ @Test
+ public void fail_if_pull_request_does_not_exist() {
+ ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey("orwell"));
+ userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("Pull request '1984' is not found for project 'orwell'");
+
+ ws.newRequest()
+ .setParam("project", project.getDbKey())
+ .setParam("pullRequest", "1984")
+ .execute();
+ }
+
+ @Test
+ public void fail_if_project_does_not_exist() {
+ userSession.logIn();
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("Project key 'foo' not found");
+
+ ws.newRequest()
+ .setParam("project", "foo")
+ .setParam("pullRequest", "123")
+ .execute();
+ }
+
+ private void verifyDeletedKey(String key) {
+ ArgumentCaptor<ComponentDto> argument = ArgumentCaptor.forClass(ComponentDto.class);
+ verify(componentCleanerService).deleteBranch(any(DbSession.class), argument.capture());
+ assertThat(argument.getValue().getDbKey()).isEqualTo(key);
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/ListActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/ListActionTest.java
new file mode 100644
index 00000000000..e8715b90bae
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/ListActionTest.java
@@ -0,0 +1,372 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.branch.pr.ws;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.resources.ResourceTypes;
+import org.sonar.api.server.ws.WebService;
+import org.sonar.api.utils.DateUtils;
+import org.sonar.api.utils.System2;
+import org.sonar.api.web.UserRole;
+import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.component.ResourceTypesRule;
+import org.sonar.db.component.SnapshotTesting;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.protobuf.DbProjectBranches;
+import org.sonar.db.rule.RuleDefinitionDto;
+import org.sonar.server.component.ComponentFinder;
+import org.sonar.server.es.EsTester;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.issue.index.IssueIndex;
+import org.sonar.server.issue.index.IssueIndexDefinition;
+import org.sonar.server.issue.index.IssueIndexer;
+import org.sonar.server.issue.index.IssueIteratorFactory;
+import org.sonar.server.permission.index.AuthorizationTypeSupport;
+import org.sonar.server.permission.index.PermissionIndexerTester;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.ws.WsActionTester;
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.ProjectPullRequests.ListWsResponse;
+import org.sonarqube.ws.ProjectPullRequests.PullRequest;
+
+import static java.lang.String.format;
+import static java.util.Collections.emptySet;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.api.issue.Issue.RESOLUTION_FALSE_POSITIVE;
+import static org.sonar.api.issue.Issue.RESOLUTION_FIXED;
+import static org.sonar.api.resources.Qualifiers.PROJECT;
+import static org.sonar.api.rules.RuleType.BUG;
+import static org.sonar.api.rules.RuleType.CODE_SMELL;
+import static org.sonar.api.rules.RuleType.VULNERABILITY;
+import static org.sonar.api.utils.DateUtils.dateToLong;
+import static org.sonar.api.utils.DateUtils.parseDateTime;
+import static org.sonar.db.component.BranchType.LONG;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
+import static org.sonar.test.JsonAssert.assertJson;
+import static org.sonarqube.ws.ProjectPullRequests.Status;
+
+public class ListActionTest {
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+ @Rule
+ public DbTester db = DbTester.create(System2.INSTANCE);
+ @Rule
+ public EsTester es = new EsTester(new IssueIndexDefinition(new MapSettings().asConfig()));
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+
+ private ResourceTypes resourceTypes = new ResourceTypesRule().setRootQualifiers(PROJECT);
+ private IssueIndexer issueIndexer = new IssueIndexer(es.client(), db.getDbClient(), new IssueIteratorFactory(db.getDbClient()));
+ private IssueIndex issueIndex = new IssueIndex(es.client(), System2.INSTANCE, userSession, new AuthorizationTypeSupport(userSession));
+ private PermissionIndexerTester permissionIndexerTester = new PermissionIndexerTester(es, issueIndexer);
+
+ public WsActionTester ws = new WsActionTester(new ListAction(db.getDbClient(), userSession, new ComponentFinder(db.getDbClient(), resourceTypes), issueIndex));
+
+ @Test
+ public void definition() {
+ WebService.Action definition = ws.getDef();
+ assertThat(definition.key()).isEqualTo("list");
+ assertThat(definition.isPost()).isFalse();
+ assertThat(definition.isInternal()).isFalse();
+ assertThat(definition.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("project");
+ assertThat(definition.since()).isEqualTo("7.1");
+ }
+
+ @Test
+ public void json_example() {
+ ComponentDto project = db.components().insertPrivateProject(p -> p.setDbKey("sonarqube"));
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setKey("feature/foo").setBranchType(LONG));
+ ComponentDto pullRequest = db.components().insertProjectBranch(project,
+ b -> b.setKey("123")
+ .setBranchType(PULL_REQUEST)
+ .setMergeBranchUuid(longLivingBranch.uuid())
+ .setPullRequestData(DbProjectBranches.PullRequestData.newBuilder()
+ .setBranch("feature/bar")
+ .setTitle("Add feature X")
+ .setUrl("https://github.com/SonarSource/sonar-core-plugins/pull/32")
+ .build()));
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+
+ db.getDbClient().snapshotDao().insert(db.getSession(),
+ SnapshotTesting.newAnalysis(pullRequest).setLast(true).setCreatedAt(DateUtils.parseDateTime("2017-04-01T01:15:42+0100").getTime()));
+ db.commit();
+
+ String json = ws.newRequest()
+ .setParam("project", project.getDbKey())
+ .execute()
+ .getInput();
+
+ assertJson(json).isSimilarTo(ws.getDef().responseExampleAsString());
+ }
+
+ @Test
+ public void pull_request() {
+ ComponentDto project = db.components().insertMainBranch();
+ db.components().insertProjectBranch(project,
+ b -> b.setKey("123")
+ .setBranchType(PULL_REQUEST)
+ .setMergeBranchUuid(project.uuid())
+ .setPullRequestData(DbProjectBranches.PullRequestData.newBuilder().setBranch("feature/bar").build()));
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getDbKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getPullRequestsList())
+ .extracting(PullRequest::getKey, PullRequest::getBranch, PullRequest::getIsOrphan)
+ .containsExactlyInAnyOrder(tuple("123", "feature/bar", false));
+ }
+
+ @Test
+ public void project_with_zero_branches() {
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+
+ String json = ws.newRequest()
+ .setParam("project", project.getDbKey())
+ .setMediaType(MediaTypes.JSON)
+ .execute()
+ .getInput();
+
+ assertJson(json).isSimilarTo("{\"pullRequests\": []}");
+ }
+
+ @Test
+ public void pull_requests() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project,
+ b -> b.setKey("long").setBranchType(BranchType.LONG));
+ ComponentDto pullRequestOnLong = db.components().insertProjectBranch(project,
+ b -> b.setKey("pull_request_on_long")
+ .setBranchType(PULL_REQUEST)
+ .setMergeBranchUuid(longLivingBranch.uuid())
+ .setPullRequestData(DbProjectBranches.PullRequestData.newBuilder().setBranch("feature/bar").build()));
+ ComponentDto pullRequestOnMaster = db.components().insertProjectBranch(project,
+ b -> b.setKey("pull_request_on_master")
+ .setBranchType(PULL_REQUEST)
+ .setMergeBranchUuid(project.uuid())
+ .setPullRequestData(DbProjectBranches.PullRequestData.newBuilder().setBranch("feature/bar").build()));
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getPullRequestsList())
+ .extracting(PullRequest::getKey, PullRequest::getBase)
+ .containsExactlyInAnyOrder(
+ tuple(pullRequestOnLong.getPullRequest(), longLivingBranch.getBranch()),
+ tuple(pullRequestOnMaster.getPullRequest(), "master"));
+ }
+
+ @Test
+ public void base_branch_is_using_default_main_name_when_main_branch_has_no_name() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto pullRequest = db.components().insertProjectBranch(project,
+ b -> b.setKey("pr-123")
+ .setBranchType(PULL_REQUEST)
+ .setMergeBranchUuid(project.uuid())
+ .setPullRequestData(DbProjectBranches.PullRequestData.newBuilder()
+ .setBranch("feature123").build()));
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", pullRequest.getKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getPullRequests(0))
+ .extracting(PullRequest::getKey, PullRequest::getBase)
+ .containsExactlyInAnyOrder(pullRequest.getPullRequest(), "master");
+ }
+
+ @Test
+ public void pull_request_on_removed_branch() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto pullRequest = db.components().insertProjectBranch(project,
+ b -> b.setKey("pr-123")
+ .setBranchType(PULL_REQUEST)
+ .setMergeBranchUuid("unknown")
+ .setPullRequestData(DbProjectBranches.PullRequestData.newBuilder().setBranch("feature/bar").build()));
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getPullRequestsList())
+ .extracting(PullRequest::getKey, PullRequest::hasBase, PullRequest::getIsOrphan)
+ .containsExactlyInAnyOrder(
+ tuple(pullRequest.getPullRequest(), false, true));
+ }
+
+ @Test
+ public void status_on_pull_requests() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
+ ComponentDto pullRequest = db.components().insertProjectBranch(project,
+ b -> b.setKey("pr-123")
+ .setBranchType(PULL_REQUEST)
+ .setMergeBranchUuid(longLivingBranch.uuid())
+ .setPullRequestData(DbProjectBranches.PullRequestData.newBuilder().setBranch("feature/bar").build()));
+ RuleDefinitionDto rule = db.rules().insert();
+ db.issues().insert(rule, pullRequest, pullRequest, i -> i.setType(BUG).setResolution(null));
+ db.issues().insert(rule, pullRequest, pullRequest, i -> i.setType(BUG).setResolution(RESOLUTION_FIXED));
+ db.issues().insert(rule, pullRequest, pullRequest, i -> i.setType(VULNERABILITY).setResolution(null));
+ db.issues().insert(rule, pullRequest, pullRequest, i -> i.setType(VULNERABILITY).setResolution(null));
+ db.issues().insert(rule, pullRequest, pullRequest, i -> i.setType(CODE_SMELL).setResolution(null));
+ db.issues().insert(rule, pullRequest, pullRequest, i -> i.setType(CODE_SMELL).setResolution(null));
+ db.issues().insert(rule, pullRequest, pullRequest, i -> i.setType(CODE_SMELL).setResolution(null));
+ db.issues().insert(rule, pullRequest, pullRequest, i -> i.setType(CODE_SMELL).setResolution(RESOLUTION_FALSE_POSITIVE));
+ issueIndexer.indexOnStartup(emptySet());
+ permissionIndexerTester.allowOnlyAnyone(project);
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getPullRequestsList().stream().map(PullRequest::getStatus))
+ .extracting(Status::hasBugs, Status::getBugs, Status::hasVulnerabilities, Status::getVulnerabilities, Status::hasCodeSmells, Status::getCodeSmells)
+ .containsExactlyInAnyOrder(tuple(true, 1L, true, 2L, true, 3L));
+ }
+
+ @Test
+ public void status_on_pull_request_with_no_issue() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto longLivingBranch = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
+ db.components().insertProjectBranch(project,
+ b -> b.setKey("pr-123")
+ .setBranchType(PULL_REQUEST)
+ .setMergeBranchUuid(longLivingBranch.uuid())
+ .setPullRequestData(DbProjectBranches.PullRequestData.newBuilder().setBranch("feature/bar").build()));
+ issueIndexer.indexOnStartup(emptySet());
+ permissionIndexerTester.allowOnlyAnyone(project);
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getPullRequestsList().stream().map(PullRequest::getStatus))
+ .extracting(Status::getBugs, Status::getVulnerabilities, Status::getCodeSmells)
+ .containsExactlyInAnyOrder(tuple(0L, 0L, 0L));
+ }
+
+ @Test
+ public void response_contains_date_of_last_analysis() {
+ Long lastAnalysisLongLivingBranch = dateToLong(parseDateTime("2017-04-01T00:00:00+0100"));
+ Long previousAnalysisPullRequest = dateToLong(parseDateTime("2017-04-02T00:00:00+0100"));
+ Long lastAnalysisPullRequest = dateToLong(parseDateTime("2017-04-03T00:00:00+0100"));
+
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+
+ ComponentDto pullRequest1 = db.components().insertProjectBranch(project,
+ b -> b.setKey("pr1")
+ .setBranchType(PULL_REQUEST)
+ .setMergeBranchUuid(project.uuid())
+ .setPullRequestData(DbProjectBranches.PullRequestData.newBuilder().setBranch("feature/pr1").build()));
+
+ ComponentDto longLivingBranch2 = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.LONG));
+
+ ComponentDto pullRequest2 = db.components().insertProjectBranch(project,
+ b -> b.setKey("pr2")
+ .setBranchType(PULL_REQUEST)
+ .setMergeBranchUuid(longLivingBranch2.uuid())
+ .setPullRequestData(DbProjectBranches.PullRequestData.newBuilder().setBranch("feature/pr2").build()));
+
+ db.getDbClient().snapshotDao().insert(db.getSession(),
+ SnapshotTesting.newAnalysis(longLivingBranch2).setCreatedAt(lastAnalysisLongLivingBranch));
+ db.getDbClient().snapshotDao().insert(db.getSession(),
+ SnapshotTesting.newAnalysis(pullRequest2).setCreatedAt(previousAnalysisPullRequest).setLast(false));
+ db.getDbClient().snapshotDao().insert(db.getSession(),
+ SnapshotTesting.newAnalysis(pullRequest2).setCreatedAt(lastAnalysisPullRequest));
+ db.commit();
+ issueIndexer.indexOnStartup(emptySet());
+ permissionIndexerTester.allowOnlyAnyone(project);
+
+ ListWsResponse response = ws.newRequest()
+ .setParam("project", project.getKey())
+ .executeProtobuf(ListWsResponse.class);
+
+ assertThat(response.getPullRequestsList())
+ .extracting(PullRequest::hasAnalysisDate, b -> "".equals(b.getAnalysisDate()) ? null : dateToLong(parseDateTime(b.getAnalysisDate())))
+ .containsExactlyInAnyOrder(
+ tuple(false, null),
+ tuple(true, lastAnalysisPullRequest));
+ }
+
+ @Test
+ public void fail_when_using_branch_db_key() throws Exception {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertMainBranch(organization);
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+ ComponentDto branch = db.components().insertProjectBranch(project);
+
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage(format("Component key '%s' not found", branch.getDbKey()));
+
+ ws.newRequest()
+ .setParam("project", branch.getDbKey())
+ .execute();
+ }
+
+ @Test
+ public void fail_if_missing_project_parameter() {
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("The 'project' parameter is missing");
+
+ ws.newRequest().execute();
+ }
+
+ @Test
+ public void fail_if_not_a_reference_on_project() {
+ ComponentDto project = db.components().insertPrivateProject();
+ ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(project));
+ userSession.logIn().addProjectPermission(UserRole.USER, project);
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Invalid project key");
+
+ ws.newRequest()
+ .setParam("project", file.getDbKey())
+ .execute();
+ }
+
+ @Test
+ public void fail_if_project_does_not_exist() {
+ expectedException.expect(NotFoundException.class);
+ expectedException.expectMessage("Component key 'foo' not found");
+
+ ws.newRequest()
+ .setParam("project", "foo")
+ .execute();
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/PullRequestWsModuleTest.java b/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/PullRequestWsModuleTest.java
new file mode 100644
index 00000000000..1db6e4b2c44
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/PullRequestWsModuleTest.java
@@ -0,0 +1,35 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.branch.pr.ws;
+
+import org.junit.Test;
+import org.sonar.core.platform.ComponentContainer;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.core.platform.ComponentContainer.COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER;
+
+public class PullRequestWsModuleTest {
+ @Test
+ public void verify_count_of_added_components() {
+ ComponentContainer container = new ComponentContainer();
+ new PullRequestWsModule().configure(container);
+ assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 3);
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/PullRequestsWsParametersTest.java b/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/PullRequestsWsParametersTest.java
new file mode 100644
index 00000000000..03054ca4fcb
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/PullRequestsWsParametersTest.java
@@ -0,0 +1,33 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.branch.pr.ws;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.test.TestUtils.hasOnlyPrivateConstructors;
+
+public class PullRequestsWsParametersTest {
+
+ @Test
+ public void private_method() {
+ assertThat(hasOnlyPrivateConstructors(PullRequestsWsParameters.class)).isTrue();
+ }
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/PullRequestsWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/PullRequestsWsTest.java
new file mode 100644
index 00000000000..59268f2136b
--- /dev/null
+++ b/server/sonar-server/src/test/java/org/sonar/server/branch/pr/ws/PullRequestsWsTest.java
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.branch.pr.ws;
+
+import org.junit.Test;
+import org.sonar.api.server.ws.Request;
+import org.sonar.api.server.ws.Response;
+import org.sonar.api.server.ws.WebService;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class PullRequestsWsTest {
+
+ @Test
+ public void define_ws() {
+ PullRequestsWs underTest = new PullRequestsWs(new PullRequestWsAction() {
+ @Override
+ public void define(WebService.NewController context) {
+ context.createAction("foo").setHandler(this);
+ }
+
+ @Override
+ public void handle(Request request, Response response) {
+
+ }
+ });
+
+ WebService.Context context = new WebService.Context();
+ underTest.define(context);
+
+ assertThat(context.controller("api/project_pull_requests").action("foo")).isNotNull();
+ }
+
+}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/branch/ws/ListActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/branch/ws/ListActionTest.java
index 937466ea214..c3a1608dbeb 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/branch/ws/ListActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/branch/ws/ListActionTest.java
@@ -70,7 +70,7 @@ import static org.sonar.api.utils.DateUtils.dateToLong;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.core.permission.GlobalPermissions.SCAN_EXECUTION;
import static org.sonar.test.JsonAssert.assertJson;
-import static org.sonarqube.ws.ProjectBranches.Branch.Status;
+import static org.sonarqube.ws.ProjectBranches.Status;
public class ListActionTest {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java
index e3b7bad2c69..a55739065f3 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/ce/ws/ActivityActionTest.java
@@ -19,7 +19,6 @@
*/
package org.sonar.server.ce.ws;
-import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -38,6 +37,7 @@ import org.sonar.db.ce.CeActivityDto.Status;
import org.sonar.db.ce.CeQueueDto;
import org.sonar.db.ce.CeTaskCharacteristicDto;
import org.sonar.db.ce.CeTaskTypes;
+import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.component.SnapshotDto;
import org.sonar.db.organization.OrganizationDto;
@@ -51,10 +51,10 @@ import org.sonar.server.ws.TestResponse;
import org.sonar.server.ws.WsActionTester;
import org.sonar.test.JsonAssert;
import org.sonarqube.ws.Ce;
-import org.sonarqube.ws.Common;
-import org.sonarqube.ws.MediaTypes;
import org.sonarqube.ws.Ce.ActivityResponse;
import org.sonarqube.ws.Ce.Task;
+import org.sonarqube.ws.Common;
+import org.sonarqube.ws.MediaTypes;
import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
@@ -68,6 +68,7 @@ import static org.sonar.db.ce.CeQueueDto.Status.IN_PROGRESS;
import static org.sonar.db.ce.CeQueueDto.Status.PENDING;
import static org.sonar.db.ce.CeTaskCharacteristicDto.BRANCH_KEY;
import static org.sonar.db.ce.CeTaskCharacteristicDto.BRANCH_TYPE_KEY;
+import static org.sonar.db.ce.CeTaskCharacteristicDto.PULL_REQUEST;
import static org.sonar.db.component.BranchType.LONG;
import static org.sonar.server.ce.ws.CeWsParameters.PARAM_COMPONENT_ID;
import static org.sonar.server.ce.ws.CeWsParameters.PARAM_COMPONENT_QUERY;
@@ -382,6 +383,45 @@ public class ActivityActionTest {
}
@Test
+ public void pull_request_in_past_activity() {
+ logInAsSystemAdministrator();
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.addProjectPermission(UserRole.USER, project);
+ ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST));
+ SnapshotDto analysis = db.components().insertSnapshot(pullRequest);
+ CeActivityDto activity = insertActivity("T1", project, SUCCESS, analysis);
+ insertCharacteristic(activity, PULL_REQUEST, pullRequest.getPullRequest());
+
+ ActivityResponse response = ws.newRequest().executeProtobuf(ActivityResponse.class);
+
+ assertThat(response.getTasksList())
+ .extracting(Task::getId, Ce.Task::getPullRequest, Ce.Task::hasPullRequestTitle, Ce.Task::getStatus, Ce.Task::getComponentKey)
+ .containsExactlyInAnyOrder(
+ // TODO the pull request title must be loaded from db
+ tuple("T1", pullRequest.getPullRequest(), false, Ce.TaskStatus.SUCCESS, pullRequest.getKey()));
+ }
+
+ @Test
+ public void pull_request_in_queue_analysis() {
+ logInAsSystemAdministrator();
+ String branch = "pr-123";
+ CeQueueDto queue1 = insertQueue("T1", null, IN_PROGRESS);
+ insertCharacteristic(queue1, PULL_REQUEST, branch);
+ CeQueueDto queue2 = insertQueue("T2", null, PENDING);
+ insertCharacteristic(queue2, PULL_REQUEST, branch);
+
+ ActivityResponse response = ws.newRequest()
+ .setParam("status", "FAILED,IN_PROGRESS,PENDING")
+ .executeProtobuf(ActivityResponse.class);
+
+ assertThat(response.getTasksList())
+ .extracting(Task::getId, Ce.Task::getPullRequest, Ce.Task::hasPullRequestTitle, Ce.Task::getStatus)
+ .containsExactlyInAnyOrder(
+ tuple("T1", branch, false, Ce.TaskStatus.IN_PROGRESS),
+ tuple("T2", branch, false, Ce.TaskStatus.PENDING));
+ }
+
+ @Test
public void fail_if_both_filters_on_component_id_and_name() {
expectedException.expect(BadRequestException.class);
expectedException.expectMessage("componentId and componentQuery must not be set at the same time");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentFinderTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentFinderTest.java
index 6a7c2ec0d78..fc4869d7280 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentFinderTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentFinderTest.java
@@ -30,6 +30,7 @@ import org.sonar.server.exceptions.NotFoundException;
import static java.lang.String.format;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newModuleDto;
@@ -193,6 +194,31 @@ public class ComponentFinderTest {
}
@Test
+ public void get_by_key_and_pull_request() {
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("pr-123").setBranchType(PULL_REQUEST).setMergeBranchUuid(project.uuid()));
+ ComponentDto module = db.components().insertComponent(newModuleDto(branch));
+ ComponentDto directory = db.components().insertComponent(newDirectory(module, "scr"));
+ ComponentDto file = db.components().insertComponent(newFileDto(module));
+
+ assertThat(underTest.getByKeyAndOptionalBranchOrPullRequest(dbSession, project.getKey(), null, "pr-123").uuid()).isEqualTo(branch.uuid());
+ assertThat(underTest.getByKeyAndOptionalBranchOrPullRequest(dbSession, module.getKey(), null, "pr-123").uuid()).isEqualTo(module.uuid());
+ assertThat(underTest.getByKeyAndOptionalBranchOrPullRequest(dbSession, file.getKey(), null, "pr-123").uuid()).isEqualTo(file.uuid());
+ assertThat(underTest.getByKeyAndOptionalBranchOrPullRequest(dbSession, directory.getKey(), null, "pr-123").uuid()).isEqualTo(directory.uuid());
+ }
+
+ @Test
+ public void fail_when_pull_request_branch_provided() {
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setKey("pr-123").setBranchType(PULL_REQUEST));
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Either branch or pull request can be provided, not both");
+
+ assertThat(underTest.getByKeyAndOptionalBranchOrPullRequest(dbSession, project.getKey(), "pr-123", "pr-123").uuid()).isEqualTo(pullRequest.uuid());
+ }
+
+ @Test
public void get_by_key_and_branch_accept_main_branch() {
ComponentDto project = db.components().insertMainBranch();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/AppActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/AppActionTest.java
index 3abc3b46b23..2bbebd58d5d 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/AppActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/AppActionTest.java
@@ -40,6 +40,7 @@ import static org.sonar.api.measures.CoreMetrics.TECHNICAL_DEBT_KEY;
import static org.sonar.api.measures.CoreMetrics.TESTS_KEY;
import static org.sonar.api.measures.CoreMetrics.VIOLATIONS_KEY;
import static org.sonar.api.web.UserRole.USER;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newModuleDto;
@@ -299,7 +300,7 @@ public class AppActionTest {
ComponentDto file = db.components().insertComponent(newFileDto(branch));
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("'componentId' and 'branch' parameters cannot be used at the same time");
+ expectedException.expectMessage("Parameter 'componentId' cannot be used at the same time as 'branch' or 'pullRequest'");
ws.newRequest()
.setParam("uuid", file.uuid())
@@ -308,6 +309,21 @@ public class AppActionTest {
}
@Test
+ public void fail_if_both_componentId_and_pull_request_parameters_provided() {
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST));
+ ComponentDto file = db.components().insertComponent(newFileDto(branch));
+
+ expectedException.expect(IllegalArgumentException.class);
+ expectedException.expectMessage("Parameter 'componentId' cannot be used at the same time as 'branch' or 'pullRequest'");
+
+ ws.newRequest()
+ .setParam("uuid", file.uuid())
+ .setParam("pullRequest", file.getPullRequest())
+ .execute();
+ }
+
+ @Test
public void fail_when_component_not_found() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
@@ -352,7 +368,7 @@ public class AppActionTest {
assertThat(action.isInternal()).isTrue();
assertThat(action.isPost()).isFalse();
assertThat(action.handler()).isNotNull();
- assertThat(action.params()).hasSize(3);
+ assertThat(action.params()).hasSize(4);
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java
index b7324a8974f..b03eae689f2 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/ShowActionTest.java
@@ -19,7 +19,6 @@
*/
package org.sonar.server.component.ws;
-import java.io.IOException;
import java.util.Date;
import javax.annotation.Nullable;
import org.junit.Rule;
@@ -47,6 +46,7 @@ import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.api.utils.DateUtils.formatDateTime;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.api.web.UserRole.USER;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newModuleDto;
@@ -56,6 +56,7 @@ import static org.sonar.test.JsonAssert.assertJson;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT_ID;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PULL_REQUEST;
public class ShowActionTest {
@Rule
@@ -81,7 +82,7 @@ public class ShowActionTest {
tuple("6.5", "Leak period date is added to the response"),
tuple("6.6", "'branch' is added to the response"),
tuple("6.6", "'version' is added to the response"));
- assertThat(action.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("component", "componentId", "branch");
+ assertThat(action.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("component", "componentId", "branch", "pullRequest");
WebService.Param componentId = action.param(PARAM_COMPONENT_ID);
assertThat(componentId.isRequired()).isFalse();
@@ -102,6 +103,11 @@ public class ShowActionTest {
assertThat(branch.isInternal()).isTrue();
assertThat(branch.isRequired()).isFalse();
assertThat(branch.since()).isEqualTo("6.6");
+
+ WebService.Param pullRequest = action.param(PARAM_PULL_REQUEST);
+ assertThat(pullRequest.isInternal()).isTrue();
+ assertThat(pullRequest.isRequired()).isFalse();
+ assertThat(pullRequest.since()).isEqualTo("7.1");
}
@Test
@@ -307,6 +313,32 @@ public class ShowActionTest {
}
@Test
+ public void pull_request() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.addProjectPermission(UserRole.USER, project);
+ String pullRequest = "pr-1234";
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey(pullRequest).setBranchType(PULL_REQUEST));
+ ComponentDto module = db.components().insertComponent(newModuleDto(branch));
+ ComponentDto directory = db.components().insertComponent(newDirectory(module, "dir"));
+ ComponentDto file = db.components().insertComponent(newFileDto(directory));
+ db.components().insertSnapshot(branch, s -> s.setVersion("1.1"));
+
+ ShowWsResponse response = ws.newRequest()
+ .setParam(PARAM_COMPONENT, file.getKey())
+ .setParam(PARAM_PULL_REQUEST, pullRequest)
+ .executeProtobuf(ShowWsResponse.class);
+
+ assertThat(response.getComponent())
+ .extracting(Component::getKey, Component::getPullRequest, Component::getVersion)
+ .containsExactlyInAnyOrder(file.getKey(), pullRequest, "1.1");
+ assertThat(response.getAncestorsList()).extracting(Component::getKey, Component::getPullRequest, Component::getVersion)
+ .containsExactlyInAnyOrder(
+ tuple(directory.getKey(), pullRequest, "1.1"),
+ tuple(module.getKey(), pullRequest, "1.1"),
+ tuple(branch.getKey(), pullRequest, "1.1"));
+ }
+
+ @Test
public void throw_ForbiddenException_if_user_doesnt_have_browse_permission_on_project() {
userSession.logIn();
@@ -343,7 +375,7 @@ public class ShowActionTest {
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("'componentId' and 'branch' parameters cannot be used at the same time");
+ expectedException.expectMessage("Parameter 'componentId' cannot be used at the same time as 'branch' or 'pullRequest'");
ws.newRequest()
.setParam(PARAM_COMPONENT_ID, branch.uuid())
diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java
index 713908c8f66..4452efaa621 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/TreeActionTest.java
@@ -61,6 +61,7 @@ import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.api.resources.Qualifiers.FILE;
import static org.sonar.api.resources.Qualifiers.PROJECT;
import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentTesting.newChildComponent;
import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newModuleDto;
@@ -71,6 +72,7 @@ import static org.sonar.db.component.ComponentTesting.newView;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_COMPONENT_ID;
+import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_PULL_REQUEST;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_QUALIFIERS;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_STRATEGY;
@@ -99,7 +101,7 @@ public class TreeActionTest {
assertThat(action.responseExample()).isNotNull();
assertThat(action.changelog()).extracting(Change::getVersion, Change::getDescription).containsExactlyInAnyOrder(
tuple("6.4", "The field 'id' is deprecated in the response"));
- assertThat(action.params()).extracting(Param::key).containsExactlyInAnyOrder("component", "componentId", "branch", "qualifiers", "strategy",
+ assertThat(action.params()).extracting(Param::key).containsExactlyInAnyOrder("component", "componentId", "branch", "pullRequest", "qualifiers", "strategy",
"q", "s", "p", "asc", "ps");
Param componentId = action.param(PARAM_COMPONENT_ID);
@@ -339,6 +341,29 @@ public class TreeActionTest {
}
@Test
+ public void pull_request() {
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.addProjectPermission(UserRole.USER, project);
+ String pullRequestId = "pr-123";
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey(pullRequestId).setBranchType(PULL_REQUEST));
+ ComponentDto module = db.components().insertComponent(newModuleDto(branch));
+ ComponentDto directory = db.components().insertComponent(newDirectory(module, "dir"));
+ ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(directory));
+
+ TreeWsResponse response = ws.newRequest()
+ .setParam(PARAM_COMPONENT, module.getKey())
+ .setParam(PARAM_PULL_REQUEST, pullRequestId)
+ .executeProtobuf(TreeWsResponse.class);
+
+ assertThat(response.getBaseComponent()).extracting(Components.Component::getKey, Components.Component::getPullRequest)
+ .containsExactlyInAnyOrder(module.getKey(), pullRequestId);
+ assertThat(response.getComponentsList()).extracting(Components.Component::getKey, Components.Component::getPullRequest)
+ .containsExactlyInAnyOrder(
+ tuple(directory.getKey(), pullRequestId),
+ tuple(file.getKey(), pullRequestId));
+ }
+
+ @Test
public void fail_when_using_branch_db_key() {
ComponentDto project = db.components().insertMainBranch();
userSession.addProjectPermission(UserRole.USER, project);
@@ -468,7 +493,7 @@ public class TreeActionTest {
ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("'componentId' and 'branch' parameters cannot be used at the same time");
+ expectedException.expectMessage("Parameter 'componentId' cannot be used at the same time as 'branch' or 'pullRequest'");
ws.newRequest()
.setParam(PARAM_COMPONENT_ID, branch.uuid())
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java
index cb603a7a98d..4a112bbb4de 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java
@@ -242,6 +242,34 @@ public class AnalysisMetadataHolderImplTest {
}
@Test
+ public void setPullRequestId() {
+ AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl();
+
+ String pullRequestId = "pr-123";
+ underTest.setPullRequestId(pullRequestId);
+
+ assertThat(underTest.getPullRequestId()).isEqualTo(pullRequestId);
+ }
+
+ @Test
+ public void getPullRequestId_throws_ISE_when_holder_is_not_initialized() {
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Pull request id has not been set");
+
+ new AnalysisMetadataHolderImpl().getPullRequestId();
+ }
+
+ @Test
+ public void setPullRequestId_throws_ISE_when_called_twice() {
+ AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl();
+ underTest.setPullRequestId("pr-123");
+
+ expectedException.expect(IllegalStateException.class);
+ expectedException.expectMessage("Pull request id has already been set");
+ underTest.setPullRequestId("pr-234");
+ }
+
+ @Test
public void set_and_get_project() {
AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl();
@@ -315,4 +343,15 @@ public class AnalysisMetadataHolderImplTest {
assertThat(underTest.isShortLivingBranch()).isTrue();
}
+
+ @Test
+ public void getPullRequestBranch_returns_true() {
+ Branch branch = mock(Branch.class);
+ when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
+
+ AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl();
+ underTest.setBranch(branch);
+
+ assertThat(underTest.isPullRequest()).isTrue();
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java
index cf89ba5747c..08f75f27e95 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/AnalysisMetadataHolderRule.java
@@ -49,6 +49,8 @@ public class AnalysisMetadataHolderRule extends ExternalResource implements Muta
private final InitializedProperty<Branch> branch = new InitializedProperty<>();
+ private final InitializedProperty<String> pullRequestId = new InitializedProperty<>();
+
private final InitializedProperty<Project> project = new InitializedProperty<>();
private final InitializedProperty<Integer> rootComponentRef = new InitializedProperty<>();
@@ -168,6 +170,18 @@ public class AnalysisMetadataHolderRule extends ExternalResource implements Muta
}
@Override
+ public MutableAnalysisMetadataHolder setPullRequestId(String pullRequestId) {
+ this.pullRequestId.setProperty(pullRequestId);
+ return this;
+ }
+
+ @Override
+ public String getPullRequestId() {
+ checkState(pullRequestId.isInitialized(), "Pull request id has not been set");
+ return pullRequestId.getProperty();
+ }
+
+ @Override
public AnalysisMetadataHolderRule setProject(Project p) {
this.project.setProperty(p);
return this;
@@ -226,4 +240,10 @@ public class AnalysisMetadataHolderRule extends ExternalResource implements Muta
Branch property = this.branch.getProperty();
return property != null && property.getType() == BranchType.LONG;
}
+
+ @Override
+ public boolean isPullRequest() {
+ Branch property = this.branch.getProperty();
+ return property != null && property.getType() == BranchType.PULL_REQUEST;
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java
index 9935ae33d25..24a8f1d15fd 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/analysis/MutableAnalysisMetadataHolderRule.java
@@ -121,6 +121,17 @@ public class MutableAnalysisMetadataHolderRule extends ExternalResource implemen
}
@Override
+ public String getPullRequestId() {
+ return delegate.getPullRequestId();
+ }
+
+ @Override
+ public MutableAnalysisMetadataHolder setPullRequestId(String pullRequestId) {
+ delegate.setPullRequestId(pullRequestId);
+ return this;
+ }
+
+ @Override
public MutableAnalysisMetadataHolderRule setProject(@Nullable Project project) {
delegate.setProject(project);
return this;
@@ -173,4 +184,9 @@ public class MutableAnalysisMetadataHolderRule extends ExternalResource implemen
public boolean isLongLivingBranch() {
return delegate.isLongLivingBranch();
}
+
+ @Override
+ public boolean isPullRequest() {
+ return delegate.isPullRequest();
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutorTest.java
index 15e0705c3e4..4270765b208 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutorTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/api/posttask/PostProjectAnalysisTasksExecutorTest.java
@@ -302,6 +302,11 @@ public class PostProjectAnalysisTasksExecutorTest {
}
@Override
+ public String getPullRequestId() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public String generateKey(ScannerReport.Component module, @Nullable ScannerReport.Component fileOrDir) {
throw new UnsupportedOperationException();
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterImplTest.java
index ad333dd598d..f37bf46dd46 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/BranchPersisterImplTest.java
@@ -81,7 +81,28 @@ public class BranchPersisterImplTest {
assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(2);
assertThat(dbTester.countRowsOfTable("project_branches")).isEqualTo(1);
+ }
+
+ @Test
+ public void persist_pull_request_data() {
+ String pullRequestId = "pr-123";
+ analysisMetadataHolder.setBranch(createBranch(BranchType.PULL_REQUEST, false, pullRequestId));
+ analysisMetadataHolder.setPullRequestId(pullRequestId);
+ treeRootHolder.setRoot(BRANCH);
+
+ // add main branch in project table and in metadata
+ ComponentDto dto = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert(), MAIN.getUuid()).setDbKey(MAIN.getKey());
+ analysisMetadataHolder.setProject(Project.copyOf(dto));
+ dbTester.getDbClient().componentDao().insert(dbTester.getSession(), dto);
+ // this should add new columns in project and project_branches
+ underTest.persist(dbTester.getSession());
+
+ dbTester.getSession().commit();
+
+ assertThat(dbTester.countRowsOfTable("projects")).isEqualTo(2);
+ assertThat(dbTester.countRowsOfTable("project_branches")).isEqualTo(1);
+ assertThat(dbTester.countSql("select count(*) from project_branches where pull_request_binary is not null")).isEqualTo(1);
}
private static Branch createBranch(BranchType type, boolean isMain, String name) {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ShortBranchComponentsWithIssuesTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ShortBranchComponentsWithIssuesTest.java
index 492b0ec5eb1..f0fca60160f 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ShortBranchComponentsWithIssuesTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/component/ShortBranchComponentsWithIssuesTest.java
@@ -151,6 +151,25 @@ public class ShortBranchComponentsWithIssuesTest {
}
@Test
+ public void should_find_components_with_issues_to_merge_on_derived_pull_request() {
+ ComponentDto project = db.components().insertMainBranch();
+ setRootUuid(project.uuid());
+
+ ComponentDto pullRequest = db.components().insertProjectBranch(project,
+ b -> b.setBranchType(BranchType.PULL_REQUEST),
+ b -> b.setMergeBranchUuid(project.uuid()));
+
+ RuleDefinitionDto rule = db.rules().insert();
+
+ ComponentDto fileWithResolvedIssue = db.components().insertComponent(ComponentTesting.newFileDto(pullRequest, null));
+ db.issues().insertIssue(IssueTesting.newIssue(rule, pullRequest, fileWithResolvedIssue).setStatus("RESOLVED"));
+
+ underTest = new ShortBranchComponentsWithIssues(treeRootHolder, db.getDbClient());
+
+ assertThat(underTest.getUuids(fileWithResolvedIssue.getKey())).hasSize(1);
+ }
+
+ @Test
public void should_not_find_components_with_issues_to_merge_on_derived_long() {
ComponentDto project = db.components().insertMainBranch();
setRootUuid(project.uuid());
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegatorTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegatorTest.java
index b98b719412e..69d84ffe759 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegatorTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/issue/IssueTrackingDelegatorTest.java
@@ -101,4 +101,18 @@ public class IssueTrackingDelegatorTest {
verifyZeroInteractions(tracker);
verifyZeroInteractions(mergeBranchTracker);
}
+
+ @Test
+ public void delegate_pull_request_tracker() {
+ Branch branch = mock(Branch.class);
+ when(branch.getType()).thenReturn(BranchType.PULL_REQUEST);
+ when(analysisMetadataHolder.getBranch()).thenReturn(mock(Branch.class));
+ when(analysisMetadataHolder.isShortLivingBranch()).thenReturn(true);
+
+ underTest.track(component);
+
+ verify(shortBranchTracker).track(component);
+ verifyZeroInteractions(tracker);
+ verifyZeroInteractions(mergeBranchTracker);
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStepTest.java
index 3d61a0369bb..43cdc901f2a 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/LoadQualityGateStepTest.java
@@ -68,6 +68,17 @@ public class LoadQualityGateStepTest {
}
@Test
+ public void add_hardcoded_QG_on_pull_request() {
+ when(analysisMetadataHolder.isPullRequest()).thenReturn(true);
+ QualityGate qualityGate = mock(QualityGate.class);
+ when(qualityGateService.findById(ShortLivingBranchQualityGate.ID)).thenReturn(Optional.of(qualityGate));
+
+ underTest.execute();
+
+ assertThat(mutableQualityGateHolder.getQualityGate().get()).isSameAs(qualityGate);
+ }
+
+ @Test
public void execute_sets_default_QualityGate_when_project_has_no_settings() {
when(settingsRepository.getConfiguration()).thenReturn(new MapSettings().asConfig());
QualityGate defaultGate = mock(QualityGate.class);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStepTest.java
index 1bf8cb09521..0501ef46791 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/QualityGateEventsStepTest.java
@@ -325,4 +325,23 @@ public class QualityGateEventsStepTest {
verifyZeroInteractions(treeRootHolder, metricRepository, measureRepository, eventRepository, notificationService);
}
+
+ @Test
+ public void no_alert_on_pull_request_branches() {
+ Branch shortBranch = mock(Branch.class);
+ when(shortBranch.getType()).thenReturn(BranchType.PULL_REQUEST);
+ analysisMetadataHolder.setBranch(shortBranch);
+ TreeRootHolder treeRootHolder = mock(TreeRootHolder.class);
+ MetricRepository metricRepository = mock(MetricRepository.class);
+ MeasureRepository measureRepository = mock(MeasureRepository.class);
+ EventRepository eventRepository = mock(EventRepository.class);
+ NotificationService notificationService = mock(NotificationService.class);
+
+ QualityGateEventsStep underTest = new QualityGateEventsStep(treeRootHolder, metricRepository, measureRepository,
+ eventRepository, notificationService, analysisMetadataHolder);
+
+ underTest.execute();
+
+ verifyZeroInteractions(treeRootHolder, metricRepository, measureRepository, eventRepository, notificationService);
+ }
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportPersistComponentsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportPersistComponentsStepTest.java
index 5dddeaf6033..75a68862c44 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportPersistComponentsStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/ReportPersistComponentsStepTest.java
@@ -944,6 +944,11 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
}
@Override
+ public String getPullRequestId() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public String generateKey(ScannerReport.Component module, @Nullable ScannerReport.Component fileOrDir) {
String moduleKey = module.getKey();
if (fileOrDir == null || isEmpty(fileOrDir.getPath())) {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStepTest.java
index 4778aa7a0f2..eacd042c8ca 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStepTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/SendIssueNotificationsStepTest.java
@@ -67,6 +67,7 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentTesting.newBranchDto;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
@@ -79,6 +80,7 @@ import static org.sonar.server.computation.task.projectanalysis.component.Report
public class SendIssueNotificationsStepTest extends BaseStepTest {
private static final String BRANCH_NAME = "feature";
+ private static final String PULL_REQUEST_ID = "pr-123";
private static final long ANALYSE_DATE = 123L;
private static final int FIVE_MINUTES_IN_MS = 1000 * 60 * 5;
@@ -143,7 +145,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
underTest.execute();
verify(notificationService).deliver(newIssuesNotificationMock);
- verify(newIssuesNotificationMock).setProject(PROJECT.getPublicKey(), PROJECT.getName(), null);
+ verify(newIssuesNotificationMock).setProject(PROJECT.getPublicKey(), PROJECT.getName(), null, null);
verify(newIssuesNotificationMock).setAnalysisDate(new Date(ANALYSE_DATE));
verify(newIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), any());
verify(newIssuesNotificationMock).setDebt(ISSUE_DURATION);
@@ -206,7 +208,25 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
underTest.execute();
verify(notificationService).deliver(newIssuesNotificationMock);
- verify(newIssuesNotificationMock).setProject(branch.getKey(), branch.longName(), BRANCH_NAME);
+ verify(newIssuesNotificationMock).setProject(branch.getKey(), branch.longName(), BRANCH_NAME, null);
+ verify(newIssuesNotificationMock).setAnalysisDate(new Date(ANALYSE_DATE));
+ verify(newIssuesNotificationMock).setStatistics(eq(branch.longName()), any(NewIssuesStatistics.Stats.class));
+ verify(newIssuesNotificationMock).setDebt(ISSUE_DURATION);
+ }
+
+ @Test
+ public void send_global_new_issues_notification_on_pull_request() {
+ ComponentDto branch = setUpProjectWithBranch();
+ issueCache.newAppender().append(
+ new DefaultIssue().setType(randomRuleType).setEffort(ISSUE_DURATION).setCreationDate(new Date(ANALYSE_DATE))).close();
+ when(notificationService.hasProjectSubscribersForTypes(branch.uuid(), SendIssueNotificationsStep.NOTIF_TYPES)).thenReturn(true);
+ analysisMetadataHolder.setBranch(newPullRequest());
+ analysisMetadataHolder.setPullRequestId(PULL_REQUEST_ID);
+
+ underTest.execute();
+
+ verify(notificationService).deliver(newIssuesNotificationMock);
+ verify(newIssuesNotificationMock).setProject(branch.getKey(), branch.longName(), null, PULL_REQUEST_ID);
verify(newIssuesNotificationMock).setAnalysisDate(new Date(ANALYSE_DATE));
verify(newIssuesNotificationMock).setStatistics(eq(branch.longName()), any(NewIssuesStatistics.Stats.class));
verify(newIssuesNotificationMock).setDebt(ISSUE_DURATION);
@@ -238,7 +258,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
verify(notificationService).deliver(newIssuesNotificationMock);
verify(notificationService).deliver(myNewIssuesNotificationMock);
verify(myNewIssuesNotificationMock).setAssignee(ISSUE_ASSIGNEE);
- verify(myNewIssuesNotificationMock).setProject(PROJECT.getPublicKey(), PROJECT.getName(), null);
+ verify(myNewIssuesNotificationMock).setProject(PROJECT.getPublicKey(), PROJECT.getName(), null, null);
verify(myNewIssuesNotificationMock).setAnalysisDate(new Date(ANALYSE_DATE));
verify(myNewIssuesNotificationMock).setStatistics(eq(PROJECT.getName()), any(NewIssuesStatistics.Stats.class));
verify(myNewIssuesNotificationMock).setDebt(ISSUE_DURATION);
@@ -424,7 +444,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
private NewIssuesNotification createNewIssuesNotificationMock() {
NewIssuesNotification notification = mock(NewIssuesNotification.class);
- when(notification.setProject(any(), any(), any())).thenReturn(notification);
+ when(notification.setProject(any(), any(), any(), any())).thenReturn(notification);
when(notification.setProjectVersion(any())).thenReturn(notification);
when(notification.setAnalysisDate(any())).thenReturn(notification);
when(notification.setStatistics(any(), any())).thenReturn(notification);
@@ -435,7 +455,7 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
private MyNewIssuesNotification createMyNewIssuesNotificationMock() {
MyNewIssuesNotification notification = mock(MyNewIssuesNotification.class);
when(notification.setAssignee(any())).thenReturn(notification);
- when(notification.setProject(any(), any(), any())).thenReturn(notification);
+ when(notification.setProject(any(), any(), any(), any())).thenReturn(notification);
when(notification.setProjectVersion(any())).thenReturn(notification);
when(notification.setAnalysisDate(any())).thenReturn(notification);
when(notification.setStatistics(any(), any())).thenReturn(notification);
@@ -450,6 +470,15 @@ public class SendIssueNotificationsStepTest extends BaseStepTest {
return branch;
}
+ private static Branch newPullRequest() {
+ Branch branch = mock(Branch.class);
+ when(branch.isMain()).thenReturn(false);
+ when(branch.getType()).thenReturn(PULL_REQUEST);
+ when(branch.getName()).thenReturn(BRANCH_NAME);
+ when(branch.getPullRequestId()).thenReturn(PULL_REQUEST_ID);
+ return branch;
+ }
+
private ComponentDto setUpProjectWithBranch() {
ComponentDto project = newPrivateProjectDto(newOrganizationDto());
ComponentDto branch = newProjectBranch(project, newBranchDto(project).setKey(BRANCH_NAME));
diff --git a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsParserTest.java b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsParserTest.java
index 3764f6ca825..d45ce9934fc 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsParserTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/DuplicationsParserTest.java
@@ -19,13 +19,13 @@
*/
package org.sonar.server.duplication.ws;
-import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import java.util.List;
import javax.annotation.Nullable;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import static java.lang.String.format;
@@ -44,14 +44,14 @@ public class DuplicationsParserTest {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
- assertThat(parser.parse(db.getSession(), file, null, null)).isEmpty();
+ assertThat(parser.parse(db.getSession(), file, null, null, null)).isEmpty();
}
@Test
public void duplication_on_same_file() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
- List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file, null,
+ List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file, null, null,
format("<duplications>\n" +
" <g>\n" +
" <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
@@ -80,7 +80,7 @@ public class DuplicationsParserTest {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file1 = db.components().insertComponent(newFileDto(project));
ComponentDto file2 = db.components().insertComponent(newFileDto(project));
- List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null,
+ List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, null,
format("<duplications>\n" +
" <g>\n" +
" <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
@@ -111,7 +111,7 @@ public class DuplicationsParserTest {
ComponentDto file2 = db.components().insertComponent(newFileDto(project1));
ComponentDto project2 = db.components().insertPrivateProject();
ComponentDto fileOnProject2 = db.components().insertComponent(newFileDto(project2));
- List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null,
+ List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, null,
format("<duplications>\n" +
" <g>\n" +
" <b s=\"148\" l=\"24\" r=\"%s\"/>\n" +
@@ -154,7 +154,7 @@ public class DuplicationsParserTest {
ComponentDto file2 = db.components().insertComponent(newFileDto(project2)
.setDbKey("com.sonarsource.orchestrator:sonar-orchestrator:src/main/java/com/sonar/orchestrator/util/CommandExecutor.java")
.setLongName("CommandExecutor"));
- List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null,
+ List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, null,
format("<duplications>\n" +
" <g>\n" +
" <b s=\"94\" l=\"101\" r=\"%s\"/>\n" +
@@ -180,7 +180,7 @@ public class DuplicationsParserTest {
public void duplication_on_not_existing_file() {
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
- List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file, null,
+ List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file, null, null,
format("<duplications>\n" +
" <g>\n" +
" <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
@@ -243,7 +243,39 @@ public class DuplicationsParserTest {
ComponentDto branch = db.components().insertProjectBranch(project);
ComponentDto file1 = db.components().insertComponent(newFileDto(branch));
ComponentDto file2 = db.components().insertComponent(newFileDto(branch));
- List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, branch.getBranch(),
+ List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, branch.getBranch(), null,
+ format("<duplications>\n" +
+ " <g>\n" +
+ " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
+ " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
+ " </g>\n" +
+ "</duplications>", file2.getDbKey(), file1.getDbKey()));
+ assertThat(blocks).hasSize(1);
+
+ List<DuplicationsParser.Duplication> duplications = blocks.get(0).getDuplications();
+ assertThat(duplications).hasSize(2);
+
+ // Current file comes first
+ DuplicationsParser.Duplication duplication1 = duplications.get(0);
+ assertThat(duplication1.file()).isEqualTo(file1);
+ assertThat(duplication1.file().getKey()).isEqualTo(file1.getKey());
+ assertThat(duplication1.from()).isEqualTo(31);
+ assertThat(duplication1.size()).isEqualTo(5);
+
+ DuplicationsParser.Duplication duplication2 = duplications.get(1);
+ assertThat(duplication2.file()).isEqualTo(file2);
+ assertThat(duplication2.file().getKey()).isEqualTo(file2.getKey());
+ assertThat(duplication2.from()).isEqualTo(20);
+ assertThat(duplication2.size()).isEqualTo(5);
+ }
+
+ @Test
+ public void duplication_on_pull_request() {
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST));
+ ComponentDto file1 = db.components().insertComponent(newFileDto(pullRequest));
+ ComponentDto file2 = db.components().insertComponent(newFileDto(pullRequest));
+ List<DuplicationsParser.Block> blocks = parser.parse(db.getSession(), file1, null, pullRequest.getPullRequest(),
format("<duplications>\n" +
" <g>\n" +
" <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
@@ -270,12 +302,8 @@ public class DuplicationsParserTest {
}
private static DuplicationsParser.Duplication duplication(List<DuplicationsParser.Duplication> duplications, @Nullable final String componentKey) {
- return Iterables.find(duplications, new Predicate<DuplicationsParser.Duplication>() {
- @Override
- public boolean apply(@Nullable DuplicationsParser.Duplication input) {
- return input != null && (componentKey == null ? input.file() == null : input.file() != null && componentKey.equals(input.file().getDbKey()));
- }
- });
+ return Iterables.find(duplications, input -> input != null && (componentKey == null ? input.file() == null
+ : input.file() != null && componentKey.equals(input.file().getDbKey())));
}
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java
index df9cb7591f2..488fccfe260 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowActionTest.java
@@ -28,6 +28,7 @@ import org.sonar.api.measures.CoreMetrics;
import org.sonar.api.server.ws.WebService;
import org.sonar.api.web.UserRole;
import org.sonar.db.DbTester;
+import org.sonar.db.component.BranchType;
import org.sonar.db.component.ComponentDto;
import org.sonar.db.metric.MetricDto;
import org.sonar.db.organization.OrganizationDto;
@@ -76,7 +77,7 @@ public class ShowActionTest {
assertThat(show.since()).isEqualTo("4.4");
assertThat(show.isInternal()).isFalse();
assertThat(show.responseExampleAsString()).isNotEmpty();
- assertThat(show.params()).hasSize(3);
+ assertThat(show.params()).hasSize(4);
}
@Test
@@ -160,6 +161,58 @@ public class ShowActionTest {
}
@Test
+ public void duplications_by_file_key_and_pull_request() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSessionRule.addProjectPermission(UserRole.CODEVIEWER, project);
+ ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(BranchType.PULL_REQUEST));
+ ComponentDto file = db.components().insertComponent(newFileDto(pullRequest));
+ db.measures().insertLiveMeasure(file, dataMetric, m -> m.setData(format("<duplications>\n" +
+ " <g>\n" +
+ " <b s=\"31\" l=\"5\" r=\"%s\"/>\n" +
+ " <b s=\"20\" l=\"5\" r=\"%s\"/>\n" +
+ " </g>\n" +
+ "</duplications>\n", file.getDbKey(), file.getDbKey())));
+
+ String result = ws.newRequest()
+ .setParam("key", file.getKey())
+ .setParam("pullRequest", pullRequest.getPullRequest())
+ .execute()
+ .getInput();
+
+ assertJson(result).isSimilarTo(
+ format("{\n" +
+ " \"duplications\": [\n" +
+ " {\n" +
+ " \"blocks\": [\n" +
+ " {\n" +
+ " \"from\": 20,\n" +
+ " \"size\": 5,\n" +
+ " \"_ref\": \"1\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"from\": 31,\n" +
+ " \"size\": 5,\n" +
+ " \"_ref\": \"1\"\n" +
+ " }\n" +
+ " ]\n" +
+ " }\n" +
+ " ],\n" +
+ " \"files\": {\n" +
+ " \"1\": {\n" +
+ " \"key\": \"%s\",\n" +
+ " \"name\": \"%s\",\n" +
+ " \"uuid\": \"%s\",\n" +
+ " \"project\": \"%s\",\n" +
+ " \"projectUuid\": \"%s\",\n" +
+ " \"projectName\": \"%s\"\n" +
+ " \"pullRequest\": \"%s\"\n" +
+ " }\n" +
+ " }\n" +
+ "}",
+ file.getKey(), file.longName(), file.uuid(), pullRequest.getKey(), pullRequest.uuid(), project.longName(), file.getPullRequest()));
+ }
+
+ @Test
public void fail_if_file_does_not_exist() {
expectedException.expect(NotFoundException.class);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowResponseBuilderTest.java b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowResponseBuilderTest.java
index 67c85ae9b6a..4042c185b33 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowResponseBuilderTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/duplication/ws/ShowResponseBuilderTest.java
@@ -32,6 +32,7 @@ import org.sonar.db.component.ComponentDto;
import org.sonar.test.JsonAssert;
import static com.google.common.collect.Lists.newArrayList;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newModuleDto;
@@ -53,7 +54,7 @@ public class ShowResponseBuilderTest {
new DuplicationsParser.Duplication(file1, 57, 12),
new DuplicationsParser.Duplication(file2, 73, 12))));
- test(blocks, null,
+ test(blocks, null, null,
"{\n" +
" \"duplications\": [\n" +
" {\n" +
@@ -98,7 +99,7 @@ public class ShowResponseBuilderTest {
new DuplicationsParser.Duplication(file1, 57, 12),
new DuplicationsParser.Duplication(file2, 73, 12))));
- test(blocks, null,
+ test(blocks, null, null,
"{\n" +
" \"duplications\": [\n" +
" {\n" +
@@ -139,7 +140,7 @@ public class ShowResponseBuilderTest {
// Duplication on a removed file
new DuplicationsParser.Duplication(null, 73, 12))));
- test(blocks, null,
+ test(blocks, null, null,
"{\n" +
" \"duplications\": [\n" +
" {\n" +
@@ -175,7 +176,7 @@ public class ShowResponseBuilderTest {
new DuplicationsParser.Duplication(file1, 57, 12),
new DuplicationsParser.Duplication(file2, 73, 12))));
- test(blocks, branch.getBranch(),
+ test(blocks, branch.getBranch(), null,
"{\n" +
" \"duplications\": [\n" +
" {\n" +
@@ -209,14 +210,58 @@ public class ShowResponseBuilderTest {
}
@Test
+ public void write_duplications_on_pull_request() {
+ ComponentDto project = db.components().insertMainBranch();
+ ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST));
+ ComponentDto file1 = db.components().insertComponent(newFileDto(pullRequest));
+ ComponentDto file2 = db.components().insertComponent(newFileDto(pullRequest));
+ List<DuplicationsParser.Block> blocks = newArrayList();
+ blocks.add(new DuplicationsParser.Block(newArrayList(
+ new DuplicationsParser.Duplication(file1, 57, 12),
+ new DuplicationsParser.Duplication(file2, 73, 12))));
+
+ test(blocks, null, pullRequest.getPullRequest(),
+ "{\n" +
+ " \"duplications\": [\n" +
+ " {\n" +
+ " \"blocks\": [\n" +
+ " {\n" +
+ " \"from\": 57, \"size\": 12, \"_ref\": \"1\"\n" +
+ " },\n" +
+ " {\n" +
+ " \"from\": 73, \"size\": 12, \"_ref\": \"2\"\n" +
+ " }\n" +
+ " ]\n" +
+ " }," +
+ " ],\n" +
+ " \"files\": {\n" +
+ " \"1\": {\n" +
+ " \"key\": \"" + file1.getKey() + "\",\n" +
+ " \"name\": \"" + file1.longName() + "\",\n" +
+ " \"project\": \"" + pullRequest.getKey() + "\",\n" +
+ " \"projectName\": \"" + pullRequest.longName() + "\",\n" +
+ " \"pullRequest\": \"" + pullRequest.getPullRequest() + "\",\n" +
+ " },\n" +
+ " \"2\": {\n" +
+ " \"key\": \"" + file2.getKey() + "\",\n" +
+ " \"name\": \"" + file2.longName() + "\",\n" +
+ " \"project\": \"" + pullRequest.getKey() + "\",\n" +
+ " \"projectName\": \"" + pullRequest.longName() + "\",\n" +
+ " \"pullRequest\": \"" + pullRequest.getPullRequest() + "\",\n" +
+ " }\n" +
+ " }" +
+ "}");
+ }
+
+ @Test
public void write_nothing_when_no_data() {
- test(Collections.emptyList(), null, "{\"duplications\": [], \"files\": {}}");
+ test(Collections.emptyList(), null, null,"{\"duplications\": [], \"files\": {}}");
}
- private void test(List<DuplicationsParser.Block> blocks, @Nullable String branch, String expected) {
+ private void test(List<DuplicationsParser.Block> blocks, @Nullable String branch, @Nullable String pullRequest, String expected) {
StringWriter output = new StringWriter();
JsonWriter jsonWriter = JsonWriter.of(output);
- ProtobufJsonFormat.write(underTest.build(db.getSession(), blocks, branch), jsonWriter);
+ ProtobufJsonFormat.write(underTest.build(db.getSession(), blocks, branch, pullRequest), jsonWriter);
JsonAssert.assertJson(output.toString()).isSimilarTo(expected);
}
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangeNotificationTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangeNotificationTest.java
index 26e244ece61..1ec5b97df72 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangeNotificationTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangeNotificationTest.java
@@ -84,7 +84,7 @@ public class IssueChangeNotificationTest {
@Test
public void set_project_without_branch() {
- IssueChangeNotification result = notification.setProject("MyService", "My Service", null);
+ IssueChangeNotification result = notification.setProject("MyService", "My Service", null, null);
assertThat(result.getFieldValue("projectKey")).isEqualTo("MyService");
assertThat(result.getFieldValue("projectName")).isEqualTo("My Service");
assertThat(result.getFieldValue("branch")).isNull();
@@ -92,13 +92,21 @@ public class IssueChangeNotificationTest {
@Test
public void set_project_with_branch() {
- IssueChangeNotification result = notification.setProject("MyService", "My Service", "feature1");
+ IssueChangeNotification result = notification.setProject("MyService", "My Service", "feature1", null);
assertThat(result.getFieldValue("projectKey")).isEqualTo("MyService");
assertThat(result.getFieldValue("projectName")).isEqualTo("My Service");
assertThat(result.getFieldValue("branch")).isEqualTo("feature1");
}
@Test
+ public void set_project_with_pull_request() {
+ IssueChangeNotification result = notification.setProject("MyService", "My Service", null, "pr-123");
+ assertThat(result.getFieldValue("projectKey")).isEqualTo("MyService");
+ assertThat(result.getFieldValue("projectName")).isEqualTo("My Service");
+ assertThat(result.getFieldValue("pullRequest")).isEqualTo("pr-123");
+ }
+
+ @Test
public void set_component() {
IssueChangeNotification result = notification.setComponent(new ComponentDto().setDbKey("MyService").setLongName("My Service"));
assertThat(result.getFieldValue("componentName")).isEqualTo("My Service");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangesEmailTemplateTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangesEmailTemplateTest.java
index b48a6aaad67..c608abfe57e 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangesEmailTemplateTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/IssueChangesEmailTemplateTest.java
@@ -170,7 +170,7 @@ public class IssueChangesEmailTemplateTest {
Notification notification = new IssueChangeNotification()
.setChangeAuthorLogin("simon")
- .setProject("Struts", "org.apache:struts", null);
+ .setProject("Struts", "org.apache:struts", null, null);
EmailMessage message = underTest.format(notification);
assertThat(message.getFrom()).isEqualTo("Simon");
@@ -182,7 +182,7 @@ public class IssueChangesEmailTemplateTest {
Notification notification = new IssueChangeNotification()
.setChangeAuthorLogin("simon")
- .setProject("Struts", "org.apache:struts", null);
+ .setProject("Struts", "org.apache:struts", null, null);
EmailMessage message = underTest.format(notification);
assertThat(message.getFrom()).isEqualTo("simon");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java
index bfaff09a8b6..819bdc2f9bf 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/notification/NewIssuesNotificationTest.java
@@ -83,7 +83,7 @@ public class NewIssuesNotificationTest {
@Test
public void set_project_without_branch() {
- underTest.setProject("project-key", "project-long-name", null);
+ underTest.setProject("project-key", "project-long-name", null, null);
assertThat(underTest.getFieldValue(NewIssuesEmailTemplate.FIELD_PROJECT_NAME)).isEqualTo("project-long-name");
assertThat(underTest.getFieldValue(NewIssuesEmailTemplate.FIELD_PROJECT_KEY)).isEqualTo("project-key");
@@ -92,7 +92,7 @@ public class NewIssuesNotificationTest {
@Test
public void set_project_with_branch() {
- underTest.setProject("project-key", "project-long-name", "feature");
+ underTest.setProject("project-key", "project-long-name", "feature", null);
assertThat(underTest.getFieldValue(NewIssuesEmailTemplate.FIELD_PROJECT_NAME)).isEqualTo("project-long-name");
assertThat(underTest.getFieldValue(NewIssuesEmailTemplate.FIELD_PROJECT_KEY)).isEqualTo("project-key");
@@ -100,6 +100,15 @@ public class NewIssuesNotificationTest {
}
@Test
+ public void set_project_with_pull_request() {
+ underTest.setProject("project-key", "project-long-name", null, "pr-123");
+
+ assertThat(underTest.getFieldValue(NewIssuesEmailTemplate.FIELD_PROJECT_NAME)).isEqualTo("project-long-name");
+ assertThat(underTest.getFieldValue(NewIssuesEmailTemplate.FIELD_PROJECT_KEY)).isEqualTo("project-key");
+ assertThat(underTest.getFieldValue(NewIssuesEmailTemplate.FIELD_PULL_REQUEST)).isEqualTo("pr-123");
+ }
+
+ @Test
public void set_project_version() {
String version = randomAlphanumeric(5);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java
index 3b7f3fdce8d..538917daf43 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionComponentsTest.java
@@ -68,6 +68,8 @@ import static org.sonar.api.utils.DateUtils.addDays;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
import static org.sonar.core.util.Uuids.UUID_EXAMPLE_02;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
+import static org.sonar.db.component.BranchType.SHORT;
import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newModuleDto;
@@ -78,6 +80,7 @@ import static org.sonar.db.issue.IssueTesting.newIssue;
import static org.sonarqube.ws.client.component.ComponentsWsParameters.PARAM_BRANCH;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_COMPONENT_KEYS;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PROJECT_KEYS;
+import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_PULL_REQUEST;
import static org.sonarqube.ws.client.issue.IssuesWsParameters.PARAM_SINCE_LEAK_PERIOD;
public class SearchActionComponentsTest {
@@ -841,7 +844,7 @@ public class SearchActionComponentsTest {
userSession.addProjectPermission(UserRole.USER, project);
ComponentDto projectFile = db.components().insertComponent(newFileDto(project));
IssueDto projectIssue = db.issues().insertIssue(newIssue(rule, project, projectFile));
- ComponentDto branch = db.components().insertProjectBranch(project);
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(SHORT));
ComponentDto branchFile = db.components().insertComponent(newFileDto(branch));
IssueDto branchIssue = db.issues().insertIssue(newIssue(rule, branch, branchFile));
allowAnyoneOnProjects(project);
@@ -863,6 +866,34 @@ public class SearchActionComponentsTest {
}
@Test
+ public void search_by_pull_request() {
+ RuleDefinitionDto rule = db.rules().insert();
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.addProjectPermission(UserRole.USER, project);
+ ComponentDto projectFile = db.components().insertComponent(newFileDto(project));
+ IssueDto projectIssue = db.issues().insertIssue(newIssue(rule, project, projectFile));
+ ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST));
+ ComponentDto pullRequestFile = db.components().insertComponent(newFileDto(pullRequest));
+ IssueDto pullRequestIssue = db.issues().insertIssue(newIssue(rule, pullRequest, pullRequestFile));
+ allowAnyoneOnProjects(project);
+ indexIssuesAndViews();
+
+ SearchWsResponse result = ws.newRequest()
+ .setParam(PARAM_COMPONENT_KEYS, pullRequest.getKey())
+ .setParam(PARAM_PULL_REQUEST, pullRequest.getPullRequest())
+ .executeProtobuf(SearchWsResponse.class);
+
+ assertThat(result.getIssuesList())
+ .extracting(Issue::getKey, Issue::getComponent, Issue::getPullRequest)
+ .containsExactlyInAnyOrder(tuple(pullRequestIssue.getKey(), pullRequestFile.getKey(), pullRequestFile.getPullRequest()));
+ assertThat(result.getComponentsList())
+ .extracting(Issues.Component::getKey, Issues.Component::getPullRequest)
+ .containsExactlyInAnyOrder(
+ tuple(pullRequestFile.getKey(), pullRequestFile.getPullRequest()),
+ tuple(pullRequest.getKey(), pullRequest.getPullRequest()));
+ }
+
+ @Test
public void search_using_main_branch_name() {
RuleDefinitionDto rule = db.rules().insert();
ComponentDto project = db.components().insertMainBranch();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
index df3f8b2056a..b8283b57c8b 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SearchActionTest.java
@@ -135,7 +135,7 @@ public class SearchActionTest {
assertThat(def.params()).extracting("key").containsExactlyInAnyOrder(
"additionalFields", "asc", "assigned", "assignees", "authors", "componentKeys", "componentRootUuids", "componentRoots", "componentUuids", "components", "branch",
- "organization",
+ "pullRequest", "organization",
"createdAfter", "createdAt", "createdBefore", "createdInLast", "directories", "facetMode", "facets", "fileUuids", "issues", "languages", "moduleUuids", "onComponentOnly",
"p", "projectUuids", "projects", "ps", "resolutions", "resolved", "rules", "s", "severities", "sinceLeakPeriod",
"statuses", "tags", "types");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/live/LiveMeasureComputerImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/live/LiveMeasureComputerImplTest.java
index 37bc2431355..4aff277f099 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/measure/live/LiveMeasureComputerImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/measure/live/LiveMeasureComputerImplTest.java
@@ -332,7 +332,7 @@ public class LiveMeasureComputerImplTest {
markProjectAsAnalyzed(project);
db.measures().insertLiveMeasure(project, alertStatusMetric, m -> m.setData(Metric.Level.WARN.name()));
db.measures().insertLiveMeasure(project, intMetric, m -> m.setVariation(42.0).setValue(null));
- BranchDto branch = db.getDbClient().branchDao().selectByKey(db.getSession(), project.projectUuid(), "master")
+ BranchDto branch = db.getDbClient().branchDao().selectByBranchKey(db.getSession(), project.projectUuid(), "master")
.orElseThrow(() -> new IllegalStateException("Can't find master branch"));
List<QGChangeEvent> result = run(file1, newQualifierBasedIntLeakFormula());
diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/live/LiveQualityGateComputerImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/live/LiveQualityGateComputerImplTest.java
index 945165d987a..602d3418906 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/measure/live/LiveQualityGateComputerImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/measure/live/LiveQualityGateComputerImplTest.java
@@ -80,6 +80,18 @@ public class LiveQualityGateComputerImplTest {
}
@Test
+ public void loadQualityGate_returns_hardcoded_gate_for_pull_requests() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertPublicProject(organization);
+ BranchDto pullRequest = newBranchDto(project).setBranchType(BranchType.PULL_REQUEST);
+ db.components().insertProjectBranch(project, pullRequest);
+
+ QualityGate result = underTest.loadQualityGate(db.getSession(), organization, project, pullRequest);
+
+ assertThat(result).isSameAs(ShortLivingBranchQualityGate.GATE);
+ }
+
+ @Test
public void loadQualityGate_on_long_branch_returns_organization_default_gate() {
OrganizationDto organization = db.organizations().insert();
ComponentDto project = db.components().insertPublicProject(organization);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java
index 3b1542d0b7d..b900442a8ec 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentActionTest.java
@@ -50,8 +50,10 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.api.web.UserRole.USER;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newProjectCopy;
+import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.computation.task.projectanalysis.metric.Metric.MetricType.INT;
import static org.sonar.test.JsonAssert.assertJson;
import static org.sonar.server.component.ws.MeasuresWsParameters.DEPRECATED_PARAM_COMPONENT_ID;
@@ -78,7 +80,7 @@ public class ComponentActionTest {
assertThat(def.since()).isEqualTo("5.4");
assertThat(def.params()).extracting(Param::key)
- .containsExactlyInAnyOrder("componentId", "component", "branch", "metricKeys", "additionalFields", "developerId", "developerKey");
+ .containsExactlyInAnyOrder("componentId", "component", "branch", "pullRequest", "metricKeys", "additionalFields", "developerId", "developerKey");
assertThat(def.param("developerId").deprecatedSince()).isEqualTo("6.4");
assertThat(def.param("developerKey").deprecatedSince()).isEqualTo("6.4");
assertThat(def.param("componentId").deprecatedSince()).isEqualTo("6.6");
@@ -143,6 +145,29 @@ public class ComponentActionTest {
}
@Test
+ public void pull_request() {
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.addProjectPermission(UserRole.USER, project);
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("pr-123").setBranchType(PULL_REQUEST));
+ SnapshotDto analysis = db.components().insertSnapshot(branch);
+ ComponentDto file = db.components().insertComponent(newFileDto(branch));
+ MetricDto complexity = db.measures().insertMetric(m1 -> m1.setKey("complexity").setValueType(INT.name()));
+ LiveMeasureDto measure = db.measures().insertLiveMeasure(file, complexity, m -> m.setValue(12.0d).setVariation(2.0d));
+
+ ComponentWsResponse response = ws.newRequest()
+ .setParam(PARAM_COMPONENT, file.getKey())
+ .setParam(PARAM_PULL_REQUEST, "pr-123")
+ .setParam(PARAM_METRIC_KEYS, complexity.getKey())
+ .executeProtobuf(ComponentWsResponse.class);
+
+ assertThat(response.getComponent()).extracting(Component::getKey, Component::getPullRequest)
+ .containsExactlyInAnyOrder(file.getKey(), "pr-123");
+ assertThat(response.getComponent().getMeasuresList())
+ .extracting(Measures.Measure::getMetric, m -> parseDouble(m.getValue()))
+ .containsExactlyInAnyOrder(tuple(complexity.getKey(), measure.getValue()));
+ }
+
+ @Test
public void reference_uuid_in_the_response() {
userSession.logIn().setRoot();
ComponentDto project = db.components().insertPrivateProject();
@@ -351,7 +376,7 @@ public class ComponentActionTest {
db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("'componentId' and 'branch' parameters cannot be used at the same time");
+ expectedException.expectMessage("Parameter 'componentId' cannot be used at the same time as 'branch' or 'pullRequest'");
ws.newRequest()
.setParam(DEPRECATED_PARAM_COMPONENT_ID, file.uuid())
diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java
index 36e71bc488d..cc42a73db20 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/ComponentTreeActionTest.java
@@ -68,6 +68,7 @@ import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
import static org.sonar.api.server.ws.WebService.Param.SORT;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.api.web.UserRole.USER;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentTesting.newDirectory;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newProjectCopy;
@@ -82,6 +83,7 @@ import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_KE
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_PERIOD_SORT;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_SORT;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_METRIC_SORT_FILTER;
+import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_QUALIFIERS;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_STRATEGY;
import static org.sonar.server.measure.ws.ComponentTreeAction.LEAVES_STRATEGY;
@@ -490,6 +492,29 @@ public class ComponentTreeActionTest {
}
@Test
+ public void pull_request() {
+ OrganizationDto organization = db.organizations().insert();
+ ComponentDto project = db.components().insertPrivateProject(organization);
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("pr-123").setBranchType(PULL_REQUEST));
+ SnapshotDto analysis = db.components().insertSnapshot(branch);
+ ComponentDto file = db.components().insertComponent(newFileDto(branch));
+ MetricDto complexity = db.measures().insertMetric(m -> m.setValueType(INT.name()));
+ LiveMeasureDto measure = db.measures().insertLiveMeasure(file, complexity, m -> m.setValue(12.0d));
+
+ ComponentTreeWsResponse response = ws.newRequest()
+ .setParam(PARAM_COMPONENT, file.getKey())
+ .setParam(PARAM_PULL_REQUEST, "pr-123")
+ .setParam(PARAM_METRIC_KEYS, complexity.getKey())
+ .executeProtobuf(Measures.ComponentTreeWsResponse.class);
+
+ assertThat(response.getBaseComponent()).extracting(Measures.Component::getKey, Measures.Component::getPullRequest)
+ .containsExactlyInAnyOrder(file.getKey(), "pr-123");
+ assertThat(response.getBaseComponent().getMeasuresList())
+ .extracting(Measures.Measure::getMetric, m -> parseDouble(m.getValue()))
+ .containsExactlyInAnyOrder(tuple(complexity.getKey(), measure.getValue()));
+ }
+
+ @Test
public void return_deprecated_id_in_the_response() {
ComponentDto project = db.components().insertPrivateProject();
SnapshotDto analysis = db.components().insertSnapshot(project);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchHistoryActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchHistoryActionTest.java
index f56b7acecc1..0b366d51a50 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchHistoryActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/measure/ws/SearchHistoryActionTest.java
@@ -62,12 +62,14 @@ import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.api.utils.DateUtils.formatDateTime;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.core.util.Protobuf.setNullable;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
import static org.sonar.db.component.SnapshotDto.STATUS_UNPROCESSED;
import static org.sonar.db.component.SnapshotTesting.newAnalysis;
import static org.sonar.db.measure.MeasureTesting.newMeasureDto;
import static org.sonar.db.metric.MetricTesting.newMetricDto;
+import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.test.JsonAssert.assertJson;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_BRANCH;
import static org.sonar.server.component.ws.MeasuresWsParameters.PARAM_COMPONENT;
@@ -288,6 +290,29 @@ public class SearchHistoryActionTest {
}
@Test
+ public void pull_request() {
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.addProjectPermission(UserRole.USER, project);
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("pr-123").setBranchType(PULL_REQUEST));
+ ComponentDto file = db.components().insertComponent(newFileDto(branch));
+ SnapshotDto analysis = db.components().insertSnapshot(branch);
+ MeasureDto measure = db.measures().insertMeasure(file, analysis, nclocMetric, m -> m.setValue(2d));
+
+ SearchHistoryResponse result = ws.newRequest()
+ .setParam(PARAM_COMPONENT, file.getKey())
+ .setParam(PARAM_PULL_REQUEST, "pr-123")
+ .setParam(PARAM_METRICS, "ncloc")
+ .executeProtobuf(SearchHistoryResponse.class);
+
+ assertThat(result.getMeasuresList()).extracting(HistoryMeasure::getMetric).hasSize(1);
+ HistoryMeasure historyMeasure = result.getMeasures(0);
+ assertThat(historyMeasure.getMetric()).isEqualTo(nclocMetric.getKey());
+ assertThat(historyMeasure.getHistoryList())
+ .extracting(m -> parseDouble(m.getValue()))
+ .containsExactlyInAnyOrder(measure.getValue());
+ }
+
+ @Test
public void fail_when_using_branch_db_key() throws Exception {
OrganizationDto organization = db.organizations().insert();
ComponentDto project = db.components().insertMainBranch(organization);
@@ -372,7 +397,7 @@ public class SearchHistoryActionTest {
assertThat(definition.isPost()).isFalse();
assertThat(definition.isInternal()).isFalse();
assertThat(definition.since()).isEqualTo("6.3");
- assertThat(definition.params()).hasSize(7);
+ assertThat(definition.params()).hasSize(8);
Param branch = definition.param("branch");
assertThat(branch.since()).isEqualTo("6.6");
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ws/SearchActionTest.java
index adf30f7b3b6..e5245cb7492 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ws/SearchActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/projectanalysis/ws/SearchActionTest.java
@@ -54,9 +54,11 @@ import static org.sonar.api.utils.DateUtils.formatDate;
import static org.sonar.api.utils.DateUtils.formatDateTime;
import static org.sonar.api.utils.DateUtils.parseDateTime;
import static org.sonar.core.util.Protobuf.setNullable;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.component.SnapshotTesting.newAnalysis;
import static org.sonar.db.event.EventTesting.newEvent;
+import static org.sonar.server.projectanalysis.ws.ProjectAnalysesWsParameters.PARAM_PULL_REQUEST;
import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
import static org.sonar.test.JsonAssert.assertJson;
import static org.sonarqube.ws.client.WsRequest.Method.POST;
@@ -357,6 +359,24 @@ public class SearchActionTest {
}
@Test
+ public void pull_request() {
+ ComponentDto project = db.components().insertPrivateProject();
+ userSession.addProjectPermission(UserRole.USER, project);
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setKey("pr-123").setBranchType(PULL_REQUEST));
+ SnapshotDto analysis = db.components().insertSnapshot(newAnalysis(branch));
+ EventDto event = db.events().insertEvent(newEvent(analysis).setCategory(EventCategory.QUALITY_GATE.getLabel()));
+
+ List<Analysis> result = call(SearchRequest.builder()
+ .setProject(project.getKey())
+ .setPullRequest("pr-123")
+ .build())
+ .getAnalysesList();
+
+ assertThat(result).extracting(Analysis::getKey).containsExactlyInAnyOrder(analysis.getUuid());
+ assertThat(result.get(0).getEventsList()).extracting(Event::getKey).containsExactlyInAnyOrder(event.getUuid());
+ }
+
+ @Test
public void empty_response() {
ComponentDto project = db.components().insertPrivateProject();
userSession.addProjectPermission(UserRole.USER, project);
@@ -422,7 +442,7 @@ public class SearchActionTest {
assertThat(definition.responseExampleAsString()).isNotEmpty();
assertThat(definition.param("project").isRequired()).isTrue();
assertThat(definition.param("category")).isNotNull();
- assertThat(definition.params()).hasSize(7);
+ assertThat(definition.params()).hasSize(8);
Param from = definition.param("from");
assertThat(from.since()).isEqualTo("6.5");
@@ -451,6 +471,7 @@ public class SearchActionTest {
.setMethod(POST.name());
setNullable(wsRequest.getProject(), project -> request.setParam(PARAM_PROJECT, project));
setNullable(wsRequest.getBranch(), branch -> request.setParam(PARAM_BRANCH, branch));
+ setNullable(wsRequest.getPullRequest(), branch -> request.setParam(PARAM_PULL_REQUEST, branch));
setNullable(wsRequest.getCategory(), category -> request.setParam(PARAM_CATEGORY, category.name()));
setNullable(wsRequest.getPage(), page -> request.setParam(Param.PAGE, String.valueOf(page)));
setNullable(wsRequest.getPageSize(), pageSize -> request.setParam(Param.PAGE_SIZE, String.valueOf(pageSize)));
diff --git a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java
index 1effcf08f68..43a40176670 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/qualitygate/changeevent/QGChangeEventListenersImplTest.java
@@ -43,12 +43,15 @@ import org.sonar.api.utils.log.LoggerLevel;
import org.sonar.core.issue.DefaultIssue;
import org.sonar.db.component.ComponentDto;
import org.sonar.server.qualitygate.changeevent.QGChangeEventListener.ChangedIssue;
+import org.sonar.server.qualitygate.changeevent.QGChangeEventListenersImpl.ChangedIssueImpl;
+import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.tuple;
+import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.doThrow;
@@ -224,20 +227,51 @@ public class QGChangeEventListenersImplTest {
verifyNoMoreInteractions(listener1, listener2, listener3);
}
+ @Test
+ public void test_status_mapping() {
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_OPEN))).isEqualTo(QGChangeEventListener.Status.OPEN);
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_REOPENED))).isEqualTo(QGChangeEventListener.Status.REOPENED);
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_CONFIRMED))).isEqualTo(QGChangeEventListener.Status.CONFIRMED);
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FALSE_POSITIVE)))
+ .isEqualTo(QGChangeEventListener.Status.RESOLVED_FP);
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_WONT_FIX)))
+ .isEqualTo(QGChangeEventListener.Status.RESOLVED_WF);
+ assertThat(ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_FIXED)))
+ .isEqualTo(QGChangeEventListener.Status.RESOLVED_FIXED);
+ try {
+ ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_CLOSED));
+ fail("Expected exception");
+ } catch (Exception e) {
+ assertThat(e).hasMessage("Unexpected status: CLOSED");
+ }
+ try {
+ ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED));
+ fail("Expected exception");
+ } catch (Exception e) {
+ assertThat(e).hasMessage("A resolved issue should have a resolution");
+ }
+ try {
+ ChangedIssueImpl.statusOf(new DefaultIssue().setStatus(Issue.STATUS_RESOLVED).setResolution(Issue.RESOLUTION_REMOVED));
+ fail("Expected exception");
+ } catch (Exception e) {
+ assertThat(e).hasMessage("Unexpected resolution for a resolved issue: REMOVED");
+ }
+ }
+
private void verifyListenerCalled(QGChangeEventListener listener, QGChangeEvent changeEvent, DefaultIssue... issues) {
ArgumentCaptor<Set<ChangedIssue>> changedIssuesCaptor = newSetCaptor();
verify(listener).onIssueChanges(same(changeEvent), changedIssuesCaptor.capture());
Set<ChangedIssue> changedIssues = changedIssuesCaptor.getValue();
Tuple[] expected = Arrays.stream(issues)
- .map(issue -> tuple(issue.key(), issue.status(), issue.type()))
+ .map(issue -> tuple(issue.key(), ChangedIssueImpl.statusOf(issue), issue.type()))
.toArray(Tuple[]::new);
assertThat(changedIssues)
.hasSize(issues.length)
- .extracting(ChangedIssue::getKey, t -> t.getStatus().name(), ChangedIssue::getType)
+ .extracting(ChangedIssue::getKey, t -> t.getStatus(), ChangedIssue::getType)
.containsOnly(expected);
}
- private static final String[] STATUSES = Issue.STATUSES.stream().toArray(String[]::new);
+ private static final String[] POSSIBLE_STATUSES = asList(Issue.STATUS_CONFIRMED, Issue.STATUS_REOPENED, Issue.STATUS_RESOLVED).stream().toArray(String[]::new);
private static int issueIdCounter = 0;
private static DefaultIssue newDefaultIssue(String projectUuid) {
@@ -245,10 +279,23 @@ public class QGChangeEventListenersImplTest {
defaultIssue.setKey("issue_" + issueIdCounter++);
defaultIssue.setProjectUuid(projectUuid);
defaultIssue.setType(RuleType.values()[new Random().nextInt(RuleType.values().length)]);
- defaultIssue.setStatus(STATUSES[new Random().nextInt(STATUSES.length)]);
+ defaultIssue.setStatus(POSSIBLE_STATUSES[new Random().nextInt(POSSIBLE_STATUSES.length)]);
+ String[] possibleResolutions = possibleResolutions(defaultIssue.getStatus());
+ if (possibleResolutions.length > 0) {
+ defaultIssue.setResolution(possibleResolutions[new Random().nextInt(possibleResolutions.length)]);
+ }
return defaultIssue;
}
+ private static String[] possibleResolutions(String status) {
+ switch (status) {
+ case Issue.STATUS_RESOLVED:
+ return new String[] {Issue.RESOLUTION_FALSE_POSITIVE, Issue.RESOLUTION_WONT_FIX};
+ default:
+ return new String[0];
+ }
+ }
+
private static ComponentDto newComponentDto(String uuid) {
ComponentDto componentDto = new ComponentDto();
componentDto.setUuid(uuid);
diff --git a/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ListDefinitionsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ListDefinitionsActionTest.java
index b297d9612ff..49ab397b29c 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ListDefinitionsActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ListDefinitionsActionTest.java
@@ -456,7 +456,7 @@ public class ListDefinitionsActionTest {
assertThat(action.isInternal()).isFalse();
assertThat(action.isPost()).isFalse();
assertThat(action.responseExampleAsString()).isNotEmpty();
- assertThat(action.params()).extracting(Param::key).containsExactlyInAnyOrder("component", "branch");
+ assertThat(action.params()).extracting(Param::key).containsExactlyInAnyOrder("component", "branch", "pullRequest");
}
@Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ResetActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ResetActionTest.java
index 6f7d06cf48e..320e37ea849 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ResetActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ResetActionTest.java
@@ -238,7 +238,7 @@ public class ResetActionTest {
assertThat(action.isInternal()).isFalse();
assertThat(action.isPost()).isTrue();
assertThat(action.responseExampleAsString()).isNull();
- assertThat(action.params()).extracting(Param::key).containsExactlyInAnyOrder("keys", "component", "branch");
+ assertThat(action.params()).extracting(Param::key).containsExactlyInAnyOrder("keys", "component", "branch", "pullRequest");
}
@Test
diff --git a/server/sonar-server/src/test/java/org/sonar/server/setting/ws/SetActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/setting/ws/SetActionTest.java
index 82b5ca6026c..8dee11d1b92 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/setting/ws/SetActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/setting/ws/SetActionTest.java
@@ -1015,7 +1015,7 @@ public class SetActionTest {
assertThat(definition.isInternal()).isFalse();
assertThat(definition.since()).isEqualTo("6.1");
assertThat(definition.params()).extracting(Param::key)
- .containsOnly("key", "value", "values", "fieldValues", "component", "branch");
+ .containsOnly("key", "value", "values", "fieldValues", "component", "branch", "pullRequest");
Param branch = definition.param("branch");
assertThat(branch.isInternal()).isTrue();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ValuesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ValuesActionTest.java
index 5ee08886851..b2d1e54a598 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ValuesActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/setting/ws/ValuesActionTest.java
@@ -871,7 +871,7 @@ public class ValuesActionTest {
assertThat(action.isInternal()).isFalse();
assertThat(action.isPost()).isFalse();
assertThat(action.responseExampleAsString()).isNotEmpty();
- assertThat(action.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("keys", "component", "branch");
+ assertThat(action.params()).extracting(WebService.Param::key).containsExactlyInAnyOrder("keys", "component", "branch", "pullRequest");
}
private ValuesWsResponse executeRequestForComponentProperties(ComponentDto componentDto, String... keys) {
diff --git a/server/sonar-server/src/test/java/org/sonar/server/source/ws/LinesActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/source/ws/LinesActionTest.java
index 30e5de83212..25cfea083de 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/source/ws/LinesActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/source/ws/LinesActionTest.java
@@ -47,6 +47,7 @@ import static java.lang.String.format;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentTesting.newFileDto;
public class LinesActionTest {
@@ -64,14 +65,14 @@ public class LinesActionTest {
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();
- SourceService sourceService;
- HtmlSourceDecorator htmlSourceDecorator;
- ComponentDao componentDao;
+ private SourceService sourceService;
+ private HtmlSourceDecorator htmlSourceDecorator;
+ private ComponentDao componentDao;
- ComponentDto project;
- ComponentDto file;
+ private ComponentDto project;
+ private ComponentDto file;
- WsTester wsTester;
+ private WsTester wsTester;
@Before
public void setUp() {
@@ -142,6 +143,26 @@ public class LinesActionTest {
}
@Test
+ public void pull_request() throws Exception {
+ ComponentDto project = db.components().insertMainBranch();
+ userSession.addProjectPermission(UserRole.USER, project);
+ ComponentDto branch = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST));
+ ComponentDto file = db.components().insertComponent(newFileDto(branch));
+ db.getDbClient().fileSourceDao().insert(db.getSession(), new FileSourceDto()
+ .setProjectUuid(branch.uuid())
+ .setFileUuid(file.uuid())
+ .setSourceData(FileSourceTesting.newFakeData(3).build()));
+ db.commit();
+ userSession.logIn("login").addProjectPermission(UserRole.CODEVIEWER, project, file);
+
+ WsTester.TestRequest request = wsTester.newGetRequest("api/sources", "lines")
+ .setParam("key", file.getKey())
+ .setParam("pullRequest", file.getPullRequest());
+
+ request.execute().assertJson(getClass(), "show_source.json");
+ }
+
+ @Test
public void fail_when_no_uuid_or_key_param() throws Exception {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Either 'uuid' or 'key' must be provided");
@@ -285,7 +306,7 @@ public class LinesActionTest {
db.components().insertProjectBranch(project, b -> b.setKey("my_branch"));
expectedException.expect(IllegalArgumentException.class);
- expectedException.expectMessage("'uuid' and 'branch' parameters cannot be used at the same time");
+ expectedException.expectMessage("Parameter 'uuid' cannot be used at the same time as 'branch' or 'pullRequest'");
wsTester.newGetRequest("api/sources", "lines")
.setParam("uuid", file.uuid())
diff --git a/server/sonar-server/src/test/java/org/sonar/server/source/ws/SourcesWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/source/ws/SourcesWsTest.java
index 9518ba177e2..bfe7f515dc3 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/source/ws/SourcesWsTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/source/ws/SourcesWsTest.java
@@ -64,7 +64,7 @@ public class SourcesWsTest {
assertThat(raw.since()).isEqualTo("5.0");
assertThat(raw.isInternal()).isFalse();
assertThat(raw.responseExampleAsString()).isNotEmpty();
- assertThat(raw.params()).hasSize(2);
+ assertThat(raw.params()).hasSize(3);
WebService.Action lines = controller.action("lines");
assertThat(lines).isNotNull();
@@ -72,7 +72,7 @@ public class SourcesWsTest {
assertThat(lines.since()).isEqualTo("5.0");
assertThat(lines.isInternal()).isTrue();
assertThat(lines.responseExampleAsString()).isNotEmpty();
- assertThat(lines.params()).hasSize(5);
+ assertThat(lines.params()).hasSize(6);
WebService.Action hash = controller.action("hash");
assertThat(hash).isNotNull();
diff --git a/server/sonar-server/src/test/java/org/sonar/server/test/ws/ListActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/test/ws/ListActionTest.java
index 59491622a70..d99241c41a9 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/test/ws/ListActionTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/test/ws/ListActionTest.java
@@ -52,9 +52,11 @@ import static org.assertj.core.api.Assertions.tuple;
import static org.sonar.api.resources.Qualifiers.UNIT_TEST_FILE;
import static org.sonar.api.web.UserRole.CODEVIEWER;
import static org.sonar.api.web.UserRole.USER;
+import static org.sonar.db.component.BranchType.PULL_REQUEST;
import static org.sonar.db.component.ComponentTesting.newFileDto;
import static org.sonar.db.protobuf.DbFileSources.Test.TestStatus.OK;
import static org.sonar.server.test.db.TestTesting.newTest;
+import static org.sonar.server.test.ws.ListAction.PARAM_PULL_REQUEST;
import static org.sonar.server.test.ws.ListAction.SOURCE_FILE_ID;
import static org.sonar.server.test.ws.ListAction.SOURCE_FILE_KEY;
import static org.sonar.server.test.ws.ListAction.SOURCE_FILE_LINE_NUMBER;
@@ -99,7 +101,7 @@ public class ListActionTest {
assertThat(action.isPost()).isFalse();
assertThat(action.handler()).isNotNull();
assertThat(action.responseExampleAsString()).isNotEmpty();
- assertThat(action.params()).hasSize(9);
+ assertThat(action.params()).hasSize(10);
assertThat(action.description()).isEqualTo("Get the list of tests either in a test file or that test a given line of source code.<br /> " +
"Requires 'Browse' permission on the file's project.<br /> " +
"One (and only one) of the following combination of parameters must be provided: " +
@@ -209,6 +211,29 @@ public class ListActionTest {
}
@Test
+ public void list_tests_by_test_file_key_and_pull_request() {
+ ComponentDto project = db.components().insertMainBranch();
+ userSessionRule.addProjectPermission(CODEVIEWER, project);
+ ComponentDto pullRequest = db.components().insertProjectBranch(project, b -> b.setBranchType(PULL_REQUEST));
+ ComponentDto mainFile = db.components().insertComponent(newFileDto(pullRequest));
+ ComponentDto testFile = db.components().insertComponent(newFileDto(pullRequest).setQualifier(UNIT_TEST_FILE));
+
+ DbFileSources.Test test1 = newTest(mainFile, 10).build();
+ DbFileSources.Test test2 = newTest(mainFile, 11).build();
+ insertTests(testFile, test1, test2);
+
+ ListResponse request = call(ws.newRequest()
+ .setParam(TEST_FILE_KEY, testFile.getKey())
+ .setParam(PARAM_PULL_REQUEST, testFile.getPullRequest()));
+
+ assertThat(request.getTestsList())
+ .extracting(Tests.Test::getId, Tests.Test::getFileKey, Tests.Test::getFilePullRequest)
+ .containsOnly(
+ tuple(test1.getUuid(), testFile.getKey(), testFile.getPullRequest()),
+ tuple(test2.getUuid(), testFile.getKey(), testFile.getPullRequest()));
+ }
+
+ @Test
public void list_tests_by_source_file_uuid_and_line_number() {
userSessionRule.addProjectPermission(CODEVIEWER, project);
ComponentDto anotherMainFile = db.components().insertComponent(newFileDto(project));
diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java
index 7415178a93d..be8df1f933e 100644
--- a/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java
+++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java
@@ -241,8 +241,8 @@ public class WebhookPayloadFactoryImplTest {
assertJson(payload.getJson())
.isSimilarTo("{" +
"\"branch\": {" +
- " \"name\": \"feature/foo\"" +
- " \"type\": \"SHORT\"" +
+ " \"name\": \"feature/foo\"," +
+ " \"type\": \"SHORT\"," +
" \"isMain\": false," +
" \"url\": \"http://foo/project/issues?branch=feature%2Ffoo&id=P1&resolved=false\"" +
"}" +
@@ -250,6 +250,23 @@ public class WebhookPayloadFactoryImplTest {
}
@Test
+ public void create_payload_on_pull_request() {
+ CeTask task = new CeTask("#1", CeTask.Status.SUCCESS);
+ ProjectAnalysis analysis = newAnalysis(task, null, new Branch(false, "pr/foo", Branch.Type.PULL_REQUEST), 1_500_000_000_000L, emptyMap());
+
+ WebhookPayload payload = underTest.create(analysis);
+ assertJson(payload.getJson())
+ .isSimilarTo("{" +
+ "\"branch\": {" +
+ " \"name\": \"pr/foo\"," +
+ " \"type\": \"PULL_REQUEST\"," +
+ " \"isMain\": false," +
+ " \"url\": \"http://foo/project/issues?pullRequest=pr%2Ffoo&id=P1&resolved=false\"" +
+ "}" +
+ "}");
+ }
+
+ @Test
public void create_without_ce_task() {
ProjectAnalysis analysis = newAnalysis(null, null, null, null, emptyMap());
diff --git a/sonar-core/src/main/java/org/sonar/core/config/ScannerProperties.java b/sonar-core/src/main/java/org/sonar/core/config/ScannerProperties.java
index 1cc3bddeef5..a9c3088f6fb 100644
--- a/sonar-core/src/main/java/org/sonar/core/config/ScannerProperties.java
+++ b/sonar-core/src/main/java/org/sonar/core/config/ScannerProperties.java
@@ -31,9 +31,14 @@ public class ScannerProperties {
public static final String BRANCHES_DOC_LINK = "https://redirect.sonarsource.com/doc/branches.html";
+ public static final String ORGANIZATION = "sonar.organization";
+
public static final String BRANCH_NAME = "sonar.branch.name";
public static final String BRANCH_TARGET = "sonar.branch.target";
- public static final String ORGANIZATION = "sonar.organization";
+
+ public static final String PULL_REQUEST_KEY = "sonar.pullrequest.key";
+ public static final String PULL_REQUEST_BRANCH = "sonar.pullrequest.branch";
+ public static final String PULL_REQUEST_BASE = "sonar.pullrequest.base";
public static final String LINKS_SOURCES_DEV = "sonar.links.scm_dev";
@@ -70,8 +75,20 @@ public class ScannerProperties {
PropertyDefinition.builder(BRANCH_TARGET)
.name("Optional name of target branch to merge into")
.description(
- "Defines what is the target branch of the branch being analyzed. The main branch cannot have a target. "
- + "If no target is defined for other branches, the main branch is used as a target.")
+ "Defines the target branch of the branch being analyzed. The main branch cannot have a target. "
+ + "If no target is defined, the main branch is used as the target.")
+ .hidden()
+ .build(),
+ PropertyDefinition.builder(PULL_REQUEST_BRANCH)
+ .name("Optional name of pull request")
+ .description("Provide a name for the pull request being analyzed. It might match an existing pull request of the project, otherwise a new pull request will be created.")
+ .hidden()
+ .build(),
+ PropertyDefinition.builder(PULL_REQUEST_BASE)
+ .name("Optional name of target branch to merge into")
+ .description(
+ "Defines the target branch of the pull request being analyzed. "
+ + "If no target is defined, the main branch is used as the target.")
.hidden()
.build());
}
diff --git a/sonar-core/src/test/java/org/sonar/core/config/CorePropertyDefinitionsTest.java b/sonar-core/src/test/java/org/sonar/core/config/CorePropertyDefinitionsTest.java
index a3cfb1e2bc5..b3054a31a22 100644
--- a/sonar-core/src/test/java/org/sonar/core/config/CorePropertyDefinitionsTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/config/CorePropertyDefinitionsTest.java
@@ -30,7 +30,7 @@ public class CorePropertyDefinitionsTest {
@Test
public void all() {
List<PropertyDefinition> defs = CorePropertyDefinitions.all();
- assertThat(defs).hasSize(57);
+ assertThat(defs).hasSize(59);
}
@Test
diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/ce/posttask/Branch.java b/sonar-plugin-api/src/main/java/org/sonar/api/ce/posttask/Branch.java
index c64ccaf898d..50c5f39e2fe 100644
--- a/sonar-plugin-api/src/main/java/org/sonar/api/ce/posttask/Branch.java
+++ b/sonar-plugin-api/src/main/java/org/sonar/api/ce/posttask/Branch.java
@@ -27,7 +27,7 @@ import java.util.Optional;
public interface Branch {
enum Type {
- LONG, SHORT
+ LONG, SHORT, PULL_REQUEST
}
boolean isMain();
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java
index 5d47999757e..b143361ec41 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/cpd/CpdExecutor.java
@@ -83,8 +83,8 @@ public class CpdExecutor {
}
public void execute() {
- if (branchConfiguration.isShortLivingBranch()) {
- LOG.info("Skipping CPD calculation for short living branch");
+ if (branchConfiguration.isShortOrPullRequest()) {
+ LOG.info("Skipping CPD calculation for short living branch and pull request");
return;
}
execute(TIMEOUT);
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java
index c1edd6f92f7..c36b8a196e6 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ComponentsPublisher.java
@@ -152,7 +152,8 @@ public class ComponentsPublisher implements ReportPublisherStep {
}
private boolean shouldSkipComponent(DefaultInputComponent component, Collection<InputComponent> children) {
- if (component instanceof InputModule && children.isEmpty() && branchConfiguration.isShortLivingBranch()) {
+ if (component instanceof InputModule && children.isEmpty()
+ && (branchConfiguration.isShortOrPullRequest())) {
// no children on a module in short branch analysis -> skip it (except root)
return !moduleHierarchy.isRoot((InputModule) component);
} else if (component instanceof InputDir && children.isEmpty()) {
@@ -165,7 +166,7 @@ public class ComponentsPublisher implements ReportPublisherStep {
} else if (component instanceof DefaultInputFile) {
// skip files not marked for publishing
DefaultInputFile inputFile = (DefaultInputFile) component;
- return !inputFile.isPublished() || (branchConfiguration.isShortLivingBranch() && inputFile.status() == Status.SAME);
+ return !inputFile.isPublished() || (branchConfiguration.isShortOrPullRequest() && inputFile.status() == Status.SAME);
}
return false;
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java
index 202d8edae8f..a17c691f8bb 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java
@@ -91,30 +91,13 @@ public class MetadataPublisher implements ReportPublisherStep {
settings.get(ORGANIZATION).ifPresent(builder::setOrganizationKey);
if (branchConfiguration.branchName() != null) {
- builder.setBranchName(branchConfiguration.branchName());
- builder.setBranchType(toProtobufBranchType(branchConfiguration.branchType()));
- String branchTarget = branchConfiguration.branchTarget();
- if (branchTarget != null) {
- builder.setMergeBranchName(branchTarget);
- }
+ addBranchInformation(builder);
}
+
Optional.ofNullable(rootProject.getBranch()).ifPresent(builder::setDeprecatedBranch);
if (scmConfiguration != null) {
- ScmProvider scmProvider = scmConfiguration.provider();
- if (scmProvider != null) {
- Path projectBasedir = moduleHierarchy.root().getBaseDir();
- try {
- builder.setRelativePathFromScmRoot(toSonarQubePath(scmProvider.relativePathFromScmRoot(projectBasedir)));
- } catch (UnsupportedOperationException e) {
- LOG.debug(e.getMessage());
- }
- try {
- builder.setScmRevisionId(scmProvider.revisionId(projectBasedir));
- } catch (UnsupportedOperationException e) {
- LOG.debug(e.getMessage());
- }
- }
+ addScmInformation(builder);
}
for (QProfile qp : qProfiles.findAll()) {
@@ -132,7 +115,40 @@ public class MetadataPublisher implements ReportPublisherStep {
writer.writeMetadata(builder.build());
}
+ private void addScmInformation(ScannerReport.Metadata.Builder builder) {
+ ScmProvider scmProvider = scmConfiguration.provider();
+ if (scmProvider != null) {
+ Path projectBasedir = moduleHierarchy.root().getBaseDir();
+ try {
+ builder.setRelativePathFromScmRoot(toSonarQubePath(scmProvider.relativePathFromScmRoot(projectBasedir)));
+ } catch (UnsupportedOperationException e) {
+ LOG.debug(e.getMessage());
+ }
+ try {
+ builder.setScmRevisionId(scmProvider.revisionId(projectBasedir));
+ } catch (UnsupportedOperationException e) {
+ LOG.debug(e.getMessage());
+ }
+ }
+ }
+
+ private void addBranchInformation(ScannerReport.Metadata.Builder builder) {
+ builder.setBranchName(branchConfiguration.branchName());
+ BranchType branchType = toProtobufBranchType(branchConfiguration.branchType());
+ builder.setBranchType(branchType);
+ String branchTarget = branchConfiguration.branchTarget();
+ if (branchTarget != null) {
+ builder.setMergeBranchName(branchTarget);
+ }
+ if (branchType == BranchType.PULL_REQUEST) {
+ builder.setPullRequestKey(branchConfiguration.pullRequestKey());
+ }
+ }
+
private static BranchType toProtobufBranchType(org.sonar.scanner.scan.branch.BranchType branchType) {
+ if (branchType == org.sonar.scanner.scan.branch.BranchType.PULL_REQUEST) {
+ return BranchType.PULL_REQUEST;
+ }
if (branchType == org.sonar.scanner.scan.branch.BranchType.LONG) {
return BranchType.LONG;
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java
index 58066ab0351..97b01015e2c 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/ReportPublisher.java
@@ -57,6 +57,7 @@ import org.sonarqube.ws.client.WsResponse;
import static org.sonar.core.config.ScannerProperties.BRANCH_NAME;
import static org.sonar.core.config.ScannerProperties.ORGANIZATION;
import static org.sonar.core.util.FileUtils.deleteQuietly;
+import static org.sonar.scanner.scan.branch.BranchType.PULL_REQUEST;
@ScannerSide
public class ReportPublisher implements Startable {
@@ -180,8 +181,12 @@ public class ReportPublisher implements Startable {
String branchName = branchConfiguration.branchName();
if (branchName != null) {
- post.setParam(CHARACTERISTIC, "branch=" + branchName);
- post.setParam(CHARACTERISTIC, "branchType=" + branchConfiguration.branchType().name());
+ if (branchConfiguration.branchType() != PULL_REQUEST) {
+ post.setParam(CHARACTERISTIC, "branch=" + branchName);
+ post.setParam(CHARACTERISTIC, "branchType=" + branchConfiguration.branchType().name());
+ } else {
+ post.setParam(CHARACTERISTIC, "pullRequest=" + branchConfiguration.pullRequestKey());
+ }
}
WsResponse response;
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java
index f63b163ae37..45b9e7f139e 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisher.java
@@ -55,7 +55,7 @@ public class TestExecutionAndCoveragePublisher implements ReportPublisherStep {
@Override
public void publish(ScannerReportWriter writer) {
- if (branchConfiguration.isShortLivingBranch()) {
+ if (branchConfiguration.isShortOrPullRequest()) {
return;
}
final ScannerReport.Test.Builder testBuilder = ScannerReport.Test.newBuilder();
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java
index 15a5b42076c..94233256871 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectReactorValidator.java
@@ -23,17 +23,26 @@ import com.google.common.base.Joiner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.stream.Stream;
import javax.annotation.Nullable;
-import org.apache.commons.lang.StringUtils;
import org.sonar.api.batch.AnalysisMode;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.bootstrap.ProjectReactor;
import org.sonar.api.utils.MessageException;
import org.sonar.core.component.ComponentKeys;
-import org.sonar.core.config.ScannerProperties;
import org.sonar.scanner.bootstrap.GlobalConfiguration;
import org.sonar.scanner.scan.branch.BranchParamsValidator;
+import static java.lang.String.format;
+import static java.util.Objects.nonNull;
+import static org.apache.commons.lang.StringUtils.isNotEmpty;
+import static org.sonar.core.config.ScannerProperties.BRANCHES_DOC_LINK;
+import static org.sonar.core.config.ScannerProperties.BRANCH_NAME;
+import static org.sonar.core.config.ScannerProperties.BRANCH_TARGET;
+import static org.sonar.core.config.ScannerProperties.PULL_REQUEST_BASE;
+import static org.sonar.core.config.ScannerProperties.PULL_REQUEST_BRANCH;
+import static org.sonar.core.config.ScannerProperties.PULL_REQUEST_KEY;
+
/**
* This class aims at validating project reactor
* @since 3.6
@@ -69,11 +78,11 @@ public class ProjectReactorValidator {
String deprecatedBranchName = reactor.getRoot().getBranch();
- if (branchParamsValidator != null) {
- // branch plugin is present
+ if (isBranchFeatureAvailable()) {
branchParamsValidator.validate(validationMessages, deprecatedBranchName);
} else {
validateBranchParamsWhenPluginAbsent(validationMessages);
+ validatePullRequestParamsWhenPluginAbsent(validationMessages);
}
validateBranch(validationMessages, deprecatedBranchName);
@@ -84,38 +93,49 @@ public class ProjectReactorValidator {
}
private void validateBranchParamsWhenPluginAbsent(List<String> validationMessages) {
- for (String param : Arrays.asList(ScannerProperties.BRANCH_NAME, ScannerProperties.BRANCH_TARGET)) {
- if (StringUtils.isNotEmpty(settings.get(param).orElse(null))) {
- validationMessages.add(String.format("To use the property \"%s\", the branch plugin is required but not installed. "
- + "See the documentation of branch support: %s.", param, ScannerProperties.BRANCHES_DOC_LINK));
+ for (String param : Arrays.asList(BRANCH_NAME, BRANCH_TARGET)) {
+ if (isNotEmpty(settings.get(param).orElse(null))) {
+ validationMessages.add(format("To use the property \"%s\", the branch plugin is required but not installed. "
+ + "See the documentation of branch support: %s.", param, BRANCHES_DOC_LINK));
}
}
}
+ private void validatePullRequestParamsWhenPluginAbsent(List<String> validationMessages) {
+ Stream.of(PULL_REQUEST_KEY, PULL_REQUEST_BRANCH, PULL_REQUEST_BASE)
+ .filter(param -> nonNull(settings.get(param).orElse(null)))
+ .forEach(param -> validationMessages.add(format("To use the property \"%s\", the branch plugin is required but not installed. "
+ + "See the documentation of branch support: %s.", param, BRANCHES_DOC_LINK)));
+ }
+
private static void validateModuleIssuesMode(ProjectDefinition moduleDef, List<String> validationMessages) {
if (!ComponentKeys.isValidModuleKeyIssuesMode(moduleDef.getKey())) {
- validationMessages.add(String.format("\"%s\" is not a valid project or module key. "
+ validationMessages.add(format("\"%s\" is not a valid project or module key. "
+ "Allowed characters in issues mode are alphanumeric, '-', '_', '.', '/' and ':', with at least one non-digit.", moduleDef.getKey()));
}
}
private static void validateModule(ProjectDefinition moduleDef, List<String> validationMessages) {
if (!ComponentKeys.isValidModuleKey(moduleDef.getKey())) {
- validationMessages.add(String.format("\"%s\" is not a valid project or module key. "
+ validationMessages.add(format("\"%s\" is not a valid project or module key. "
+ "Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", moduleDef.getKey()));
}
String originalVersion = moduleDef.getOriginalVersion();
if (originalVersion != null && originalVersion.length() > 100) {
- validationMessages.add(String.format("\"%s\" is not a valid version name for module \"%s\". " +
+ validationMessages.add(format("\"%s\" is not a valid version name for module \"%s\". " +
"The maximum length for version numbers is 100 characters.", originalVersion, moduleDef.getKey()));
}
}
private static void validateBranch(List<String> validationMessages, @Nullable String branch) {
- if (StringUtils.isNotEmpty(branch) && !ComponentKeys.isValidBranch(branch)) {
- validationMessages.add(String.format("\"%s\" is not a valid branch name. "
+ if (isNotEmpty(branch) && !ComponentKeys.isValidBranch(branch)) {
+ validationMessages.add(format("\"%s\" is not a valid branch name. "
+ "Allowed characters are alphanumeric, '-', '_', '.' and '/'.", branch));
}
}
+ private boolean isBranchFeatureAvailable() {
+ return branchParamsValidator != null;
+ }
+
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java
index b57cc8f10b2..3dd06bbab78 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java
@@ -20,6 +20,7 @@
package org.sonar.scanner.scan;
import com.google.common.annotations.VisibleForTesting;
+import javax.annotation.Nullable;
import org.apache.commons.lang.StringUtils;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.InstantiationStrategy;
@@ -91,6 +92,7 @@ import org.sonar.scanner.scan.branch.BranchConfiguration;
import org.sonar.scanner.scan.branch.BranchConfigurationProvider;
import org.sonar.scanner.scan.branch.BranchType;
import org.sonar.scanner.scan.branch.ProjectBranchesProvider;
+import org.sonar.scanner.scan.branch.ProjectPullRequestsProvider;
import org.sonar.scanner.scan.filesystem.BatchIdGenerator;
import org.sonar.scanner.scan.filesystem.InputComponentStoreProvider;
import org.sonar.scanner.scan.filesystem.StatusDetection;
@@ -146,6 +148,7 @@ public class ProjectScanContainer extends ComponentContainer {
new RulesProvider(),
new BranchConfigurationProvider(),
new ProjectBranchesProvider(),
+ new ProjectPullRequestsProvider(),
DefaultAnalysisMode.class,
new ProjectRepositoriesProvider(),
@@ -254,7 +257,13 @@ public class ProjectScanContainer extends ComponentContainer {
String branchName = props.property(ScannerProperties.BRANCH_NAME);
if (branchName != null) {
BranchConfiguration branchConfig = getComponentByType(BranchConfiguration.class);
- LOG.info("Branch name: {}, type: {}", branchName, toDisplayName(branchConfig.branchType()));
+ LOG.info("Branch name: {}, type: {}", branchName, branchTypeToDisplayName(branchConfig.branchType()));
+ }
+
+ String pullRequestBranch = props.property(ScannerProperties.PULL_REQUEST_BRANCH);
+ if (pullRequestBranch != null) {
+ String pullRequestBase = props.property(ScannerProperties.PULL_REQUEST_BASE);
+ LOG.info("Pull request into {}: {}", pullRequestBaseToDisplayName(pullRequestBase), pullRequestBranch);
}
LOG.debug("Start recursive analysis of project modules");
@@ -265,7 +274,11 @@ public class ProjectScanContainer extends ComponentContainer {
}
}
- private static String toDisplayName(BranchType branchType) {
+ private static String pullRequestBaseToDisplayName(@Nullable String pullRequestBase) {
+ return pullRequestBase != null ? pullRequestBase : "default branch";
+ }
+
+ private static String branchTypeToDisplayName(BranchType branchType) {
switch (branchType) {
case LONG:
return "long living";
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfiguration.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfiguration.java
index 4ddcff98abd..695c3c6d62d 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfiguration.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfiguration.java
@@ -36,8 +36,8 @@ public interface BranchConfiguration {
*/
BranchType branchType();
- default boolean isShortLivingBranch() {
- return branchType() == BranchType.SHORT;
+ default boolean isShortOrPullRequest() {
+ return branchType() == BranchType.PULL_REQUEST || branchType() == BranchType.SHORT;
}
/**
@@ -54,7 +54,19 @@ public interface BranchConfiguration {
/**
* The name of the base branch to determine project repository and changed files.
+ *
+ * Note: this is important for the scanner during the analysis of long living branches.
+ * For short living branches, branchBase is always the same as branchTarget.
+ * For long living branches, branchBase is the target in case of first analysis,
+ * otherwise it's the branch itself.
*/
@CheckForNull
String branchBase();
+
+ /**
+ * The key of the pull request.
+ *
+ * @throws IllegalStateException if this branch configuration is not a pull request.
+ */
+ String pullRequestKey();
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationLoader.java
index 24b57650ee9..5b845195d05 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationLoader.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationLoader.java
@@ -27,5 +27,5 @@ import org.sonar.api.batch.ScannerSide;
@ScannerSide
@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
public interface BranchConfigurationLoader {
- BranchConfiguration load(Map<String, String> localSettings, Supplier<Map<String, String>> remoteSettingsSupplier, ProjectBranches branches);
+ BranchConfiguration load(Map<String, String> localSettings, Supplier<Map<String, String>> remoteSettingsSupplier, ProjectBranches branches, ProjectPullRequests pullRequests);
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationProvider.java
index 08caee68ca2..276ba411c28 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationProvider.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchConfigurationProvider.java
@@ -39,14 +39,14 @@ public class BranchConfigurationProvider extends ProviderAdapter {
private BranchConfiguration branchConfiguration = null;
public BranchConfiguration provide(@Nullable BranchConfigurationLoader loader, GlobalConfiguration globalConfiguration, ProjectKey projectKey,
- SettingsLoader settingsLoader, ProjectBranches branches) {
+ SettingsLoader settingsLoader, ProjectBranches branches, ProjectPullRequests pullRequests) {
if (branchConfiguration == null) {
if (loader == null) {
branchConfiguration = new DefaultBranchConfiguration();
} else {
Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG);
Supplier<Map<String, String>> settingsSupplier = createSettingsSupplier(globalConfiguration, projectKey, settingsLoader);
- branchConfiguration = loader.load(globalConfiguration.getProperties(), settingsSupplier, branches);
+ branchConfiguration = loader.load(globalConfiguration.getProperties(), settingsSupplier, branches, pullRequests);
profiler.stopInfo();
}
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchType.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchType.java
index b90f633ded0..e8143ce4480 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchType.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/BranchType.java
@@ -20,5 +20,5 @@
package org.sonar.scanner.scan.branch;
public enum BranchType {
- SHORT, LONG
+ SHORT, LONG, PULL_REQUEST
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/DefaultBranchConfiguration.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/DefaultBranchConfiguration.java
index 045c3e3ccd4..2e048e6c689 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/DefaultBranchConfiguration.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/DefaultBranchConfiguration.java
@@ -46,4 +46,9 @@ public class DefaultBranchConfiguration implements BranchConfiguration {
public String branchBase() {
return null;
}
+
+ @Override
+ public String pullRequestKey() {
+ throw new IllegalStateException("Only a branch of type PULL_REQUEST can have a pull request id.");
+ }
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java
index 2a59148dbd0..043360fb64f 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectBranchesProvider.java
@@ -26,8 +26,6 @@ import org.sonar.api.batch.bootstrap.ProjectKey;
import org.sonar.api.utils.log.Logger;
import org.sonar.api.utils.log.Loggers;
import org.sonar.api.utils.log.Profiler;
-import org.sonar.core.config.ScannerProperties;
-import org.sonar.scanner.bootstrap.GlobalConfiguration;
public class ProjectBranchesProvider extends ProviderAdapter {
@@ -36,16 +34,19 @@ public class ProjectBranchesProvider extends ProviderAdapter {
private ProjectBranches branches = null;
- public ProjectBranches provide(@Nullable ProjectBranchesLoader loader, ProjectKey projectKey, GlobalConfiguration settings) {
- if (branches == null) {
- if (loader == null || !settings.get(ScannerProperties.BRANCH_NAME).isPresent()) {
- branches = new ProjectBranches(Collections.emptyList());
- } else {
- Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG);
- branches = loader.load(projectKey.get());
- profiler.stopInfo();
- }
+ public ProjectBranches provide(@Nullable ProjectBranchesLoader loader, ProjectKey projectKey) {
+ if (branches != null) {
+ return branches;
}
+
+ if (loader == null) {
+ branches = new ProjectBranches(Collections.emptyList());
+ return branches;
+ }
+
+ Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG);
+ branches = loader.load(projectKey.get());
+ profiler.stopInfo();
return branches;
}
}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequests.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequests.java
new file mode 100644
index 00000000000..a9f4c2c3d07
--- /dev/null
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequests.java
@@ -0,0 +1,49 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.scanner.scan.branch;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import javax.annotation.CheckForNull;
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * Container class for information about the pull requests of a project.
+ */
+@Immutable
+public class ProjectPullRequests {
+
+ private final Map<String, PullRequestInfo> pullRequestsById;
+
+ public ProjectPullRequests(List<PullRequestInfo> pullRequestsById) {
+ this.pullRequestsById = pullRequestsById.stream().collect(Collectors.toMap(PullRequestInfo::getBranch, Function.identity()));
+ }
+
+ @CheckForNull
+ public PullRequestInfo get(String branch) {
+ return pullRequestsById.get(branch);
+ }
+
+ public boolean isEmpty() {
+ return pullRequestsById.isEmpty();
+ }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsLoader.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsLoader.java
new file mode 100644
index 00000000000..525afd56f38
--- /dev/null
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsLoader.java
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.scanner.scan.branch;
+
+import org.sonar.api.batch.InstantiationStrategy;
+import org.sonar.api.batch.ScannerSide;
+
+@ScannerSide
+@InstantiationStrategy(InstantiationStrategy.PER_BATCH)
+public interface ProjectPullRequestsLoader {
+
+ /**
+ * Load the pull requests of a project.
+ */
+ ProjectPullRequests load(String projectKey);
+
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProvider.java
new file mode 100644
index 00000000000..15023c8c2fc
--- /dev/null
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProvider.java
@@ -0,0 +1,51 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.scanner.scan.branch;
+
+import java.util.Collections;
+import org.picocontainer.injectors.ProviderAdapter;
+import org.sonar.api.batch.bootstrap.ProjectKey;
+import org.sonar.api.utils.log.Logger;
+import org.sonar.api.utils.log.Loggers;
+import org.sonar.api.utils.log.Profiler;
+
+public class ProjectPullRequestsProvider extends ProviderAdapter {
+
+ private static final Logger LOG = Loggers.get(ProjectPullRequestsProvider.class);
+ private static final String LOG_MSG = "Load project pull requests";
+
+ private ProjectPullRequests pullRequests = null;
+
+ public ProjectPullRequests provide(@org.picocontainer.annotations.Nullable ProjectPullRequestsLoader loader, ProjectKey projectKey) {
+ if (pullRequests != null) {
+ return pullRequests;
+ }
+
+ if (loader == null) {
+ pullRequests = new ProjectPullRequests(Collections.emptyList());
+ return pullRequests;
+ }
+
+ Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG);
+ pullRequests = loader.load(projectKey.get());
+ profiler.stopInfo();
+ return pullRequests;
+ }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/PullRequestInfo.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/PullRequestInfo.java
new file mode 100644
index 00000000000..6ccc3656fb5
--- /dev/null
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/branch/PullRequestInfo.java
@@ -0,0 +1,53 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.scanner.scan.branch;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.Immutable;
+
+/**
+ * Container class for information about a pull request.
+ */
+@Immutable
+public class PullRequestInfo {
+ private final String id;
+ private final String branch;
+ private final String base;
+
+ public PullRequestInfo(String id, String branch, @Nullable String base) {
+ this.id = id;
+ this.branch = branch;
+ this.base = base;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ @CheckForNull
+ public String getBase() {
+ return base;
+ }
+}
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java
index 3ea00232847..e6d96e51733 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/filesystem/InputComponentStore.java
@@ -80,7 +80,8 @@ public class InputComponentStore {
return inputFileCache.values().stream()
.map(f -> (DefaultInputFile) f)
.filter(DefaultInputFile::isPublished)
- .filter(f -> (!branchConfiguration.isShortLivingBranch()) || f.status() != Status.SAME)::iterator;
+ .filter(f -> !branchConfiguration.isShortOrPullRequest() || f.status() != Status.SAME)
+ ::iterator;
}
public Iterable<InputFile> allFiles() {
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFilesProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFilesProvider.java
index 38e2416bfad..a04b4f5021a 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFilesProvider.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmChangedFilesProvider.java
@@ -62,7 +62,7 @@ public class ScmChangedFilesProvider extends ProviderAdapter {
@CheckForNull
private static Collection<Path> loadChangedFilesIfNeeded(ScmConfiguration scmConfiguration, BranchConfiguration branchConfiguration, Path rootBaseDir) {
- if (branchConfiguration.isShortLivingBranch() && branchConfiguration.branchTarget() != null) {
+ if (branchConfiguration.isShortOrPullRequest() && branchConfiguration.branchTarget() != null) {
ScmProvider scmProvider = scmConfiguration.provider();
if (scmProvider != null) {
Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG);
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java
index cbdafc68bcd..bb587269fd9 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmPublisher.java
@@ -103,7 +103,7 @@ public final class ScmPublisher {
}
if (configuration.forceReloadAll() || f.status() != Status.SAME) {
addIfNotEmpty(filesToBlame, f);
- } else if (!branchConfiguration.isShortLivingBranch()) {
+ } else if (!branchConfiguration.isShortOrPullRequest()) {
// File status is SAME so that mean fileData exists
FileData fileData = projectRepositories.fileData(inputModule.definition().getKeyWithBranch(), inputFile.getModuleRelativePath());
if (StringUtils.isEmpty(fileData.revision())) {
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java
index 6947f918d60..6972e241500 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorContext.java
@@ -148,7 +148,7 @@ public class DefaultSensorContext implements SensorContext {
@Override
public NewCoverage newCoverage() {
- if (branchConfiguration.isShortLivingBranch()) {
+ if (branchConfiguration.isShortOrPullRequest()) {
return NO_OP_NEW_COVERAGE;
}
return new DefaultCoverage(sensorStorage);
@@ -156,7 +156,7 @@ public class DefaultSensorContext implements SensorContext {
@Override
public NewCpdTokens newCpdTokens() {
- if (analysisMode.isIssues() || branchConfiguration.isShortLivingBranch()) {
+ if (analysisMode.isIssues() || branchConfiguration.isShortOrPullRequest()) {
return NO_OP_NEW_CPD_TOKENS;
}
return new DefaultCpdTokens(config, sensorStorage);
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
index 43d9198a06c..6b5a7137d94 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sensor/DefaultSensorStorage.java
@@ -355,7 +355,7 @@ public class DefaultSensorStorage implements SensorStorage {
}
private boolean shouldSkipStorage(DefaultInputFile defaultInputFile) {
- return branchConfiguration.isShortLivingBranch() && defaultInputFile.status() == InputFile.Status.SAME;
+ return branchConfiguration.isShortOrPullRequest() && defaultInputFile.status() == InputFile.Status.SAME;
}
/**
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java
index 8605ab5418d..f685e4f22cf 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/cpd/CpdExecutorTest.java
@@ -100,7 +100,18 @@ public class CpdExecutorTest {
@Test
public void skipIfShortBranch() {
- when(branchConfig.isShortLivingBranch()).thenReturn(true);
+ when(branchConfig.isShortOrPullRequest()).thenReturn(true);
+ index = mock(SonarCpdBlockIndex.class);
+ executor = new CpdExecutor(settings, index, publisher, componentStore, branchConfig);
+
+ executor.execute();
+
+ verifyZeroInteractions(index);
+ }
+
+ @Test
+ public void skip_if_pull_request() {
+ when(branchConfig.isShortOrPullRequest()).thenReturn(true);
index = mock(SonarCpdBlockIndex.class);
executor = new CpdExecutor(settings, index, publisher, componentStore, branchConfig);
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java
index 71d86e98de6..eb28ba1a3d0 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/mediumtest/ScannerMediumTester.java
@@ -72,6 +72,7 @@ import org.sonar.scanner.scan.branch.BranchConfiguration;
import org.sonar.scanner.scan.branch.BranchConfigurationLoader;
import org.sonar.scanner.scan.branch.BranchType;
import org.sonar.scanner.scan.branch.ProjectBranches;
+import org.sonar.scanner.scan.branch.ProjectPullRequests;
import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile;
import org.sonarqube.ws.Rules.ListResponse.Rule;
@@ -420,6 +421,11 @@ public class ScannerMediumTester extends ExternalResource {
public String branchBase() {
return branchBase;
}
+
+ @Override
+ public String pullRequestKey() {
+ throw new UnsupportedOperationException();
+ }
}
public ScannerMediumTester setBranchType(BranchType branchType) {
@@ -439,7 +445,7 @@ public class ScannerMediumTester extends ExternalResource {
private class FakeBranchConfigurationLoader implements BranchConfigurationLoader {
@Override
- public BranchConfiguration load(Map<String, String> localSettings, Supplier<Map<String, String>> settingsSupplier, ProjectBranches branches) {
+ public BranchConfiguration load(Map<String, String> localSettings, Supplier<Map<String, String>> settingsSupplier, ProjectBranches branches, ProjectPullRequests pullRequests) {
return branchConfiguration;
}
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java
index eda36629c96..1e84cff4434 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ComponentsPublisherTest.java
@@ -310,7 +310,77 @@ public class ComponentsPublisherTest {
@Test
public void skip_unchanged_components_in_short_branches() throws IOException {
- when(branchConfiguration.isShortLivingBranch()).thenReturn(true);
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
+ ProjectAnalysisInfo projectAnalysisInfo = mock(ProjectAnalysisInfo.class);
+ when(projectAnalysisInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12"));
+
+ Path moduleBaseDir = temp.newFolder().toPath();
+ ProjectDefinition rootDef = ProjectDefinition.create()
+ .setKey("foo")
+ .setProperty(CoreProperties.PROJECT_VERSION_PROPERTY, "1.0")
+ .setName("Root project")
+ .setDescription("Root description")
+ .setBaseDir(moduleBaseDir.toFile())
+ .setWorkDir(temp.newFolder());
+ DefaultInputModule root = new DefaultInputModule(rootDef, 1);
+
+ moduleHierarchy = mock(InputModuleHierarchy.class);
+ when(moduleHierarchy.root()).thenReturn(root);
+ when(moduleHierarchy.children(root)).thenReturn(Collections.emptyList());
+
+ // dir with changed files
+ DefaultInputDir dir = new DefaultInputDir("module1", "src", 2)
+ .setModuleBaseDir(moduleBaseDir);
+ tree.index(dir, root);
+
+ // dir without changed files or issues
+ DefaultInputDir dir2 = new DefaultInputDir("module1", "src2", 3)
+ .setModuleBaseDir(moduleBaseDir);
+ tree.index(dir2, root);
+
+ // dir without changed files but has issues
+ DefaultInputDir dir3 = new DefaultInputDir("module1", "src3", 4)
+ .setModuleBaseDir(moduleBaseDir);
+ tree.index(dir3, root);
+ writeIssue(4);
+
+ DefaultInputFile file = new TestInputFileBuilder("module1", "src/Foo.java", 5)
+ .setLines(2)
+ .setPublish(true)
+ .setStatus(InputFile.Status.ADDED)
+ .build();
+ tree.index(file, dir);
+
+ DefaultInputFile file2 = new TestInputFileBuilder("module1", "src2/Foo2.java", 6)
+ .setPublish(true)
+ .setStatus(InputFile.Status.SAME)
+ .setLines(2)
+ .build();
+ tree.index(file2, dir2);
+
+ DefaultInputFile file3 = new TestInputFileBuilder("module1", "src3/Foo3.java", 7)
+ .setPublish(true)
+ .setStatus(InputFile.Status.SAME)
+ .setLines(2)
+ .build();
+ tree.index(file3, dir3);
+
+ ComponentsPublisher publisher = new ComponentsPublisher(moduleHierarchy, tree, branchConfiguration);
+ publisher.publish(writer);
+
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 1)).isTrue();
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 2)).isTrue();
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 4)).isTrue();
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 5)).isTrue();
+
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 3)).isFalse();
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 6)).isFalse();
+ assertThat(writer.hasComponentData(FileStructure.Domain.COMPONENT, 7)).isFalse();
+ }
+
+ @Test
+ public void skip_unchanged_components_in_pull_requests() throws IOException {
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
ProjectAnalysisInfo projectAnalysisInfo = mock(ProjectAnalysisInfo.class);
when(projectAnalysisInfo.analysisDate()).thenReturn(DateUtils.parseDate("2012-12-12"));
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java
index 128561c45bf..b25f773f536 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/ReportPublisherTest.java
@@ -59,6 +59,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static org.sonar.scanner.scan.branch.BranchType.PULL_REQUEST;
import static org.sonar.scanner.scan.branch.BranchType.SHORT;
public class ReportPublisherTest {
@@ -267,4 +268,42 @@ public class ReportPublisherTest {
.containsExactlyInAnyOrder("branch=" + branchName, "branchType=" + SHORT.name());
}
+ @Test
+ public void send_pull_request_characteristic() throws Exception {
+ ReportPublisher underTest = new ReportPublisher(settings.asConfig(), wsClient, server, contextPublisher, moduleHierarchy, mode, mock(TempFolder.class),
+ new ReportPublisherStep[0], branchConfiguration);
+
+ String orgName = "MyOrg";
+ settings.setProperty(ScannerProperties.ORGANIZATION, orgName);
+
+ String branchName = "feature";
+ String pullRequestId = "pr-123";
+ when(branchConfiguration.branchName()).thenReturn(branchName);
+ when(branchConfiguration.branchType()).thenReturn(PULL_REQUEST);
+ when(branchConfiguration.pullRequestKey()).thenReturn(pullRequestId);
+
+ WsResponse response = mock(WsResponse.class);
+
+ PipedOutputStream out = new PipedOutputStream();
+ PipedInputStream in = new PipedInputStream(out);
+ Ce.SubmitResponse.newBuilder().build().writeTo(out);
+ out.close();
+
+ when(response.failIfNotSuccessful()).thenReturn(response);
+ when(response.contentStream()).thenReturn(in);
+
+ when(wsClient.call(any(WsRequest.class))).thenReturn(response);
+ underTest.upload(temp.newFile());
+
+ ArgumentCaptor<WsRequest> capture = ArgumentCaptor.forClass(WsRequest.class);
+ verify(wsClient).call(capture.capture());
+
+ WsRequest wsRequest = capture.getValue();
+ assertThat(wsRequest.getParameters().getKeys()).hasSize(3);
+ assertThat(wsRequest.getParameters().getValues("organization")).containsExactly(orgName);
+ assertThat(wsRequest.getParameters().getValues("projectKey")).containsExactly("struts");
+ assertThat(wsRequest.getParameters().getValues("characteristic"))
+ .containsExactlyInAnyOrder("pullRequest=" + pullRequestId);
+ }
+
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisherTest.java
index 7b35a9af541..a1175ee2332 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisherTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/TestExecutionAndCoveragePublisherTest.java
@@ -40,7 +40,21 @@ public class TestExecutionAndCoveragePublisherTest {
@Test
public void do_nothing_for_short_living_branches() throws IOException {
BranchConfiguration branchConfiguration = mock(BranchConfiguration.class);
- when(branchConfiguration.isShortLivingBranch()).thenReturn(true);
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
+ InputComponentStore componentStore = mock(InputComponentStore.class);
+ TestExecutionAndCoveragePublisher publisher = new TestExecutionAndCoveragePublisher(componentStore, null, branchConfiguration);
+ File outputDir = temp.newFolder();
+ ScannerReportWriter writer = new ScannerReportWriter(outputDir);
+
+ publisher.publish(writer);
+
+ verifyZeroInteractions(componentStore);
+ }
+
+ @Test
+ public void do_nothing_for_pull_requests() throws IOException {
+ BranchConfiguration branchConfiguration = mock(BranchConfiguration.class);
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
InputComponentStore componentStore = mock(InputComponentStore.class);
TestExecutionAndCoveragePublisher publisher = new TestExecutionAndCoveragePublisher(componentStore, null, branchConfiguration);
File outputDir = temp.newFolder();
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java
index e71a71f161b..0c0799c9ab3 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/ProjectReactorValidatorTest.java
@@ -191,6 +191,45 @@ public class ProjectReactorValidatorTest {
}
@Test
+ public void fail_when_pull_request_id_specified_but_branch_plugin_not_present() {
+ ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, "foo");
+ ProjectReactor reactor = new ProjectReactor(def);
+
+ when(settings.get(eq(ScannerProperties.PULL_REQUEST_KEY))).thenReturn(Optional.of("#1984"));
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("the branch plugin is required but not installed");
+
+ validator.validate(reactor);
+ }
+
+ @Test
+ public void fail_when_pull_request_branch_is_specified_but_branch_plugin_not_present() {
+ ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, "foo");
+ ProjectReactor reactor = new ProjectReactor(def);
+
+ when(settings.get(eq(ScannerProperties.PULL_REQUEST_BRANCH))).thenReturn(Optional.of("feature1"));
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("the branch plugin is required but not installed");
+
+ validator.validate(reactor);
+ }
+
+ @Test
+ public void fail_when_pull_request_base_specified_but_branch_plugin_not_present() {
+ ProjectDefinition def = ProjectDefinition.create().setProperty(CoreProperties.PROJECT_KEY_PROPERTY, "foo");
+ ProjectReactor reactor = new ProjectReactor(def);
+
+ when(settings.get(eq(ScannerProperties.PULL_REQUEST_BASE))).thenReturn(Optional.of("feature1"));
+
+ thrown.expect(MessageException.class);
+ thrown.expectMessage("the branch plugin is required but not installed");
+
+ validator.validate(reactor);
+ }
+
+ @Test
public void not_fail_with_valid_version() {
validator.validate(createProjectReactor("foo", def -> def.setVersion("1.0")));
validator.validate(createProjectReactor("foo", def -> def.setVersion("2017-10-16")));
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/BranchConfigurationProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/BranchConfigurationProviderTest.java
index 067114dd43b..a8fbc75cc8b 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/BranchConfigurationProviderTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/BranchConfigurationProviderTest.java
@@ -41,6 +41,7 @@ public class BranchConfigurationProviderTest {
private BranchConfigurationLoader loader;
private BranchConfiguration config;
private ProjectBranches branches;
+ private ProjectPullRequests pullRequests;
private ProjectKey projectKey;
private Map<String, String> globalPropertiesMap;
private Map<String, String> remoteProjectSettings;
@@ -52,6 +53,7 @@ public class BranchConfigurationProviderTest {
loader = mock(BranchConfigurationLoader.class);
config = mock(BranchConfiguration.class);
branches = mock(ProjectBranches.class);
+ pullRequests = mock(ProjectPullRequests.class);
settingsLoader = mock(SettingsLoader.class);
projectKey = mock(ProjectKey.class);
globalPropertiesMap = new HashMap<>();
@@ -61,22 +63,24 @@ public class BranchConfigurationProviderTest {
@Test
public void should_cache_config() {
- BranchConfiguration configuration = provider.provide(null, globalConfiguration, projectKey, settingsLoader, branches);
- assertThat(provider.provide(null, globalConfiguration, projectKey, settingsLoader, branches)).isSameAs(configuration);
+ BranchConfiguration configuration = provider.provide(null, globalConfiguration, projectKey, settingsLoader, branches, pullRequests);
+ assertThat(provider.provide(null, globalConfiguration, projectKey, settingsLoader, branches, pullRequests)).isSameAs(configuration);
}
@Test
public void should_use_loader() {
- when(loader.load(eq(globalPropertiesMap), any(Supplier.class), eq(branches))).thenReturn(config);
- BranchConfiguration branchConfig = provider.provide(loader, globalConfiguration, projectKey, settingsLoader, branches);
+ when(loader.load(eq(globalPropertiesMap), any(Supplier.class), eq(branches), eq(pullRequests))).thenReturn(config);
- assertThat(branchConfig).isSameAs(config);
+ BranchConfiguration result = provider.provide(loader, globalConfiguration, projectKey, settingsLoader, branches, pullRequests);
+
+ assertThat(result).isSameAs(config);
}
@Test
public void should_return_default_if_no_loader() {
- BranchConfiguration configuration = provider.provide(null, globalConfiguration, projectKey, settingsLoader, branches);
- assertThat(configuration.branchTarget()).isNull();
- assertThat(configuration.branchType()).isEqualTo(BranchType.LONG);
+ BranchConfiguration result = provider.provide(null, globalConfiguration, projectKey, settingsLoader, branches, pullRequests);
+
+ assertThat(result.branchTarget()).isNull();
+ assertThat(result.branchType()).isEqualTo(BranchType.LONG);
}
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectBranchesProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectBranchesProviderTest.java
index 6b75d766e3e..fd22c4d96e8 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectBranchesProviderTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectBranchesProviderTest.java
@@ -19,11 +19,8 @@
*/
package org.sonar.scanner.scan.branch;
-import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
-import org.sonar.api.config.Configuration;
-import org.sonar.scanner.bootstrap.GlobalConfiguration;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.anyString;
@@ -34,33 +31,30 @@ public class ProjectBranchesProviderTest {
private ProjectBranchesProvider provider = new ProjectBranchesProvider();
private ProjectBranchesLoader mockLoader;
private ProjectBranches mockBranches;
- private GlobalConfiguration mockSettings;
@Before
public void setUp() {
mockLoader = mock(ProjectBranchesLoader.class);
mockBranches = mock(ProjectBranches.class);
- mockSettings = mock(GlobalConfiguration.class);
}
@Test
public void should_cache_branches() {
- ProjectBranches branches = provider.provide(null, () -> "project", mockSettings);
- assertThat(provider.provide(null, () -> "project", mockSettings)).isSameAs(branches);
+ ProjectBranches branches = provider.provide(null, () -> "project");
+ assertThat(provider.provide(null, () -> "project")).isSameAs(branches);
}
@Test
public void should_use_loader() {
when(mockLoader.load("key")).thenReturn(mockBranches);
- when(mockSettings.get(anyString())).thenReturn(Optional.of("somebranch"));
- ProjectBranches branches = provider.provide(mockLoader, () -> "key", mockSettings);
+ ProjectBranches branches = provider.provide(mockLoader, () -> "key");
assertThat(branches).isSameAs(mockBranches);
}
@Test
public void should_return_default_if_no_loader() {
- ProjectBranches branches = provider.provide(null, () -> "project", mockSettings);
+ ProjectBranches branches = provider.provide(null, () -> "project");
assertThat(branches.isEmpty()).isTrue();
}
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProviderTest.java
new file mode 100644
index 00000000000..b3c1ab87fd7
--- /dev/null
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scan/branch/ProjectPullRequestsProviderTest.java
@@ -0,0 +1,63 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.scanner.scan.branch;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import static java.util.Collections.emptyList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class ProjectPullRequestsProviderTest {
+ private ProjectPullRequestsProvider provider = new ProjectPullRequestsProvider();
+ private ProjectPullRequestsLoader mockLoader;
+ private ProjectPullRequests pullRequests;
+
+ @Before
+ public void setUp() {
+ mockLoader = mock(ProjectPullRequestsLoader.class);
+ pullRequests = new ProjectPullRequests(emptyList());
+ }
+
+ @Test
+ public void cache_pull_requests() {
+ ProjectPullRequests pullRequests = provider.provide(null, () -> "project");
+
+ assertThat(provider.provide(null, () -> "project")).isSameAs(pullRequests);
+ }
+
+ @Test
+ public void should_use_loader() {
+ when(mockLoader.load("key")).thenReturn(pullRequests);
+
+ ProjectPullRequests result = provider.provide(mockLoader, () -> "key");
+
+ assertThat(result).isSameAs(pullRequests);
+ }
+
+ @Test
+ public void should_return_default_if_no_loader() {
+ ProjectPullRequests result = provider.provide(null, () -> "project");
+
+ assertThat(result.isEmpty()).isTrue();
+ }
+}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesProviderTest.java
index 83db811b6b9..bc212aa9489 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesProviderTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmChangedFilesProviderTest.java
@@ -66,7 +66,7 @@ public class ScmChangedFilesProviderTest {
@Test
public void testNoScmProvider() {
- when(branchConfiguration.isShortLivingBranch()).thenReturn(true);
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
when(branchConfiguration.branchTarget()).thenReturn("target");
ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy);
@@ -78,7 +78,7 @@ public class ScmChangedFilesProviderTest {
@Test
public void testFailIfRelativePath() {
when(branchConfiguration.branchTarget()).thenReturn("target");
- when(branchConfiguration.isShortLivingBranch()).thenReturn(true);
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
when(scmConfiguration.provider()).thenReturn(scmProvider);
when(scmProvider.branchChangedFiles("target", rootBaseDir)).thenReturn(Collections.singleton(Paths.get("changedFile")));
@@ -90,7 +90,7 @@ public class ScmChangedFilesProviderTest {
@Test
public void testProviderDoesntSupport() {
when(branchConfiguration.branchTarget()).thenReturn("target");
- when(branchConfiguration.isShortLivingBranch()).thenReturn(true);
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
when(scmConfiguration.provider()).thenReturn(scmProvider);
when(scmProvider.branchChangedFiles("target", rootBaseDir)).thenReturn(null);
ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy);
@@ -101,7 +101,7 @@ public class ScmChangedFilesProviderTest {
@Test
public void testNoOpInNonShortLivedBranch() {
- when(branchConfiguration.isShortLivingBranch()).thenReturn(false);
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(false);
ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy);
assertThat(scmChangedFiles.get()).isNull();
@@ -118,7 +118,7 @@ public class ScmChangedFilesProviderTest {
};
when(scmConfiguration.provider()).thenReturn(legacy);
- when(branchConfiguration.isShortLivingBranch()).thenReturn(true);
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
when(branchConfiguration.branchTarget()).thenReturn("target");
ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy);
@@ -130,7 +130,7 @@ public class ScmChangedFilesProviderTest {
@Test
public void testReturnChangedFiles() {
when(branchConfiguration.branchTarget()).thenReturn("target");
- when(branchConfiguration.isShortLivingBranch()).thenReturn(true);
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
when(scmConfiguration.provider()).thenReturn(scmProvider);
when(scmProvider.branchChangedFiles("target", rootBaseDir)).thenReturn(Collections.singleton(Paths.get("changedFile").toAbsolutePath()));
ScmChangedFiles scmChangedFiles = provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy);
@@ -143,7 +143,7 @@ public class ScmChangedFilesProviderTest {
public void testCacheObject() {
provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy);
provider.provide(scmConfiguration, branchConfiguration, inputModuleHierarchy);
- verify(branchConfiguration).isShortLivingBranch();
+ verify(branchConfiguration).isShortOrPullRequest();
}
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java
index d09769ee3a8..68771fe5059 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorContextTest.java
@@ -91,7 +91,14 @@ public class DefaultSensorContextTest {
@Test
public void shouldSkipDupsAndCoverageOnShortBranches() {
- when(branchConfig.isShortLivingBranch()).thenReturn(true);
+ when(branchConfig.isShortOrPullRequest()).thenReturn(true);
+ assertThat(adaptor.newCpdTokens()).isEqualTo(DefaultSensorContext.NO_OP_NEW_CPD_TOKENS);
+ assertThat(adaptor.newCoverage()).isEqualTo(DefaultSensorContext.NO_OP_NEW_COVERAGE);
+ }
+
+ @Test
+ public void shouldSkipDupsAndCoverageOnPullRequests() {
+ when(branchConfig.isShortOrPullRequest()).thenReturn(true);
assertThat(adaptor.newCpdTokens()).isEqualTo(DefaultSensorContext.NO_OP_NEW_CPD_TOKENS);
assertThat(adaptor.newCoverage()).isEqualTo(DefaultSensorContext.NO_OP_NEW_COVERAGE);
}
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
index 52da53edbf5..843e4730b45 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sensor/DefaultSensorStorageTest.java
@@ -127,7 +127,7 @@ public class DefaultSensorStorageTest {
@Test
public void should_skip_issue_on_short_branch_when_file_status_is_SAME() {
InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").setStatus(InputFile.Status.SAME).build();
- when(branchConfiguration.isShortLivingBranch()).thenReturn(true);
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
DefaultIssue issue = new DefaultIssue().at(new DefaultIssueLocation().on(file));
underTest.store(issue);
@@ -151,7 +151,7 @@ public class DefaultSensorStorageTest {
DefaultInputFile file = new TestInputFileBuilder("foo", "src/Foo.php")
.setContents("// comment")
.setStatus(InputFile.Status.SAME).build();
- when(branchConfiguration.isShortLivingBranch()).thenReturn(true);
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
DefaultHighlighting highlighting = new DefaultHighlighting(underTest).onFile(file).highlight(0, 1, TypeOfText.KEYWORD);
underTest.store(highlighting);
@@ -178,7 +178,20 @@ public class DefaultSensorStorageTest {
@Test
public void should_skip_file_measure_on_short_branch_when_file_status_is_SAME() {
InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").setStatus(InputFile.Status.SAME).build();
- when(branchConfiguration.isShortLivingBranch()).thenReturn(true);
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
+
+ underTest.store(new DefaultMeasure()
+ .on(file)
+ .forMetric(CoreMetrics.NCLOC)
+ .withValue(10));
+
+ verifyZeroInteractions(measureCache);
+ }
+
+ @Test
+ public void should_skip_file_measure_on_pull_request_when_file_status_is_SAME() {
+ InputFile file = new TestInputFileBuilder("foo", "src/Foo.php").setStatus(InputFile.Status.SAME).build();
+ when(branchConfiguration.isShortOrPullRequest()).thenReturn(true);
underTest.store(new DefaultMeasure()
.on(file)
diff --git a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
index 7d3123d24c1..c99b9c9882e 100644
--- a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
+++ b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto
@@ -46,6 +46,8 @@ message Metadata {
string relative_path_from_scm_root = 12;
string scm_revision_id = 13;
+ string pull_request_key = 14;
+
message QProfile {
string key = 1;
string name = 2;
@@ -62,6 +64,7 @@ message Metadata {
UNSET = 0;
LONG = 1;
SHORT = 2;
+ PULL_REQUEST = 3;
}
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/DefaultWsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/DefaultWsClient.java
index 3c5e64ec06a..bdbbbf90c62 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/DefaultWsClient.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/DefaultWsClient.java
@@ -45,8 +45,9 @@ import org.sonarqube.ws.client.profiles.ProfilesService;
import org.sonarqube.ws.client.projectanalyses.ProjectAnalysesService;
import org.sonarqube.ws.client.projectbranches.ProjectBranchesService;
import org.sonarqube.ws.client.projectlinks.ProjectLinksService;
-import org.sonarqube.ws.client.projecttags.ProjectTagsService;
+import org.sonarqube.ws.client.projectpullrequests.ProjectPullRequestsService;
import org.sonarqube.ws.client.projects.ProjectsService;
+import org.sonarqube.ws.client.projecttags.ProjectTagsService;
import org.sonarqube.ws.client.properties.PropertiesService;
import org.sonarqube.ws.client.qualitygates.QualitygatesService;
import org.sonarqube.ws.client.qualityprofiles.QualityprofilesService;
@@ -102,6 +103,7 @@ class DefaultWsClient implements WsClient {
private final ProjectAnalysesService projectAnalysesService;
private final ProjectBranchesService projectBranchesService;
private final ProjectLinksService projectLinksService;
+ private final ProjectPullRequestsService projectPullRequestsService;
private final ProjectTagsService projectTagsService;
private final ProjectsService projectsService;
private final PropertiesService propertiesService;
@@ -152,6 +154,7 @@ class DefaultWsClient implements WsClient {
this.projectAnalysesService = new ProjectAnalysesService(wsConnector);
this.projectBranchesService = new ProjectBranchesService(wsConnector);
this.projectLinksService = new ProjectLinksService(wsConnector);
+ this.projectPullRequestsService = new ProjectPullRequestsService(wsConnector);
this.projectTagsService = new ProjectTagsService(wsConnector);
this.projectsService = new ProjectsService(wsConnector);
this.propertiesService = new PropertiesService(wsConnector);
@@ -303,6 +306,11 @@ class DefaultWsClient implements WsClient {
}
@Override
+ public ProjectPullRequestsService projectPullRequests() {
+ return projectPullRequestsService;
+ }
+
+ @Override
public ProjectTagsService projectTags() {
return projectTagsService;
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java
index 570b96cef47..79ca15098c2 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/WsClient.java
@@ -45,6 +45,7 @@ import org.sonarqube.ws.client.profiles.ProfilesService;
import org.sonarqube.ws.client.projectanalyses.ProjectAnalysesService;
import org.sonarqube.ws.client.projectbranches.ProjectBranchesService;
import org.sonarqube.ws.client.projectlinks.ProjectLinksService;
+import org.sonarqube.ws.client.projectpullrequests.ProjectPullRequestsService;
import org.sonarqube.ws.client.projecttags.ProjectTagsService;
import org.sonarqube.ws.client.projects.ProjectsService;
import org.sonarqube.ws.client.properties.PropertiesService;
@@ -139,6 +140,8 @@ public interface WsClient {
ProjectLinksService projectLinks();
+ ProjectPullRequestsService projectPullRequests();
+
ProjectTagsService projectTags();
ProjectsService projects();
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java
index 5e726b0eafb..f6fdf792115 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/component/ComponentsWsParameters.java
@@ -39,6 +39,7 @@ public class ComponentsWsParameters {
public static final String PARAM_COMPONENT_ID = "componentId";
public static final String PARAM_COMPONENT = "component";
public static final String PARAM_BRANCH = "branch";
+ public static final String PARAM_PULL_REQUEST = "pullRequest";
private ComponentsWsParameters() {
// static utility class
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/components/AppRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/components/AppRequest.java
index 772651aa090..14441f113b9 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/components/AppRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/components/AppRequest.java
@@ -34,6 +34,7 @@ public class AppRequest {
private String branch;
private String component;
private String componentId;
+ private String pullRequest;
/**
* This is part of the internal API.
@@ -73,4 +74,17 @@ public class AppRequest {
public String getComponentId() {
return componentId;
}
+
+ /**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public AppRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/components/ComponentsService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/components/ComponentsService.java
index 483474301b1..bc8d556b033 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/components/ComponentsService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/components/ComponentsService.java
@@ -55,6 +55,7 @@ public class ComponentsService extends BaseService {
.setParam("branch", request.getBranch())
.setParam("component", request.getComponent())
.setParam("componentId", request.getComponentId())
+ .setParam("pullRequest", request.getPullRequest())
.setMediaType(MediaTypes.JSON)
).content();
}
@@ -111,7 +112,8 @@ public class ComponentsService extends BaseService {
new GetRequest(path("show"))
.setParam("branch", request.getBranch())
.setParam("component", request.getComponent())
- .setParam("componentId", request.getComponentId()),
+ .setParam("componentId", request.getComponentId())
+ .setParam("pullRequest", request.getPullRequest()),
ShowWsResponse.parser());
}
@@ -147,6 +149,7 @@ public class ComponentsService extends BaseService {
.setParam("componentId", request.getComponentId())
.setParam("p", request.getP())
.setParam("ps", request.getPs())
+ .setParam("pullRequest", request.getPullRequest())
.setParam("q", request.getQ())
.setParam("qualifiers", request.getQualifiers() == null ? null : request.getQualifiers().stream().collect(Collectors.joining(",")))
.setParam("s", request.getS() == null ? null : request.getS().stream().collect(Collectors.joining(",")))
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/components/ShowRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/components/ShowRequest.java
index b6306545598..4a04cd56d1b 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/components/ShowRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/components/ShowRequest.java
@@ -34,6 +34,7 @@ public class ShowRequest {
private String branch;
private String component;
private String componentId;
+ private String pullRequest;
/**
* This is part of the internal API.
@@ -73,4 +74,17 @@ public class ShowRequest {
public String getComponentId() {
return componentId;
}
+
+ /**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public ShowRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/components/TreeRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/components/TreeRequest.java
index 4aae16c2d9c..82ae76cb9a6 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/components/TreeRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/components/TreeRequest.java
@@ -37,6 +37,7 @@ public class TreeRequest {
private String componentId;
private String p;
private String ps;
+ private String pullRequest;
private String q;
private List<String> qualifiers;
private List<String> s;
@@ -124,6 +125,19 @@ public class TreeRequest {
}
/**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public TreeRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ /**
* Example value: "FILE_NAM"
*/
public TreeRequest setQ(String q) {
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/duplications/DuplicationsService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/duplications/DuplicationsService.java
index de57a61729d..dc68920fc3d 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/duplications/DuplicationsService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/duplications/DuplicationsService.java
@@ -50,6 +50,7 @@ public class DuplicationsService extends BaseService {
new GetRequest(path("show"))
.setParam("branch", request.getBranch())
.setParam("key", request.getKey())
+ .setParam("pullRequest", request.getPullRequest())
.setParam("uuid", request.getUuid()),
ShowResponse.parser());
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/duplications/ShowRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/duplications/ShowRequest.java
index 0ee02965ce5..333618abb66 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/duplications/ShowRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/duplications/ShowRequest.java
@@ -33,6 +33,7 @@ public class ShowRequest {
private String branch;
private String key;
+ private String pullRequest;
private String uuid;
/**
@@ -61,6 +62,19 @@ public class ShowRequest {
}
/**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public ShowRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ /**
* Example value: "584a89f2-8037-4f7b-b82c-8b45d2d63fb2"
* @deprecated since 6.5
*/
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java
index 3dbaa54c9fd..0032e024f4d 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/issue/IssuesWsParameters.java
@@ -70,6 +70,7 @@ public class IssuesWsParameters {
public static final String PARAM_FILE_UUIDS = "fileUuids";
public static final String PARAM_ON_COMPONENT_ONLY = "onComponentOnly";
public static final String PARAM_BRANCH = "branch";
+ public static final String PARAM_PULL_REQUEST = "pullRequest";
public static final String PARAM_ORGANIZATION = "organization";
public static final String PARAM_RULES = "rules";
public static final String PARAM_ACTIONS = "actions";
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/issues/IssuesService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/issues/IssuesService.java
index 28a7ebe3775..75918c48fad 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/issues/IssuesService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/issues/IssuesService.java
@@ -232,6 +232,7 @@ public class IssuesService extends BaseService {
.setParam("projectUuids", request.getProjectUuids() == null ? null : request.getProjectUuids().stream().collect(Collectors.joining(",")))
.setParam("projects", request.getProjects() == null ? null : request.getProjects().stream().collect(Collectors.joining(",")))
.setParam("ps", request.getPs())
+ .setParam("pullRequest", request.getPullRequest())
.setParam("resolutions", request.getResolutions() == null ? null : request.getResolutions().stream().collect(Collectors.joining(",")))
.setParam("resolved", request.getResolved())
.setParam("rules", request.getRules() == null ? null : request.getRules().stream().collect(Collectors.joining(",")))
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/issues/SearchRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/issues/SearchRequest.java
index 3e5def5f536..74096bdde58 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/issues/SearchRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/issues/SearchRequest.java
@@ -59,6 +59,7 @@ public class SearchRequest {
private List<String> projectUuids;
private List<String> projects;
private String ps;
+ private String pullRequest;
private List<String> resolutions;
private String resolved;
private List<String> rules;
@@ -470,6 +471,19 @@ public class SearchRequest {
}
/**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public SearchRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ /**
* Example value: "FIXED,REMOVED"
* Possible values:
* <ul>
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/ComponentRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/ComponentRequest.java
index 5029fb0e572..fa8041a095d 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/ComponentRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/ComponentRequest.java
@@ -38,6 +38,7 @@ public class ComponentRequest {
private String developerId;
private String developerKey;
private List<String> metricKeys;
+ private String pullRequest;
/**
* Example value: "periods,metrics"
@@ -133,4 +134,17 @@ public class ComponentRequest {
public List<String> getMetricKeys() {
return metricKeys;
}
+
+ /**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public ComponentRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/ComponentTreeRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/ComponentTreeRequest.java
index 8799bf1bc5c..d9416aa81c9 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/ComponentTreeRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/ComponentTreeRequest.java
@@ -44,6 +44,7 @@ public class ComponentTreeRequest {
private String metricSortFilter;
private String p;
private String ps;
+ private String pullRequest;
private String q;
private List<String> qualifiers;
private List<String> s;
@@ -230,6 +231,19 @@ public class ComponentTreeRequest {
}
/**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public ComponentTreeRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ /**
* Example value: "FILE_NAM"
*/
public ComponentTreeRequest setQ(String q) {
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/MeasuresService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/MeasuresService.java
index 06e82e95def..f9662d04ace 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/MeasuresService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/MeasuresService.java
@@ -57,7 +57,8 @@ public class MeasuresService extends BaseService {
.setParam("componentId", request.getComponentId())
.setParam("developerId", request.getDeveloperId())
.setParam("developerKey", request.getDeveloperKey())
- .setParam("metricKeys", request.getMetricKeys() == null ? null : request.getMetricKeys().stream().collect(Collectors.joining(","))),
+ .setParam("metricKeys", request.getMetricKeys() == null ? null : request.getMetricKeys().stream().collect(Collectors.joining(",")))
+ .setParam("pullRequest", request.getPullRequest()),
ComponentWsResponse.parser());
}
@@ -84,6 +85,7 @@ public class MeasuresService extends BaseService {
.setParam("metricSortFilter", request.getMetricSortFilter())
.setParam("p", request.getP())
.setParam("ps", request.getPs())
+ .setParam("pullRequest", request.getPullRequest())
.setParam("q", request.getQ())
.setParam("qualifiers", request.getQualifiers() == null ? null : request.getQualifiers().stream().collect(Collectors.joining(",")))
.setParam("s", request.getS() == null ? null : request.getS().stream().collect(Collectors.joining(",")))
@@ -122,6 +124,7 @@ public class MeasuresService extends BaseService {
.setParam("metrics", request.getMetrics() == null ? null : request.getMetrics().stream().collect(Collectors.joining(",")))
.setParam("p", request.getP())
.setParam("ps", request.getPs())
+ .setParam("pullRequest", request.getPullRequest())
.setParam("to", request.getTo()),
SearchHistoryResponse.parser());
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/SearchHistoryRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/SearchHistoryRequest.java
index 37f2b02da89..04395aff33a 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/SearchHistoryRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/measures/SearchHistoryRequest.java
@@ -37,6 +37,7 @@ public class SearchHistoryRequest {
private List<String> metrics;
private String p;
private String ps;
+ private String pullRequest;
private String to;
/**
@@ -115,6 +116,19 @@ public class SearchHistoryRequest {
}
/**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public SearchHistoryRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ /**
* Example value: "2017-10-19 or 2017-10-19T13:00:00+0200"
*/
public SearchHistoryRequest setTo(String to) {
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/navigation/ComponentRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/navigation/ComponentRequest.java
index 5dc8aa75360..a442f0092c7 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/navigation/ComponentRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/navigation/ComponentRequest.java
@@ -33,6 +33,7 @@ public class ComponentRequest {
private String branch;
private String component;
+ private String pullRequest;
/**
* This is part of the internal API.
@@ -58,4 +59,17 @@ public class ComponentRequest {
public String getComponent() {
return component;
}
+
+ /**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public ComponentRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/navigation/NavigationService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/navigation/NavigationService.java
index 7dcbc5090f4..0b8edf6a5b4 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/navigation/NavigationService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/navigation/NavigationService.java
@@ -49,6 +49,7 @@ public class NavigationService extends BaseService {
new GetRequest(path("component"))
.setParam("branch", request.getBranch())
.setParam("component", request.getComponent())
+ .setParam("pullRequest", request.getPullRequest())
.setMediaType(MediaTypes.JSON)
).content();
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/permissions/TemplateGroupsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/permissions/TemplateGroupsRequest.java
index 5388364e035..04d9b272bce 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/permissions/TemplateGroupsRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/permissions/TemplateGroupsRequest.java
@@ -65,7 +65,6 @@ public class TemplateGroupsRequest {
}
/**
- * This is a mandatory parameter.
* Possible values:
* <ul>
* <li>"admin"</li>
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalyses/ProjectAnalysesService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalyses/ProjectAnalysesService.java
index 3166e6f8215..10ab3a2efba 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalyses/ProjectAnalysesService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalyses/ProjectAnalysesService.java
@@ -102,6 +102,7 @@ public class ProjectAnalysesService extends BaseService {
.setParam("p", request.getP())
.setParam("project", request.getProject())
.setParam("ps", request.getPs())
+ .setParam("pullRequest", request.getPullRequest())
.setParam("to", request.getTo()),
SearchResponse.parser());
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalyses/SearchRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalyses/SearchRequest.java
index 83e00959d0e..1f81fa87296 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalyses/SearchRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectanalyses/SearchRequest.java
@@ -37,6 +37,7 @@ public class SearchRequest {
private String p;
private String project;
private String ps;
+ private String pullRequest;
private String to;
/**
@@ -121,6 +122,19 @@ public class SearchRequest {
}
/**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public SearchRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ /**
* Example value: "2017-10-19 or 2017-10-19T13:00:00+0200"
*/
public SearchRequest setTo(String to) {
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesService.java
index 1a65b559e8b..9eca20837c9 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectbranches/ProjectBranchesService.java
@@ -45,8 +45,8 @@ public class ProjectBranchesService extends BaseService {
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/project_branches/delete">Further information about this action online (including a response example)</a>
* @since 6.6
*/
- public String delete(DeleteRequest request) {
- return call(
+ public void delete(DeleteRequest request) {
+ call(
new PostRequest(path("delete"))
.setParam("branch", request.getBranch())
.setParam("project", request.getProject())
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/DeleteRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/DeleteRequest.java
new file mode 100644
index 00000000000..aaaf8869bf7
--- /dev/null
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/DeleteRequest.java
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.projectpullrequests;
+
+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/project_pull_requests/delete">Further information about this action online (including a response example)</a>
+ * @since 7.1
+ */
+@Generated("sonar-ws-generator")
+public class DeleteRequest {
+
+ private String project;
+ private String pullRequest;
+
+ /**
+ * This is a mandatory parameter.
+ * Example value: "my_project"
+ */
+ public DeleteRequest setProject(String project) {
+ this.project = project;
+ return this;
+ }
+
+ public String getProject() {
+ return project;
+ }
+
+ /**
+ * This is a mandatory parameter.
+ * Example value: "1543"
+ */
+ public DeleteRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/ListRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/ListRequest.java
new file mode 100644
index 00000000000..fe7d93ef0bd
--- /dev/null
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/ListRequest.java
@@ -0,0 +1,48 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.projectpullrequests;
+
+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/project_pull_requests/list">Further information about this action online (including a response example)</a>
+ * @since 7.1
+ */
+@Generated("sonar-ws-generator")
+public class ListRequest {
+
+ private String project;
+
+ /**
+ * This is a mandatory parameter.
+ * Example value: "my_project"
+ */
+ public ListRequest setProject(String project) {
+ this.project = project;
+ return this;
+ }
+
+ public String getProject() {
+ return project;
+ }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/ProjectPullRequestsService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/ProjectPullRequestsService.java
new file mode 100644
index 00000000000..b7ba3dabc18
--- /dev/null
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/ProjectPullRequestsService.java
@@ -0,0 +1,70 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.projectpullrequests;
+
+import java.util.stream.Collectors;
+import javax.annotation.Generated;
+import org.sonarqube.ws.MediaTypes;
+import org.sonarqube.ws.client.BaseService;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.WsConnector;
+import org.sonarqube.ws.ProjectPullRequests.ListWsResponse;
+
+/**
+ * @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/project_pull_requests">Further information about this web service online</a>
+ */
+@Generated("sonar-ws-generator")
+public class ProjectPullRequestsService extends BaseService {
+
+ public ProjectPullRequestsService(WsConnector wsConnector) {
+ super(wsConnector, "api/project_pull_requests");
+ }
+
+ /**
+ *
+ * This is part of the internal API.
+ * This is a POST request.
+ * @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/project_pull_requests/delete">Further information about this action online (including a response example)</a>
+ * @since 7.1
+ */
+ public void delete(DeleteRequest request) {
+ call(
+ new PostRequest(path("delete"))
+ .setParam("project", request.getProject())
+ .setParam("pullRequest", request.getPullRequest())
+ .setMediaType(MediaTypes.JSON)
+ ).content();
+ }
+
+ /**
+ *
+ * This is part of the internal API.
+ * This is a GET request.
+ * @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/project_pull_requests/list">Further information about this action online (including a response example)</a>
+ * @since 7.1
+ */
+ public ListWsResponse list(ListRequest request) {
+ return call(
+ new GetRequest(path("list"))
+ .setParam("project", request.getProject()),
+ ListWsResponse.parser());
+ }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/package-info.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/package-info.java
new file mode 100644
index 00000000000..e742bab1d5e
--- /dev/null
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/projectpullrequests/package-info.java
@@ -0,0 +1,25 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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
+@Generated("sonar-ws-generator")
+package org.sonarqube.ws.client.projectpullrequests;
+
+import javax.annotation.ParametersAreNonnullByDefault;
+import javax.annotation.Generated;
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CopyRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CopyRequest.java
index dffd09b323a..c0615b16482 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CopyRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CopyRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CreateConditionRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CreateConditionRequest.java
index ebc887524f4..0ef25a4c881 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CreateConditionRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CreateConditionRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CreateRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CreateRequest.java
index 31ba90f154f..0fb572e240b 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CreateRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/CreateRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DeleteConditionRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DeleteConditionRequest.java
index 6f9c7d6a308..d85bf386d0f 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DeleteConditionRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DeleteConditionRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DeselectRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DeselectRequest.java
index f3ea2174093..2b7603aef0a 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DeselectRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DeselectRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DestroyRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DestroyRequest.java
index 3e2169d78d1..89c817d1863 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DestroyRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/DestroyRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/GetByProjectRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/GetByProjectRequest.java
index f07bc686d31..809241b5fe4 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/GetByProjectRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/GetByProjectRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ListRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ListRequest.java
index 2f998f79002..01b75ec4e47 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ListRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ListRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ProjectStatusRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ProjectStatusRequest.java
index 64272965eed..557d4f53bf6 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ProjectStatusRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ProjectStatusRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/QualitygatesService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/QualitygatesService.java
index 662524360a7..1c65c9e9181 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/QualitygatesService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/QualitygatesService.java
@@ -19,20 +19,21 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.stream.Collectors;
import javax.annotation.Generated;
import org.sonarqube.ws.MediaTypes;
-import org.sonarqube.ws.Qualitygates.CreateConditionResponse;
+import org.sonarqube.ws.client.BaseService;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.WsConnector;
import org.sonarqube.ws.Qualitygates.CreateResponse;
+import org.sonarqube.ws.Qualitygates.CreateConditionResponse;
import org.sonarqube.ws.Qualitygates.GetByProjectResponse;
import org.sonarqube.ws.Qualitygates.ListWsResponse;
import org.sonarqube.ws.Qualitygates.ProjectStatusResponse;
import org.sonarqube.ws.Qualitygates.SearchResponse;
import org.sonarqube.ws.Qualitygates.ShowWsResponse;
import org.sonarqube.ws.Qualitygates.UpdateConditionResponse;
-import org.sonarqube.ws.client.BaseService;
-import org.sonarqube.ws.client.GetRequest;
-import org.sonarqube.ws.client.PostRequest;
-import org.sonarqube.ws.client.WsConnector;
/**
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/qualitygates">Further information about this web service online</a>
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/RenameRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/RenameRequest.java
index f08636bda92..d98af61c139 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/RenameRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/RenameRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SearchRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SearchRequest.java
index ec39eb47fff..a7bcf5b94bd 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SearchRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SearchRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SelectRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SelectRequest.java
index 146cda0c09e..e5bd51005c5 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SelectRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SelectRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SetAsDefaultRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SetAsDefaultRequest.java
index 7bdc088ec18..2692abf157c 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SetAsDefaultRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/SetAsDefaultRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ShowRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ShowRequest.java
index 5bdcfa8214f..b36b8a4e2f5 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ShowRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/ShowRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/UpdateConditionRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/UpdateConditionRequest.java
index 77934588782..8435549d125 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/UpdateConditionRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/qualitygates/UpdateConditionRequest.java
@@ -19,6 +19,7 @@
*/
package org.sonarqube.ws.client.qualitygates;
+import java.util.List;
import javax.annotation.Generated;
/**
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/rules/SearchRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/rules/SearchRequest.java
index 0a15f7f8500..bb974145015 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/rules/SearchRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/rules/SearchRequest.java
@@ -162,6 +162,7 @@ public class SearchRequest {
* <li>"remFn"</li>
* <li>"remFnOverloaded"</li>
* <li>"repo"</li>
+ * <li>"scope"</li>
* <li>"severity"</li>
* <li>"status"</li>
* <li>"sysTags"</li>
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ListDefinitionsRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ListDefinitionsRequest.java
index 97859d258cd..716f8c45347 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ListDefinitionsRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ListDefinitionsRequest.java
@@ -33,6 +33,7 @@ public class ListDefinitionsRequest {
private String branch;
private String component;
+ private String pullRequest;
/**
* This is part of the internal API.
@@ -58,4 +59,17 @@ public class ListDefinitionsRequest {
public String getComponent() {
return component;
}
+
+ /**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public ListDefinitionsRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ResetRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ResetRequest.java
index 8d47493a4b9..6f1e6d771da 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ResetRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ResetRequest.java
@@ -34,6 +34,7 @@ public class ResetRequest {
private String branch;
private String component;
private List<String> keys;
+ private String pullRequest;
/**
* This is part of the internal API.
@@ -72,4 +73,17 @@ public class ResetRequest {
public List<String> getKeys() {
return keys;
}
+
+ /**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public ResetRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/SetRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/SetRequest.java
index ef5da8de676..819d0bf0d8b 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/SetRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/SetRequest.java
@@ -35,6 +35,7 @@ public class SetRequest {
private String component;
private List<String> fieldValues;
private String key;
+ private String pullRequest;
private String value;
private List<String> values;
@@ -89,6 +90,19 @@ public class SetRequest {
}
/**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public SetRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ /**
* Example value: "git@github.com:SonarSource/sonarqube.git"
*/
public SetRequest setValue(String value) {
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/SettingsService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/SettingsService.java
index f920dbb7a1a..d3b64a5579e 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/SettingsService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/SettingsService.java
@@ -93,7 +93,8 @@ public class SettingsService extends BaseService {
return call(
new GetRequest(path("list_definitions"))
.setParam("branch", request.getBranch())
- .setParam("component", request.getComponent()),
+ .setParam("component", request.getComponent())
+ .setParam("pullRequest", request.getPullRequest()),
ListDefinitionsWsResponse.parser());
}
@@ -110,6 +111,7 @@ public class SettingsService extends BaseService {
.setParam("branch", request.getBranch())
.setParam("component", request.getComponent())
.setParam("keys", request.getKeys() == null ? null : request.getKeys().stream().collect(Collectors.joining(",")))
+ .setParam("pullRequest", request.getPullRequest())
.setMediaType(MediaTypes.JSON)
).content();
}
@@ -128,6 +130,7 @@ public class SettingsService extends BaseService {
.setParam("component", request.getComponent())
.setParam("fieldValues", request.getFieldValues() == null ? null : request.getFieldValues())
.setParam("key", request.getKey())
+ .setParam("pullRequest", request.getPullRequest())
.setParam("value", request.getValue())
.setParam("values", request.getValues() == null ? null : request.getValues())
.setMediaType(MediaTypes.JSON)
@@ -146,7 +149,8 @@ public class SettingsService extends BaseService {
new GetRequest(path("values"))
.setParam("branch", request.getBranch())
.setParam("component", request.getComponent())
- .setParam("keys", request.getKeys() == null ? null : request.getKeys().stream().collect(Collectors.joining(","))),
+ .setParam("keys", request.getKeys() == null ? null : request.getKeys().stream().collect(Collectors.joining(",")))
+ .setParam("pullRequest", request.getPullRequest()),
ValuesWsResponse.parser());
}
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ValuesRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ValuesRequest.java
index 3d7da5ea1cd..be37e21c8d0 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ValuesRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/settings/ValuesRequest.java
@@ -34,6 +34,7 @@ public class ValuesRequest {
private String branch;
private String component;
private List<String> keys;
+ private String pullRequest;
/**
* This is part of the internal API.
@@ -71,4 +72,17 @@ public class ValuesRequest {
public List<String> getKeys() {
return keys;
}
+
+ /**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public ValuesRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/sources/LinesRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/sources/LinesRequest.java
index c100ee46691..00848510f63 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/sources/LinesRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/sources/LinesRequest.java
@@ -34,6 +34,7 @@ public class LinesRequest {
private String branch;
private String from;
private String key;
+ private String pullRequest;
private String to;
private String uuid;
@@ -75,6 +76,19 @@ public class LinesRequest {
}
/**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public LinesRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ /**
* Example value: "20"
*/
public LinesRequest setTo(String to) {
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/sources/RawRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/sources/RawRequest.java
index 7fccf661dbf..5a7652b6717 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/sources/RawRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/sources/RawRequest.java
@@ -33,6 +33,7 @@ public class RawRequest {
private String branch;
private String key;
+ private String pullRequest;
/**
* This is part of the internal API.
@@ -59,4 +60,17 @@ public class RawRequest {
public String getKey() {
return key;
}
+
+ /**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public RawRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/sources/SourcesService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/sources/SourcesService.java
index 37795a4e13c..f8fe1a17b1e 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/sources/SourcesService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/sources/SourcesService.java
@@ -82,6 +82,7 @@ public class SourcesService extends BaseService {
.setParam("branch", request.getBranch())
.setParam("from", request.getFrom())
.setParam("key", request.getKey())
+ .setParam("pullRequest", request.getPullRequest())
.setParam("to", request.getTo())
.setParam("uuid", request.getUuid())
.setMediaType(MediaTypes.JSON)
@@ -100,6 +101,7 @@ public class SourcesService extends BaseService {
new GetRequest(path("raw"))
.setParam("branch", request.getBranch())
.setParam("key", request.getKey())
+ .setParam("pullRequest", request.getPullRequest())
.setMediaType(MediaTypes.JSON)
).content();
}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/tests/ListRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/tests/ListRequest.java
index 6e445f06c85..a7a38ca8cd6 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/tests/ListRequest.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/tests/ListRequest.java
@@ -34,6 +34,7 @@ public class ListRequest {
private String branch;
private String p;
private String ps;
+ private String pullRequest;
private String sourceFileId;
private String sourceFileKey;
private String sourceFileLineNumber;
@@ -79,6 +80,19 @@ public class ListRequest {
}
/**
+ * This is part of the internal API.
+ * Example value: "5461"
+ */
+ public ListRequest setPullRequest(String pullRequest) {
+ this.pullRequest = pullRequest;
+ return this;
+ }
+
+ public String getPullRequest() {
+ return pullRequest;
+ }
+
+ /**
* Example value: "AU-TpxcA-iU5OvuD2FL0"
*/
public ListRequest setSourceFileId(String sourceFileId) {
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/tests/TestsService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/tests/TestsService.java
index 2552c9a1a99..3683b5d79c7 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/tests/TestsService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/tests/TestsService.java
@@ -72,6 +72,7 @@ public class TestsService extends BaseService {
.setParam("branch", request.getBranch())
.setParam("p", request.getP())
.setParam("ps", request.getPs())
+ .setParam("pullRequest", request.getPullRequest())
.setParam("sourceFileId", request.getSourceFileId())
.setParam("sourceFileKey", request.getSourceFileKey())
.setParam("sourceFileLineNumber", request.getSourceFileLineNumber())
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/users/SetHomepageRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/users/SetHomepageRequest.java
new file mode 100644
index 00000000000..8ad79cc9590
--- /dev/null
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/users/SetHomepageRequest.java
@@ -0,0 +1,67 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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.users;
+
+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/users/set_homepage">Further information about this action online (including a response example)</a>
+ * @since 7.0
+ */
+@Generated("sonar-ws-generator")
+public class SetHomepageRequest {
+
+ private String parameter;
+ private String type;
+
+ /**
+ * Example value: "my_project"
+ */
+ public SetHomepageRequest setParameter(String parameter) {
+ this.parameter = parameter;
+ return this;
+ }
+
+ public String getParameter() {
+ return parameter;
+ }
+
+ /**
+ * This is a mandatory parameter.
+ * Possible values:
+ * <ul>
+ * <li>"PROJECT"</li>
+ * <li>"ORGANIZATION"</li>
+ * <li>"MY_PROJECTS"</li>
+ * <li>"MY_ISSUES"</li>
+ * </ul>
+ */
+ public SetHomepageRequest setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ public String getType() {
+ return type;
+ }
+}
diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UsersService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UsersService.java
index c490b22eeb0..3dcec5eaa39 100644
--- a/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UsersService.java
+++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/users/UsersService.java
@@ -160,6 +160,22 @@ public class UsersService extends BaseService {
*
* This is part of the internal API.
* This is a POST request.
+ * @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/users/set_homepage">Further information about this action online (including a response example)</a>
+ * @since 7.0
+ */
+ public void setHomepage(SetHomepageRequest request) {
+ call(
+ new PostRequest(path("set_homepage"))
+ .setParam("parameter", request.getParameter())
+ .setParam("type", request.getType())
+ .setMediaType(MediaTypes.JSON)
+ ).content();
+ }
+
+ /**
+ *
+ * This is part of the internal API.
+ * This is a POST request.
* @see <a href="https://next.sonarqube.com/sonarqube/web_api/api/users/skip_onboarding_tutorial">Further information about this action online (including a response example)</a>
* @since 6.5
*/
diff --git a/sonar-ws/src/main/protobuf/ws-ce.proto b/sonar-ws/src/main/protobuf/ws-ce.proto
index dc18f7cb87e..5787a326e5e 100644
--- a/sonar-ws/src/main/protobuf/ws-ce.proto
+++ b/sonar-ws/src/main/protobuf/ws-ce.proto
@@ -92,6 +92,8 @@ message Task {
optional string branch = 21;
optional sonarqube.ws.commons.BranchType branchType = 22;
optional string errorType = 23;
+ optional string pullRequest = 24;
+ optional string pullRequestTitle = 25;
}
enum TaskStatus {
diff --git a/sonar-ws/src/main/protobuf/ws-commons.proto b/sonar-ws/src/main/protobuf/ws-commons.proto
index b5b0167e8d0..947e2487657 100644
--- a/sonar-ws/src/main/protobuf/ws-commons.proto
+++ b/sonar-ws/src/main/protobuf/ws-commons.proto
@@ -127,4 +127,5 @@ enum BranchType {
LONG = 1;
SHORT = 2;
+ PULL_REQUEST = 3;
}
diff --git a/sonar-ws/src/main/protobuf/ws-components.proto b/sonar-ws/src/main/protobuf/ws-components.proto
index 73059133649..a995eb42cbc 100644
--- a/sonar-ws/src/main/protobuf/ws-components.proto
+++ b/sonar-ws/src/main/protobuf/ws-components.proto
@@ -122,6 +122,7 @@ message Component {
optional string project = 17;
optional string branch = 18;
optional string version = 19;
+ optional string pullRequest = 20;
message Tags {
repeated string tags = 1;
diff --git a/sonar-ws/src/main/protobuf/ws-duplications.proto b/sonar-ws/src/main/protobuf/ws-duplications.proto
index 6eeb5f29a32..98cbe46885c 100644
--- a/sonar-ws/src/main/protobuf/ws-duplications.proto
+++ b/sonar-ws/src/main/protobuf/ws-duplications.proto
@@ -52,4 +52,5 @@ message File {
string subProjectUuid = 8;
string subProjectName = 9;
string branch = 10;
+ string pullRequest = 11;
}
diff --git a/sonar-ws/src/main/protobuf/ws-issues.proto b/sonar-ws/src/main/protobuf/ws-issues.proto
index c869c9c1ff3..125f6e4c216 100644
--- a/sonar-ws/src/main/protobuf/ws-issues.proto
+++ b/sonar-ws/src/main/protobuf/ws-issues.proto
@@ -156,6 +156,7 @@ message Issue {
optional string organization = 29;
optional string branch = 30;
+ optional string pullRequest = 32;
}
message Transitions {
@@ -234,6 +235,7 @@ message Component {
optional int64 unusedProjectId = 9;
optional int64 unusedSubProjectId = 10;
optional string branch = 12;
+ optional string pullRequest = 13;
}
// Response of GET api/issues/changelog
diff --git a/sonar-ws/src/main/protobuf/ws-measures.proto b/sonar-ws/src/main/protobuf/ws-measures.proto
index 36cd4ab6174..c861426c3ea 100644
--- a/sonar-ws/src/main/protobuf/ws-measures.proto
+++ b/sonar-ws/src/main/protobuf/ws-measures.proto
@@ -76,6 +76,7 @@ message Component {
optional string language = 10;
repeated Measure measures = 11;
optional string branch = 12;
+ optional string pullRequest = 13;
}
message Period {
diff --git a/sonar-ws/src/main/protobuf/ws-projectbranches.proto b/sonar-ws/src/main/protobuf/ws-projectbranches.proto
index 55d8e4032ce..e75439ab06e 100644
--- a/sonar-ws/src/main/protobuf/ws-projectbranches.proto
+++ b/sonar-ws/src/main/protobuf/ws-projectbranches.proto
@@ -45,16 +45,15 @@ message Branch {
optional Status status = 5;
optional bool isOrphan = 6;
optional string analysisDate = 7;
+}
- message Status {
- // Quality gate status is only present for long living branch
- optional string qualityGateStatus = 1;
- // Merge bugs, vulnerabilities and codeSmell are only present for short living branch
- optional int64 bugs = 2;
- optional int64 vulnerabilities = 3;
- optional int64 codeSmells = 4;
- }
-
+message Status {
+ // Quality gate status is only present for long living branch
+ optional string qualityGateStatus = 1;
+ // Merge bugs, vulnerabilities and codeSmell are only present for short living branch
+ optional int64 bugs = 2;
+ optional int64 vulnerabilities = 3;
+ optional int64 codeSmells = 4;
}
diff --git a/sonar-ws/src/main/protobuf/ws-projectpullrequests.proto b/sonar-ws/src/main/protobuf/ws-projectpullrequests.proto
new file mode 100644
index 00000000000..e6bc0300953
--- /dev/null
+++ b/sonar-ws/src/main/protobuf/ws-projectpullrequests.proto
@@ -0,0 +1,49 @@
+// SonarQube, open source software quality management tool.
+// Copyright (C) 2008-2016 SonarSource
+// mailto:contact AT sonarsource DOT com
+//
+// SonarQube 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.
+//
+// SonarQube 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.
+
+syntax = "proto2";
+
+package sonarqube.ws.projectpullrequest;
+
+option java_package = "org.sonarqube.ws";
+option java_outer_classname = "ProjectPullRequests";
+option optimize_for = SPEED;
+
+import "ws-commons.proto";
+
+// WS api/project_pull_requests/list
+message ListWsResponse {
+ repeated PullRequest pullRequests = 1;
+}
+
+message PullRequest {
+ optional string key = 1;
+ optional string title = 2;
+ optional string branch = 3;
+ optional string base = 4;
+ optional Status status = 5;
+ optional bool isOrphan = 6;
+ optional string analysisDate = 7;
+ optional string url = 8;
+}
+
+message Status {
+ optional int64 bugs = 2;
+ optional int64 vulnerabilities = 3;
+ optional int64 codeSmells = 4;
+}
diff --git a/sonar-ws/src/main/protobuf/ws-tests.proto b/sonar-ws/src/main/protobuf/ws-tests.proto
index 74865970ca6..6d389e735bd 100644
--- a/sonar-ws/src/main/protobuf/ws-tests.proto
+++ b/sonar-ws/src/main/protobuf/ws-tests.proto
@@ -42,6 +42,7 @@ message CoveredFilesResponse {
optional string longName = 3;
optional int32 coveredLines = 4;
optional string branch = 5;
+ optional string pullRequest = 6;
}
}
@@ -58,6 +59,7 @@ message Test {
optional string message = 9;
optional string stacktrace = 10;
optional string fileBranch = 11;
+ optional string filePullRequest = 12;
}
enum TestStatus {
diff --git a/tests/src/test/java/org/sonarqube/tests/issue/AutoAssignTest.java b/tests/src/test/java/org/sonarqube/tests/issue/AutoAssignTest.java
index 258ef9b92f3..7fbf43db21e 100644
--- a/tests/src/test/java/org/sonarqube/tests/issue/AutoAssignTest.java
+++ b/tests/src/test/java/org/sonarqube/tests/issue/AutoAssignTest.java
@@ -185,7 +185,7 @@ public class AutoAssignTest extends AbstractIssueTest {
.setName(name)
.setEmail(email)
.setPassword("xxxxxxx")
- .setScmAccounts(asList(scmAccounts)));
+ .setScmAccount(asList(scmAccounts)));
}
private static void deleteAllUsers() {
diff --git a/tests/src/test/java/org/sonarqube/tests/issue/IssueCreationDatePluginChangedTest.java b/tests/src/test/java/org/sonarqube/tests/issue/IssueCreationDatePluginChangedTest.java
index cff4e0f1aa5..76ae03a0a8b 100644
--- a/tests/src/test/java/org/sonarqube/tests/issue/IssueCreationDatePluginChangedTest.java
+++ b/tests/src/test/java/org/sonarqube/tests/issue/IssueCreationDatePluginChangedTest.java
@@ -95,7 +95,7 @@ public class IssueCreationDatePluginChangedTest {
// Create a user and register her to receive notification on NewIssues
tester.users().generate(t -> t.setLogin(USER_LOGIN).setPassword(USER_PASSWORD).setEmail(USER_EMAIL)
- .setScmAccounts(ImmutableList.of("jhenry")));
+ .setScmAccount(ImmutableList.of("jhenry")));
// Add notifications to the test user
WsClient wsClient = newUserWsClient(ORCHESTRATOR, USER_LOGIN, USER_PASSWORD);
wsClient.wsConnector().call(new PostRequest("api/notifications/add")
diff --git a/tests/src/test/java/org/sonarqube/tests/user/LocalAuthenticationTest.java b/tests/src/test/java/org/sonarqube/tests/user/LocalAuthenticationTest.java
index 3f56905e7b2..51e258bdfb8 100644
--- a/tests/src/test/java/org/sonarqube/tests/user/LocalAuthenticationTest.java
+++ b/tests/src/test/java/org/sonarqube/tests/user/LocalAuthenticationTest.java
@@ -241,7 +241,7 @@ public class LocalAuthenticationTest {
.setLogin("test")
.setName("Test")
.setEmail("test@email.com")
- .setScmAccounts(asList("test1", "test2"))
+ .setScmAccount(asList("test1", "test2"))
.setPassword("password"));
assertThat(checkAuthenticationWithAuthenticateWebService("test", "password")).isTrue();
diff --git a/travis.sh b/travis.sh
index eaf9879c83c..5386944b148 100755
--- a/travis.sh
+++ b/travis.sh
@@ -34,7 +34,7 @@ function installNode {
#
function configureTravis {
mkdir -p ~/.local
- curl -sSL https://github.com/SonarSource/travis-utils/tarball/v41 | tar zx --strip-components 1 -C ~/.local
+ curl -sSL https://github.com/SonarSource/travis-utils/tarball/v47 | tar zx --strip-components 1 -C ~/.local
source ~/.local/bin/install
}
configureTravis