From 469af3846a5ca3f78b2b190ec4a2b27e3c7fd11f Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Thu, 30 Aug 2018 11:07:00 +0200 Subject: [PATCH] SONAR-11185 Allow portfolio to define projects by tags * SONAR-11199 Allow selection of no project mode * Add tags in views definition * Create api/views/set_tags_mode * Return tags mode in api/views/show * Take into account tags during portfolio computation --- .../LoadReportAnalysisMetadataHolderStep.java | 2 +- .../webhook/WebhookPostTask.java | 4 +- .../AnalysisMetadataHolderImplTest.java | 9 ++- .../component/BranchPersisterImplTest.java | 4 +- .../component/ComponentTreeBuilderTest.java | 10 ++- .../ConfigurationRepositoryTest.java | 14 ++-- ...PostMeasuresComputationChecksStepTest.java | 7 +- .../step/BuildComponentTreeStepTest.java | 9 ++- .../step/PersistLiveMeasuresStepTest.java | 5 +- .../step/QualityGateEventsStepTest.java | 3 +- .../step/ReportPersistComponentsStepTest.java | 4 +- .../webhook/WebhookPostTaskTest.java | 2 +- .../org/sonar/db/component/ComponentDao.java | 6 +- .../sonar/db/component/ComponentMapper.java | 2 +- .../sonar/db/component/ComponentMapper.xml | 7 +- .../sonar/db/component/ComponentDaoTest.java | 19 ++--- .../sonar/server/issue/IssueFieldsSetter.java | 8 +- .../org/sonar/server/project/Project.java | 15 ++-- .../org/sonar/server/webhook/Project.java | 79 +++++++++++++++++++ .../sonar/server/webhook/ProjectAnalysis.java | 1 - .../webhook/WebhookPayloadFactoryImpl.java | 1 - .../org/sonar/server/project/ProjectTest.java | 23 +++--- .../server/webhook/ProjectAnalysisTest.java | 3 +- .../WebhookPayloadFactoryImplTest.java | 1 - .../server/component/ComponentService.java | 5 +- .../server/organization/ws/DeleteAction.java | 2 +- .../webhook/WebhookQGChangeEventListener.java | 1 - .../ComponentServiceUpdateKeyTest.java | 5 +- .../server/issue/ws/SetTagsActionTest.java | 6 +- .../organization/ws/DeleteActionTest.java | 6 -- .../ProjectLifeCycleListenersImplTest.java | 12 +-- .../server/project/RekeyedProjectTest.java | 7 +- .../WebhookQGChangeEventListenerTest.java | 3 +- .../sonar/api/server/rule/RuleTagFormat.java | 28 ++++++- .../api/server/rule/RuleTagFormatTest.java | 23 ++++++ .../ws/client/views/SetTagsModeRequest.java | 60 ++++++++++++++ .../ws/client/views/ViewsService.java | 17 ++++ 37 files changed, 313 insertions(+), 100 deletions(-) create mode 100644 server/sonar-server-common/src/main/java/org/sonar/server/webhook/Project.java create mode 100644 sonar-ws/src/main/java/org/sonarqube/ws/client/views/SetTagsModeRequest.java diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java index 5b09d232bdd..de73734d68a 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java @@ -111,7 +111,7 @@ public class LoadReportAnalysisMetadataHolderStep implements ComputationStep { ceTask.getComponentUuid())); } ComponentDto dto = toProject(reportProjectKey); - analysisMetadata.setProject(new Project(dto.uuid(), dto.getDbKey(), dto.name(), dto.description())); + analysisMetadata.setProject(Project.from(dto)); return () -> { if (!componentKey.equals(reportProjectKey)) { throw MessageException.of(format( diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/webhook/WebhookPostTask.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/webhook/WebhookPostTask.java index a4262fc1cf9..df697738b02 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/webhook/WebhookPostTask.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/webhook/WebhookPostTask.java @@ -25,13 +25,13 @@ import java.util.Set; import org.sonar.api.ce.posttask.PostProjectAnalysisTask; import org.sonar.api.measures.Metric; import org.sonar.core.util.stream.MoreCollectors; -import org.sonar.server.project.Project; import org.sonar.server.qualitygate.Condition; import org.sonar.server.qualitygate.EvaluatedCondition; import org.sonar.server.qualitygate.EvaluatedQualityGate; import org.sonar.server.webhook.Analysis; import org.sonar.server.webhook.Branch; import org.sonar.server.webhook.CeTask; +import org.sonar.server.webhook.Project; import org.sonar.server.webhook.WebHooks; import org.sonar.server.webhook.WebhookPayloadFactory; @@ -57,7 +57,6 @@ public class WebhookPostTask implements PostProjectAnalysisTask { private static org.sonar.server.webhook.ProjectAnalysis convert(ProjectAnalysis projectAnalysis) { CeTask ceTask = new CeTask(projectAnalysis.getCeTask().getId(), CeTask.Status.valueOf(projectAnalysis.getCeTask().getStatus().name())); - Project project = new Project(projectAnalysis.getProject().getUuid(), projectAnalysis.getProject().getKey(), projectAnalysis.getProject().getName()); Analysis analysis = projectAnalysis.getAnalysis().map(a -> new Analysis(a.getAnalysisUuid(), a.getDate().getTime())).orElse(null); Branch branch = projectAnalysis.getBranch().map(b -> new Branch(b.isMain(), b.getName().orElse(null), Branch.Type.valueOf(b.getType().name()))).orElse(null); EvaluatedQualityGate qualityGate = Optional.ofNullable(projectAnalysis.getQualityGate()) @@ -81,6 +80,7 @@ public class WebhookPostTask implements PostProjectAnalysisTask { Long date = projectAnalysis.getAnalysis().map(a -> a.getDate().getTime()).orElse(null); Map properties = projectAnalysis.getScannerContext().getProperties(); + Project project = new Project(projectAnalysis.getProject().getUuid(), projectAnalysis.getProject().getKey(), projectAnalysis.getProject().getName()); return new org.sonar.server.webhook.ProjectAnalysis(project, ceTask, analysis, branch, qualityGate, date, properties); } } diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java index d5084ce526c..d777dc9c40d 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java @@ -30,6 +30,8 @@ import org.sonar.server.project.Project; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; +import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto; public class AnalysisMetadataHolderImplTest { @@ -274,7 +276,7 @@ public class AnalysisMetadataHolderImplTest { public void set_and_get_project() { AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(); - Project project = new Project("U", "K", "N"); + Project project = Project.from(newPrivateProjectDto(newOrganizationDto())); underTest.setProject(project); assertThat(underTest.getProject()).isSameAs(project); @@ -291,11 +293,12 @@ public class AnalysisMetadataHolderImplTest { @Test public void setProject_throws_ISE_when_called_twice() { AnalysisMetadataHolderImpl underTest = new AnalysisMetadataHolderImpl(); - underTest.setProject(new Project("U", "K", "N")); + underTest.setProject(Project.from(newPrivateProjectDto(newOrganizationDto()))); expectedException.expect(IllegalStateException.class); expectedException.expectMessage("Project has already been set"); - underTest.setProject(new Project("U", "K", "N")); + + underTest.setProject(Project.from(newPrivateProjectDto(newOrganizationDto()))); } @Test diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImplTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImplTest.java index 6cf1c26a2f2..822a25f67eb 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImplTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImplTest.java @@ -71,7 +71,7 @@ public class BranchPersisterImplTest { // add main branch in project table and in metadata ComponentDto dto = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert(), MAIN.getUuid()).setDbKey(MAIN.getKey()); - analysisMetadataHolder.setProject(new Project(dto.uuid(), dto.getDbKey(), dto.name())); + analysisMetadataHolder.setProject(Project.from(dto)); dbTester.getDbClient().componentDao().insert(dbTester.getSession(), dto); // this should add new columns in project and project_branches @@ -92,7 +92,7 @@ public class BranchPersisterImplTest { // add main branch in project table and in metadata ComponentDto dto = ComponentTesting.newPrivateProjectDto(dbTester.organizations().insert(), MAIN.getUuid()).setDbKey(MAIN.getKey()); - analysisMetadataHolder.setProject(new Project(dto.uuid(), dto.getKey(), dto.name(), dto.description())); + analysisMetadataHolder.setProject(Project.from(dto)); dbTester.getDbClient().componentDao().insert(dbTester.getSession(), dto); // this should add new columns in project and project_branches diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java index 5cc30eff7ed..a598a179dbb 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java @@ -31,10 +31,10 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.rules.ExternalResource; import org.mockito.Mockito; +import org.sonar.ce.task.projectanalysis.analysis.Branch; import org.sonar.core.component.ComponentKeys; import org.sonar.db.component.SnapshotDto; import org.sonar.scanner.protocol.output.ScannerReport; -import org.sonar.ce.task.projectanalysis.analysis.Branch; import org.sonar.server.project.Project; import static com.google.common.base.Preconditions.checkArgument; @@ -42,13 +42,15 @@ import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; import static org.mockito.Mockito.when; +import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER; +import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; +import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto; +import static org.sonar.scanner.protocol.output.ScannerReport.Component.newBuilder; import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.DIRECTORY; import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.FILE; import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.MODULE; import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.PROJECT; import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.UNRECOGNIZED; -import static org.sonar.scanner.protocol.output.ScannerReport.Component.newBuilder; -import static org.sonar.ce.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER; public class ComponentTreeBuilderTest { @@ -66,7 +68,7 @@ public class ComponentTreeBuilderTest { @Rule public ScannerComponentProvider scannerComponentProvider = new ScannerComponentProvider(); - private Project projectInDb = new Project(UUID_SUPPLIER.apply("K1"), "K1", "theProjectName"); + private Project projectInDb = Project.from(newPrivateProjectDto(newOrganizationDto(), UUID_SUPPLIER.apply("K1")).setDbKey("K1").setDescription(null)); @Test public void build_throws_IAE_for_all_types_but_PROJECT_MODULE_DIRECTORY_FILE() { diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ConfigurationRepositoryTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ConfigurationRepositoryTest.java index edf912d9b64..a9f81e8d615 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ConfigurationRepositoryTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ConfigurationRepositoryTest.java @@ -37,10 +37,12 @@ import org.sonar.server.project.Project; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; +import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; +import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto; public class ConfigurationRepositoryTest { - private static Project PROJECT = new Project("UUID", "KEY", "NAME"); + private static Project PROJECT = Project.from(newPrivateProjectDto(newOrganizationDto())); @Rule public final DbTester db = DbTester.create(System2.INSTANCE); @@ -71,7 +73,7 @@ public class ConfigurationRepositoryTest { @Test public void get_project_settings_from_db() { ComponentDto project = db.components().insertPrivateProject(); - analysisMetadataHolder.setProject(new Project(project.uuid(), project.getDbKey(), project.name())); + analysisMetadataHolder.setProject(Project.from(project)); insertProjectProperty(project, "key", "value"); Configuration config = underTest.getConfiguration(); @@ -96,7 +98,7 @@ public class ConfigurationRepositoryTest { globalSettings.setProperty("key", "value1"); ComponentDto project = db.components().insertPrivateProject(); insertProjectProperty(project, "key", "value2"); - analysisMetadataHolder.setProject(new Project(project.uuid(), project.getDbKey(), project.name())); + analysisMetadataHolder.setProject(Project.from(project)); Configuration config = underTest.getConfiguration(); assertThat(config.get("key")).hasValue("value2"); @@ -106,7 +108,7 @@ public class ConfigurationRepositoryTest { public void project_settings_are_cached_to_avoid_db_access() { ComponentDto project = db.components().insertPrivateProject(); insertProjectProperty(project, "key", "value"); - analysisMetadataHolder.setProject(new Project(project.uuid(), project.getDbKey(), project.name())); + analysisMetadataHolder.setProject(Project.from(project)); Configuration config = underTest.getConfiguration(); assertThat(config.get("key")).hasValue("value"); @@ -123,7 +125,9 @@ public class ConfigurationRepositoryTest { ComponentDto branchDto = db.components().insertProjectBranch(project); Branch branch = mock(Branch.class); when(branch.getName()).thenReturn(branchDto.getBranch()); - analysisMetadataHolder.setProject(new Project(project.uuid(), project.getDbKey(), project.name())).setBranch(branch); + analysisMetadataHolder + .setProject(Project.from(project)) + .setBranch(branch); globalSettings.setProperty("global", "global value"); insertProjectProperty(project, "project", "project value"); insertProjectProperty(branchDto, "branch", "branch value"); diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/measure/PostMeasuresComputationChecksStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/measure/PostMeasuresComputationChecksStepTest.java index c7bd59039df..5cf3b26fe1f 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/measure/PostMeasuresComputationChecksStepTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/measure/PostMeasuresComputationChecksStepTest.java @@ -42,6 +42,8 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.sonar.api.measures.CoreMetrics.NCLOC; import static org.sonar.ce.task.projectanalysis.component.ReportComponent.DUMB_PROJECT; +import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; +import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto; public class PostMeasuresComputationChecksStepTest { @@ -70,14 +72,15 @@ public class PostMeasuresComputationChecksStepTest { @Test public void context_contains_project_uuid_from_analysis_metada_holder() { - analysisMetadataHolder.setProject(new Project("project_uuid", "project_key", "project_name")); + Project project = Project.from(newPrivateProjectDto(newOrganizationDto())); + analysisMetadataHolder.setProject(project); PostMeasuresComputationCheck check = mock(PostMeasuresComputationCheck.class); newStep(check).execute(new TestComputationStepContext()); ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(Context.class); verify(check).onCheck(contextArgumentCaptor.capture()); - assertThat(contextArgumentCaptor.getValue().getProjectUuid()).isEqualTo("project_uuid"); + assertThat(contextArgumentCaptor.getValue().getProjectUuid()).isEqualTo(project.getUuid()); } @Test diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java index 2ac9cdbab6d..6268cdd3af5 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java @@ -59,6 +59,7 @@ import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.component.ComponentTesting.newModuleDto; import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; import static org.sonar.db.component.SnapshotTesting.newAnalysis; +import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto; import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.DIRECTORY; import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.FILE; import static org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType.MODULE; @@ -214,7 +215,7 @@ public class BuildComponentTreeStepTest { when(branch.generateKey(any(), any())).thenReturn("generated"); analysisMetadataHolder.setRootComponentRef(ROOT_REF) .setAnalysisDate(ANALYSIS_DATE) - .setProject(new Project("U1", REPORT_PROJECT_KEY, REPORT_PROJECT_KEY)) + .setProject(Project.from(newPrivateProjectDto(newOrganizationDto()).setDbKey(REPORT_PROJECT_KEY))) .setBranch(branch); BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder); reportReader.putComponent(componentWithKey(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF)); @@ -256,7 +257,7 @@ public class BuildComponentTreeStepTest { Branch branch = new DefaultBranchImpl(); analysisMetadataHolder.setRootComponentRef(ROOT_REF) .setAnalysisDate(ANALYSIS_DATE) - .setProject(new Project("U1", REPORT_PROJECT_KEY, REPORT_PROJECT_KEY)) + .setProject(Project.from(newPrivateProjectDto(newOrganizationDto()).setDbKey(REPORT_PROJECT_KEY))) .setBranch(branch); BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder); reportReader.putComponent(componentWithKey(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF)); @@ -276,7 +277,7 @@ public class BuildComponentTreeStepTest { public void generate_keys_when_using_legacy_branch() { analysisMetadataHolder.setRootComponentRef(ROOT_REF) .setAnalysisDate(ANALYSIS_DATE) - .setProject(new Project("U1", REPORT_PROJECT_KEY, REPORT_PROJECT_KEY)) + .setProject(Project.from(newPrivateProjectDto(newOrganizationDto()).setDbKey(REPORT_PROJECT_KEY))) .setBranch(new DefaultBranchImpl("origin/feature")); BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder); reportReader.putComponent(componentWithKey(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF)); @@ -474,7 +475,7 @@ public class BuildComponentTreeStepTest { analysisMetadataHolder.setRootComponentRef(ROOT_REF) .setAnalysisDate(ANALYSIS_DATE) .setBranch(new DefaultBranchImpl(null)) - .setProject(new Project("U1", REPORT_PROJECT_KEY, REPORT_PROJECT_KEY)); + .setProject(Project.from(newPrivateProjectDto(newOrganizationDto()).setDbKey(REPORT_PROJECT_KEY))); } } diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistLiveMeasuresStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistLiveMeasuresStepTest.java index a9e6aaf9205..53a143118bd 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistLiveMeasuresStepTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistLiveMeasuresStepTest.java @@ -42,6 +42,7 @@ import org.sonar.db.measure.LiveMeasureDto; import org.sonar.db.metric.MetricDto; import org.sonar.server.project.Project; +import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; import static org.sonar.ce.task.projectanalysis.component.Component.Type.DIRECTORY; import static org.sonar.ce.task.projectanalysis.component.Component.Type.FILE; @@ -248,7 +249,7 @@ public class PersistLiveMeasuresStepTest extends BaseStepTest { .build()) .build(); treeRootHolder.setRoot(project); - analysisMetadataHolder.setProject(new Project(project.getUuid(), project.getKey(), project.getName())); + analysisMetadataHolder.setProject(new Project(project.getUuid(), project.getKey(), project.getName(), project.getDescription(), emptyList())); // components as persisted in db ComponentDto projectDto = insertComponent("project-key", "project-uuid"); @@ -273,7 +274,7 @@ public class PersistLiveMeasuresStepTest extends BaseStepTest { ComponentDto portfolioDto = insertComponent("view-key", "view-uuid"); ComponentDto subViewDto = insertComponent("subview-key", "subview-uuid"); ComponentDto projectDto = insertComponent("project-key", "project-uuid"); - analysisMetadataHolder.setProject(new Project(portfolioDto.uuid(), portfolioDto.getDbKey(), portfolioDto.name())); + analysisMetadataHolder.setProject(Project.from(portfolioDto)); } private void assertThatMeasureIsNotPersisted(String componentUuid, Metric metric) { diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStepTest.java index 32caea964ba..25157a69c85 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStepTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStepTest.java @@ -44,6 +44,7 @@ import org.sonar.db.component.BranchType; import org.sonar.server.notification.NotificationService; import org.sonar.server.project.Project; +import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; @@ -88,7 +89,7 @@ public class QualityGateEventsStepTest { @Before public void setUp() { when(metricRepository.getByKey(ALERT_STATUS_KEY)).thenReturn(alertStatusMetric); - analysisMetadataHolder.setProject(new Project(PROJECT_COMPONENT.getUuid(), PROJECT_COMPONENT.getKey(), PROJECT_COMPONENT.getName())); + analysisMetadataHolder.setProject(new Project(PROJECT_COMPONENT.getUuid(), PROJECT_COMPONENT.getKey(), PROJECT_COMPONENT.getName(), PROJECT_COMPONENT.getDescription(), emptyList())); analysisMetadataHolder.setBranch(mock(Branch.class)); treeRootHolder.setRoot(PROJECT_COMPONENT); } diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportPersistComponentsStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportPersistComponentsStepTest.java index 454d6f910e4..2c284e10b9e 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportPersistComponentsStepTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportPersistComponentsStepTest.java @@ -895,14 +895,14 @@ public class ReportPersistComponentsStepTest extends BaseStepTest { private ComponentDto prepareProject(Consumer... populators) { ComponentDto dto = db.components().insertPrivateProject(db.organizations().insert(), populators); - analysisMetadataHolder.setProject(new Project(dto.uuid(), dto.getDbKey(), dto.name())); + analysisMetadataHolder.setProject(Project.from(dto)); analysisMetadataHolder.setBranch(new DefaultBranchImpl()); return dto; } private ComponentDto prepareBranch(String branchName, Consumer... populators) { ComponentDto dto = db.components().insertPrivateProject(db.organizations().insert(), populators); - analysisMetadataHolder.setProject(new Project(dto.uuid(), dto.getDbKey(), dto.name())); + analysisMetadataHolder.setProject(Project.from(dto)); analysisMetadataHolder.setBranch(new TestBranch(branchName)); return dto; } diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/webhook/WebhookPostTaskTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/webhook/WebhookPostTaskTest.java index 829f1b4f418..762f4c3d25f 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/webhook/WebhookPostTaskTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/webhook/WebhookPostTaskTest.java @@ -158,7 +158,7 @@ public class WebhookPostTaskTest { } verify(payloadFactory).create(new ProjectAnalysis( - new org.sonar.server.project.Project(project.getUuid(), project.getKey(), project.getName()), + new org.sonar.server.webhook.Project(project.getUuid(), project.getKey(), project.getName()), new org.sonar.server.webhook.CeTask(ceTask.getId(), org.sonar.server.webhook.CeTask.Status.valueOf(ceTask.getStatus().name())), analysisUUid == null ? null : new Analysis(analysisUUid, date.getTime()), new org.sonar.server.webhook.Branch(branch.isMain(), branch.getName().get(), org.sonar.server.webhook.Branch.Type.valueOf(branch.getType().name())), 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 ef7891ea62f..1e531a1e63a 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 @@ -299,12 +299,12 @@ public class ComponentDao implements Dao { } /** - * Select all root components (projects and views), including disabled ones, for a given organization. + * Select all projects for a given organization. * * Branches are not returned */ - public List selectAllRootsByOrganization(DbSession dbSession, String organizationUuid) { - return mapper(dbSession).selectAllRootsByOrganization(organizationUuid); + public List selectProjectsByOrganization(DbSession dbSession, String organizationUuid) { + return mapper(dbSession).selectProjectsByOrganization(organizationUuid); } public List selectGhostProjects(DbSession session, String organizationUuid, @Nullable String query, int offset, int limit) { 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 c8247cd6393..7d041d714ec 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 @@ -87,7 +87,7 @@ public interface ComponentMapper { */ List selectProjects(); - List selectAllRootsByOrganization(@Param("organizationUuid") String organizationUuid); + List selectProjectsByOrganization(@Param("organizationUuid") String organizationUuid); /** * Return all descendant modules (including itself) from a given component uuid and scope 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 4dcc61d5277..28e539a7de9 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 @@ -246,13 +246,14 @@ AND p.main_branch_project_uuid IS NULL - select from projects p where - p.scope='PRJ' - and (p.qualifier='TRK' or p.qualifier='VW' or p.qualifier='APP') + p.enabled=${_true} + and p.scope='PRJ' + and p.qualifier='TRK' and p.organization_uuid = #{organizationUuid,jdbcType=VARCHAR} and p.main_branch_project_uuid IS NULL diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java index b58ef55e99f..650c82a0000 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java @@ -699,7 +699,7 @@ public class ComponentDaoTest { @DataProvider public static Object[][] oneOrMoreProjects() { - return new Object[][]{ + return new Object[][] { {1}, {1 + new Random().nextInt(10)} }; @@ -928,7 +928,7 @@ public class ComponentDaoTest { @DataProvider public static Object[][] portfolioOrApplicationRootViewQualifier() { - return new Object[][]{ + return new Object[][] { {Qualifiers.VIEW}, {Qualifiers.APP}, }; @@ -1008,7 +1008,7 @@ public class ComponentDaoTest { } @Test - public void select_all_roots_by_organization() { + public void select_projects_by_organization() { OrganizationDto organization = db.organizations().insert(); ComponentDto project1 = db.components().insertPrivateProject(organization); ComponentDto module = db.components().insertComponent(newModuleDto(project1)); @@ -1020,18 +1020,19 @@ public class ComponentDaoTest { OrganizationDto otherOrganization = db.organizations().insert(); ComponentDto projectOnOtherOrganization = db.components().insertPrivateProject(otherOrganization); - assertThat(underTest.selectAllRootsByOrganization(dbSession, organization.getUuid())) + assertThat(underTest.selectProjectsByOrganization(dbSession, organization.getUuid())) .extracting(ComponentDto::uuid) - .containsExactlyInAnyOrder(project1.uuid(), project2.uuid(), view.uuid(), application.uuid()); + .containsExactlyInAnyOrder(project1.uuid(), project2.uuid()) + .doesNotContain(view.uuid(), application.uuid()); } @Test - public void select_all_roots_by_organization_does_not_return_branches() { + public void select_projects_by_organization_does_not_return_branches() { OrganizationDto organization = db.organizations().insert(); ComponentDto project = db.components().insertMainBranch(organization); ComponentDto branch = db.components().insertProjectBranch(project); - assertThat(underTest.selectAllRootsByOrganization(dbSession, organization.getUuid())) + assertThat(underTest.selectProjectsByOrganization(dbSession, organization.getUuid())) .extracting(ComponentDto::uuid) .containsExactlyInAnyOrder(project.uuid()) .doesNotContain(branch.uuid()); @@ -1164,7 +1165,6 @@ public class ComponentDaoTest { underTest.countByQuery(dbSession, query.build()); } - @Test public void countByNclocRanges_on_zero_projects() { db.measures().insertMetric(m -> m.setKey(CoreMetrics.NCLOC_KEY)); @@ -1183,7 +1183,8 @@ public class ComponentDaoTest { tuple("1M", 0L), tuple("+1M", 0L)); } - @Test + + @Test public void countByNclocRanges() { MetricDto ncloc = db.measures().insertMetric(m -> m.setKey(CoreMetrics.NCLOC_KEY)); diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java b/server/sonar-server-common/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java index 077bd3b6a1c..7f5abde500d 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java @@ -24,7 +24,6 @@ import java.time.temporal.ChronoUnit; import java.util.Collection; import java.util.Date; import java.util.HashSet; -import java.util.Locale; import java.util.Objects; import java.util.Set; import javax.annotation.Nullable; @@ -36,7 +35,6 @@ import org.sonar.api.utils.Duration; import org.sonar.core.issue.DefaultIssue; import org.sonar.core.issue.DefaultIssueComment; import org.sonar.core.issue.IssueChangeContext; -import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.user.UserDto; import static com.google.common.base.Preconditions.checkState; @@ -318,11 +316,7 @@ public class IssueFieldsSetter { } public boolean setTags(DefaultIssue issue, Collection tags, IssueChangeContext context) { - Set newTags = tags.stream() - .filter(Objects::nonNull) - .filter(tag -> !tag.isEmpty()) - .map(tag -> RuleTagFormat.validate(tag.toLowerCase(Locale.ENGLISH))) - .collect(MoreCollectors.toSet()); + Set newTags = RuleTagFormat.validate(tags); Set oldTags = new HashSet<>(issue.tags()); if (!oldTags.equals(newTags)) { diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/project/Project.java b/server/sonar-server-common/src/main/java/org/sonar/server/project/Project.java index af220370e34..4576e716aae 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/project/Project.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/project/Project.java @@ -19,6 +19,7 @@ */ package org.sonar.server.project; +import java.util.List; import java.util.Objects; import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; @@ -31,20 +32,18 @@ public class Project { private final String key; private final String name; private final String description; + private final List tags; - public Project(String uuid, String key, String name) { - this(uuid, key, name, null); - } - - public Project(String uuid, String key, String name, @Nullable String description) { + public Project(String uuid, String key, String name, @Nullable String description, List tags) { this.uuid = uuid; this.key = key; this.name = name; this.description = description; + this.tags = tags; } public static Project from(ComponentDto project) { - return new Project(project.uuid(), project.getDbKey(), project.name(), project.description()); + return new Project(project.uuid(), project.getKey(), project.name(), project.description(), project.getTags()); } /** @@ -69,6 +68,10 @@ public class Project { return description; } + public List getTags() { + return tags; + } + @Override public boolean equals(Object o) { if (this == o) { diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/webhook/Project.java b/server/sonar-server-common/src/main/java/org/sonar/server/webhook/Project.java new file mode 100644 index 00000000000..9ec7c2c6764 --- /dev/null +++ b/server/sonar-server-common/src/main/java/org/sonar/server/webhook/Project.java @@ -0,0 +1,79 @@ +/* + * 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.webhook; + +import java.util.Objects; +import javax.annotation.concurrent.Immutable; + +@Immutable +public class Project { + + private final String uuid; + private final String key; + private final String name; + + public Project(String uuid, String key, String name) { + this.uuid = uuid; + this.key = key; + this.name = name; + } + + public String getUuid() { + return uuid; + } + + public String getKey() { + return key; + } + + public String getName() { + return name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Project project = (Project) o; + return Objects.equals(uuid, project.uuid) && + Objects.equals(key, project.key) && + Objects.equals(name, project.name); + } + + @Override + public int hashCode() { + return Objects.hash(uuid, key, name); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("Project{"); + sb.append("uuid='").append(uuid).append('\''); + sb.append(", key='").append(key).append('\''); + sb.append(", name='").append(name).append('\''); + sb.append('}'); + return sb.toString(); + } + +} diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/webhook/ProjectAnalysis.java b/server/sonar-server-common/src/main/java/org/sonar/server/webhook/ProjectAnalysis.java index 3a9175adbfb..2e055ad77f6 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/webhook/ProjectAnalysis.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/webhook/ProjectAnalysis.java @@ -23,7 +23,6 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import javax.annotation.Nullable; -import org.sonar.server.project.Project; import org.sonar.server.qualitygate.EvaluatedQualityGate; import static com.google.common.collect.ImmutableMap.copyOf; diff --git a/server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookPayloadFactoryImpl.java b/server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookPayloadFactoryImpl.java index d6b9b298ec6..51f93e22328 100644 --- a/server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookPayloadFactoryImpl.java +++ b/server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookPayloadFactoryImpl.java @@ -31,7 +31,6 @@ import org.sonar.api.platform.Server; import org.sonar.api.server.ServerSide; import org.sonar.api.utils.System2; import org.sonar.api.utils.text.JsonWriter; -import org.sonar.server.project.Project; import org.sonar.server.qualitygate.Condition; import org.sonar.server.qualitygate.EvaluatedCondition; import org.sonar.server.qualitygate.EvaluatedQualityGate; diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/project/ProjectTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/project/ProjectTest.java index 4f2a351e844..0f620f33b2b 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/project/ProjectTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/project/ProjectTest.java @@ -21,13 +21,14 @@ package org.sonar.server.project; import org.junit.Test; +import static java.util.Collections.emptyList; import static org.assertj.core.api.Assertions.assertThat; public class ProjectTest { @Test public void test_bean_without_description() { - Project project1 = new Project("U1", "K1", "N1"); - Project project2 = new Project("U1", "K1", "N1", null); + Project project1 = new Project("U1", "K1", "N1", null, emptyList()); + Project project2 = new Project("U1", "K1", "N1", null, emptyList()); assertThat(project1.getUuid()).isEqualTo(project2.getUuid()).isEqualTo("U1"); assertThat(project1.getKey()).isEqualTo(project2.getKey()).isEqualTo("K1"); @@ -41,7 +42,7 @@ public class ProjectTest { @Test public void test_bean_with_description() { - Project project1 = new Project("U1", "K1", "N1", "D1"); + Project project1 = new Project("U1", "K1", "N1", "D1", emptyList()); assertThat(project1.getUuid()).isEqualTo("U1"); assertThat(project1.getKey()).isEqualTo("K1"); @@ -55,23 +56,23 @@ public class ProjectTest { @Test public void test_equals_and_hashCode() { - Project project1 = new Project("U1", "K1", "N1"); - Project project2 = new Project("U1", "K1", "N1", "D1"); + Project project1 = new Project("U1", "K1", "N1", null, emptyList()); + Project project2 = new Project("U1", "K1", "N1", "D1", emptyList()); assertThat(project1).isEqualTo(project1); assertThat(project1).isNotEqualTo(null); assertThat(project1).isNotEqualTo(new Object()); - assertThat(project1).isEqualTo(new Project("U1", "K1", "N1", null)); - assertThat(project1).isNotEqualTo(new Project("U1", "K2", "N1", null)); - assertThat(project1).isNotEqualTo(new Project("U1", "K1", "N2", null)); + assertThat(project1).isEqualTo(new Project("U1", "K1", "N1", null, emptyList())); + assertThat(project1).isNotEqualTo(new Project("U1", "K2", "N1", null, emptyList())); + assertThat(project1).isNotEqualTo(new Project("U1", "K1", "N2", null, emptyList())); assertThat(project1).isEqualTo(project2); assertThat(project1.hashCode()).isEqualTo(project1.hashCode()); assertThat(project1.hashCode()).isNotEqualTo(null); assertThat(project1.hashCode()).isNotEqualTo(new Object().hashCode()); - assertThat(project1.hashCode()).isEqualTo(new Project("U1", "K1", "N1", null).hashCode()); - assertThat(project1.hashCode()).isNotEqualTo(new Project("U1", "K2", "N1", null).hashCode()); - assertThat(project1.hashCode()).isNotEqualTo(new Project("U1", "K1", "N2", null).hashCode()); + assertThat(project1.hashCode()).isEqualTo(new Project("U1", "K1", "N1", null, emptyList()).hashCode()); + assertThat(project1.hashCode()).isNotEqualTo(new Project("U1", "K2", "N1", null, emptyList()).hashCode()); + assertThat(project1.hashCode()).isNotEqualTo(new Project("U1", "K1", "N2", null, emptyList()).hashCode()); assertThat(project1.hashCode()).isEqualTo(project2.hashCode()); } diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/ProjectAnalysisTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/ProjectAnalysisTest.java index 0a453709b67..21c7cb87390 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/ProjectAnalysisTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/ProjectAnalysisTest.java @@ -25,7 +25,6 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.measures.Metric; -import org.sonar.server.project.Project; import org.sonar.server.qualitygate.EvaluatedQualityGate; import org.sonar.server.qualitygate.QualityGate; @@ -149,6 +148,6 @@ public class ProjectAnalysisTest { @Test public void verify_toString() { assertThat(underTest.toString()).isEqualTo( - "ProjectAnalysis{project=Project{uuid='uuid', key='key', name='name', description=null}, ceTask=CeTask{id='id', status=SUCCESS}, branch=Branch{main=true, name='name', type=SHORT}, qualityGate=EvaluatedQualityGate{qualityGate=QualityGate{id=id, name='name', conditions=[]}, status=WARN, evaluatedConditions=[]}, updatedAt=1, properties={a=b}, analysis=Analysis{uuid='analysis_uuid', date=1500}}"); + "ProjectAnalysis{project=Project{uuid='uuid', key='key', name='name'}, ceTask=CeTask{id='id', status=SUCCESS}, branch=Branch{main=true, name='name', type=SHORT}, qualityGate=EvaluatedQualityGate{qualityGate=QualityGate{id=id, name='name', conditions=[]}, status=WARN, evaluatedConditions=[]}, updatedAt=1, properties={a=b}, analysis=Analysis{uuid='analysis_uuid', date=1500}}"); } } diff --git a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java index a494b9a86e7..be8df1f933e 100644 --- a/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java +++ b/server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java @@ -27,7 +27,6 @@ import org.junit.Test; import org.sonar.api.measures.Metric; import org.sonar.api.platform.Server; import org.sonar.api.utils.System2; -import org.sonar.server.project.Project; import org.sonar.server.qualitygate.Condition; import org.sonar.server.qualitygate.EvaluatedCondition; import org.sonar.server.qualitygate.EvaluatedQualityGate; diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java index b96e28f37dc..9ad3ba01458 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java @@ -37,6 +37,7 @@ import org.sonar.server.project.ProjectLifeCycleListeners; import org.sonar.server.project.RekeyedProject; import org.sonar.server.user.UserSession; +import static java.util.Collections.emptyList; import static java.util.Collections.singleton; import static java.util.Collections.singletonList; import static org.sonar.core.component.ComponentKeys.isValidModuleKey; @@ -64,7 +65,7 @@ public class ComponentService { dbClient.componentKeyUpdaterDao().updateKey(dbSession, projectOrModule.uuid(), newKey); projectIndexers.commitAndIndex(dbSession, singletonList(projectOrModule), ProjectIndexer.Cause.PROJECT_KEY_UPDATE); if (isMainProject(projectOrModule)) { - Project newProject = new Project(projectOrModule.uuid(), newKey, projectOrModule.name(), projectOrModule.description()); + Project newProject = new Project(projectOrModule.uuid(), newKey, projectOrModule.name(), projectOrModule.description(), projectOrModule.getTags()); projectLifeCycleListeners.onProjectsRekeyed(singleton(new RekeyedProject(newProject, projectOrModule.getDbKey()))); } } @@ -96,7 +97,7 @@ public class ComponentService { private static RekeyedProject toRekeyedProject(ComponentKeyUpdaterDao.RekeyedResource rekeyedResource) { ResourceDto resource = rekeyedResource.getResource(); - Project project = new Project(resource.getUuid(), resource.getKey(), resource.getName(), resource.getDescription()); + Project project = new Project(resource.getUuid(), resource.getKey(), resource.getName(), resource.getDescription(), emptyList()); return new RekeyedProject(project, rekeyedResource.getOldKey()); } diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java index 57f5f96696a..7b1685f8385 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java @@ -128,7 +128,7 @@ public class DeleteAction implements OrganizationsWsAction { } private void deleteProjects(DbSession dbSession, OrganizationDto organization) { - List roots = dbClient.componentDao().selectAllRootsByOrganization(dbSession, organization.getUuid()); + List roots = dbClient.componentDao().selectProjectsByOrganization(dbSession, organization.getUuid()); try { componentCleanerService.delete(dbSession, roots); } finally { diff --git a/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookQGChangeEventListener.java b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookQGChangeEventListener.java index ee040fa0afc..f7d3310af8b 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookQGChangeEventListener.java +++ b/server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookQGChangeEventListener.java @@ -32,7 +32,6 @@ import org.sonar.db.component.AnalysisPropertyDto; import org.sonar.db.component.BranchDto; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.SnapshotDto; -import org.sonar.server.project.Project; import org.sonar.server.qualitygate.EvaluatedQualityGate; import org.sonar.server.qualitygate.changeevent.QGChangeEvent; import org.sonar.server.qualitygate.changeevent.QGChangeEventListener; diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java index e638592f57a..08443427b21 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java @@ -40,6 +40,7 @@ import org.sonar.server.project.ProjectLifeCycleListeners; import org.sonar.server.project.RekeyedProject; import org.sonar.server.tester.UserSessionRule; +import static java.util.Collections.emptyList; import static org.assertj.guava.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -196,7 +197,7 @@ public class ComponentServiceUpdateKeyTest { assertComponentKeyUpdated(inactiveModule.getDbKey(), "your_project:root:inactive_module"); assertComponentKeyUpdated(inactiveFile.getDbKey(), "your_project:root:module:src/InactiveFile.xoo"); verify(projectLifeCycleListeners).onProjectsRekeyed(ImmutableSet.of( - new RekeyedProject(new Project(project.uuid(), "your_project", project.name(), project.uuid()), "my_project") + new RekeyedProject(new Project(project.uuid(), "your_project", project.name(), project.uuid(), emptyList()), "my_project") )); } @@ -213,7 +214,7 @@ public class ComponentServiceUpdateKeyTest { assertComponentKeyUpdated(module.getDbKey(), "your_project:root:module"); assertComponentKeyUpdated(file.getDbKey(), "your_project:root:module:src/File.xoo"); verify(projectLifeCycleListeners).onProjectsRekeyed(ImmutableSet.of( - new RekeyedProject(new Project(project.uuid(), "your_project", project.name(), project.uuid()), "my_project") + new RekeyedProject(new Project(project.uuid(), "your_project", project.name(), project.uuid(), emptyList()), "my_project") )); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java index 1f4aa2f20c5..928c255de3a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java @@ -44,9 +44,9 @@ import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.UnauthorizedException; import org.sonar.server.issue.IssueFieldsSetter; import org.sonar.server.issue.IssueFinder; -import org.sonar.server.issue.WebIssueStorage; import org.sonar.server.issue.IssueUpdater; import org.sonar.server.issue.TestIssueChangePostProcessor; +import org.sonar.server.issue.WebIssueStorage; import org.sonar.server.issue.index.IssueIndexer; import org.sonar.server.issue.index.IssueIteratorFactory; import org.sonar.server.notification.NotificationManager; @@ -178,12 +178,12 @@ public class SetTagsActionTest { } @Test - public void fail_when_bad_tag_format() { + public void fail_when_tag_use_bad_format() { IssueDto issueDto = db.issues().insertIssue(newIssue().setTags(singletonList("old-tag"))); logIn(issueDto); expectedException.expect(IllegalArgumentException.class); - expectedException.expectMessage("Tag 'pol op' is invalid. Rule tags accept only the characters: a-z, 0-9, '+', '-', '#', '.'"); + expectedException.expectMessage("Tags 'pol op' are invalid. Rule tags accept only the characters: a-z, 0-9, '+', '-', '#', '.'"); call(issueDto.getKey(), "pol op"); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java index e78a9e140bb..3d709dc4c99 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java @@ -354,12 +354,6 @@ public class DeleteActionTest { ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project)); ComponentDto directory = db.components().insertComponent(ComponentTesting.newDirectory(module, "a/b" + i)); ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(module, directory)); - ComponentDto view = db.components().insertView(organization); - ComponentDto subview1 = db.components().insertComponent(ComponentTesting.newSubView(view, "v1" + i, "ksv1" + i)); - ComponentDto subview2 = db.components().insertComponent(ComponentTesting.newSubView(subview1, "v2" + i, "ksv2" + i)); - ComponentDto application = db.components().insertApplication(organization); - ComponentDto projectCopy = db.components().insertComponent(ComponentTesting.newProjectCopy("pc1" + i, project, subview1)); - ComponentDto projectCopyForApplication = db.components().insertComponent(ComponentTesting.newProjectCopy("pc2" + i, project, application)); ComponentDto branch = db.components().insertProjectBranch(project); return project; }).collect(toSet()); diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java index c1bf5acf7fa..797714fd4b4 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java @@ -40,6 +40,8 @@ import static org.mockito.ArgumentMatchers.same; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verifyZeroInteractions; +import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; +import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto; @RunWith(DataProviderRunner.class) public class ProjectLifeCycleListenersImplTest { @@ -203,7 +205,6 @@ public class ProjectLifeCycleListenersImplTest { inOrder.verifyNoMoreInteractions(); } - @DataProvider public static Object[][] oneOrManyProjects() { return new Object[][] { @@ -296,16 +297,15 @@ public class ProjectLifeCycleListenersImplTest { }; } - private static int counter = 3_989; - private static Project newUniqueProject() { - int base = counter++; - return new Project(base + "_uuid", base + "_key", base + "_name"); + return Project.from(newPrivateProjectDto(newOrganizationDto())); } + private static int counter = 3_989; + private static RekeyedProject newUniqueRekeyedProject() { int base = counter++; - Project project = new Project(base + "_uuid", base + "_key", base + "_name"); + Project project = Project.from(newPrivateProjectDto(newOrganizationDto())); return new RekeyedProject(project, base + "_old_key"); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/RekeyedProjectTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/RekeyedProjectTest.java index 3e9ddd7afa3..dbbd90e9f1b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/project/RekeyedProjectTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/project/RekeyedProjectTest.java @@ -23,8 +23,11 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import static java.util.Collections.emptyList; import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.assertj.core.api.Assertions.assertThat; +import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto; +import static org.sonar.db.organization.OrganizationTesting.newOrganizationDto; public class RekeyedProjectTest { @Rule @@ -86,7 +89,7 @@ public class RekeyedProjectTest { @Test public void verify_toString() { - Project project = new Project("A", "B", "C", "D"); + Project project = new Project("A", "B", "C", "D", emptyList()); String previousKey = "E"; RekeyedProject underTest = new RekeyedProject(project, previousKey); @@ -94,6 +97,6 @@ public class RekeyedProjectTest { } private static Project newRandomProject() { - return new Project(randomAlphanumeric(3), randomAlphanumeric(4), randomAlphanumeric(5)); + return Project.from(newPrivateProjectDto(newOrganizationDto())); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookQGChangeEventListenerTest.java b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookQGChangeEventListenerTest.java index ca1adbb94d5..2c49567e31a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookQGChangeEventListenerTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookQGChangeEventListenerTest.java @@ -49,7 +49,6 @@ import org.sonar.db.component.BranchType; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.SnapshotDto; import org.sonar.db.organization.OrganizationDto; -import org.sonar.server.project.Project; import org.sonar.server.qualitygate.EvaluatedQualityGate; import org.sonar.server.qualitygate.changeevent.QGChangeEvent; import org.sonar.server.qualitygate.changeevent.QGChangeEventListener; @@ -216,7 +215,7 @@ public class WebhookQGChangeEventListenerTest { @DataProvider public static Object[][] newQGorNot() { EvaluatedQualityGate newQualityGate = mock(EvaluatedQualityGate.class); - return new Object[][]{ + return new Object[][] { {null}, {newQualityGate} }; diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagFormat.java b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagFormat.java index a7cb441fb50..3ca072a2adb 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagFormat.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagFormat.java @@ -19,8 +19,16 @@ */ package org.sonar.api.server.rule; +import java.util.Collection; +import java.util.Objects; +import java.util.Set; import org.apache.commons.lang.StringUtils; +import static java.lang.String.format; +import static java.lang.String.join; +import static java.util.Locale.ENGLISH; +import static java.util.stream.Collectors.toSet; + /** * The characters allowed in rule tags are the same as those on StackOverflow, basically lower-case * letters, digits, plus (+), sharp (#), dash (-) and dot (.) @@ -29,6 +37,8 @@ import org.apache.commons.lang.StringUtils; */ public class RuleTagFormat { + private static final String ERROR_MESSAGE_SUFFIX = "Rule tags accept only the characters: a-z, 0-9, '+', '-', '#', '.'"; + private static final String VALID_CHARACTERS_REGEX = "^[a-z0-9\\+#\\-\\.]+$"; private RuleTagFormat() { @@ -41,8 +51,24 @@ public class RuleTagFormat { public static String validate(String tag) { if (!isValid(tag)) { - throw new IllegalArgumentException(String.format("Tag '%s' is invalid. Rule tags accept only the characters: a-z, 0-9, '+', '-', '#', '.'", tag)); + throw new IllegalArgumentException(format("Tag '%s' is invalid. %s", tag, ERROR_MESSAGE_SUFFIX)); } return tag; } + + public static Set validate(Collection tags) { + Set sanitizedTags = tags.stream() + .filter(Objects::nonNull) + .filter(tag -> !tag.isEmpty()) + .map(tag -> tag.toLowerCase(ENGLISH)) + .collect(toSet()); + Set invalidTags = sanitizedTags.stream() + .filter(tag -> !isValid(tag)) + .collect(toSet()); + if (invalidTags.isEmpty()) { + return sanitizedTags; + } + throw new IllegalArgumentException(format("Tags '%s' are invalid. %s", join(", ", invalidTags), ERROR_MESSAGE_SUFFIX)); + } + } diff --git a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleTagFormatTest.java b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleTagFormatTest.java index 597e52fcbdb..381f31b570a 100644 --- a/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleTagFormatTest.java +++ b/sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleTagFormatTest.java @@ -19,12 +19,16 @@ */ package org.sonar.api.server.rule; +import java.util.Collections; import org.junit.Test; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; public class RuleTagFormatTest { + @Test public void isValid() { assertThat(RuleTagFormat.isValid(null)).isFalse(); @@ -57,4 +61,23 @@ public class RuleTagFormatTest { assertThat(e).hasMessage("Tag ' ' is invalid. Rule tags accept only the characters: a-z, 0-9, '+', '-', '#', '.'"); } } + + @Test + public void validate_and_sanitize_collection_of_tags() { + assertThat(RuleTagFormat.validate(asList("style", "coding-style", ""))).containsExactly("coding-style", "style"); + assertThat(RuleTagFormat.validate(asList("style", "coding-style", null))).containsExactly("coding-style", "style"); + assertThat(RuleTagFormat.validate(asList("style", "style", null))).containsExactly("style"); + assertThat(RuleTagFormat.validate(singletonList("Uppercase"))).containsExactly("uppercase"); + assertThat(RuleTagFormat.validate(Collections.emptyList())).isEmpty(); + } + + @Test + public void fail_to_validate_collection_of_tags() { + try { + RuleTagFormat.validate(asList("coding style", "Stylé", "valid")); + fail(); + } catch (IllegalArgumentException e) { + assertThat(e).hasMessage("Tags 'coding style, stylé' are invalid. Rule tags accept only the characters: a-z, 0-9, '+', '-', '#', '.'"); + } + } } diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/views/SetTagsModeRequest.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/views/SetTagsModeRequest.java new file mode 100644 index 00000000000..7b429544393 --- /dev/null +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/views/SetTagsModeRequest.java @@ -0,0 +1,60 @@ +/* + * 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.views; + +import java.util.List; +import javax.annotation.Generated; + +/** + * This is part of the internal API. + * This is a POST request. + * @see Further information about this action online (including a response example) + * @since 7.4 + */ +@Generated("sonar-ws-generator") +public class SetTagsModeRequest { + + private String portfolio; + private List tags; + + /** + * This is a mandatory parameter. + */ + public SetTagsModeRequest setPortfolio(String portfolio) { + this.portfolio = portfolio; + return this; + } + + public String getPortfolio() { + return portfolio; + } + + /** + * This is a mandatory parameter. + */ + public SetTagsModeRequest setTags(List tags) { + this.tags = tags; + return this; + } + + public List getTags() { + return tags; + } +} diff --git a/sonar-ws/src/main/java/org/sonarqube/ws/client/views/ViewsService.java b/sonar-ws/src/main/java/org/sonarqube/ws/client/views/ViewsService.java index 802a6688300..79839f0eb8e 100644 --- a/sonar-ws/src/main/java/org/sonarqube/ws/client/views/ViewsService.java +++ b/sonar-ws/src/main/java/org/sonarqube/ws/client/views/ViewsService.java @@ -19,6 +19,7 @@ */ package org.sonarqube.ws.client.views; +import java.util.stream.Collectors; import javax.annotation.Generated; import org.sonarqube.ws.MediaTypes; import org.sonarqube.ws.client.BaseService; @@ -360,6 +361,22 @@ public class ViewsService extends BaseService { ).content(); } + /** + * + * This is part of the internal API. + * This is a POST request. + * @see Further information about this action online (including a response example) + * @since 7.4 + */ + public void setTagsMode(SetTagsModeRequest request) { + call( + new PostRequest(path("set_tags_mode")) + .setParam("portfolio", request.getPortfolio()) + .setParam("tags", request.getTags() == null ? null : request.getTags().stream().collect(Collectors.joining(","))) + .setMediaType(MediaTypes.JSON) + ).content(); + } + /** * * This is part of the internal API. -- 2.39.5