]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11185 Allow portfolio to define projects by tags
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Thu, 30 Aug 2018 09:07:00 +0000 (11:07 +0200)
committerSonarTech <sonartech@sonarsource.com>
Fri, 7 Sep 2018 18:20:56 +0000 (20:20 +0200)
* 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

37 files changed:
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/LoadReportAnalysisMetadataHolderStep.java
server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/webhook/WebhookPostTask.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/analysis/AnalysisMetadataHolderImplTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/BranchPersisterImplTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ConfigurationRepositoryTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/measure/PostMeasuresComputationChecksStepTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/PersistLiveMeasuresStepTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/QualityGateEventsStepTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/ReportPersistComponentsStepTest.java
server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/webhook/WebhookPostTaskTest.java
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentDao.java
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentDaoTest.java
server/sonar-server-common/src/main/java/org/sonar/server/issue/IssueFieldsSetter.java
server/sonar-server-common/src/main/java/org/sonar/server/project/Project.java
server/sonar-server-common/src/main/java/org/sonar/server/webhook/Project.java [new file with mode: 0644]
server/sonar-server-common/src/main/java/org/sonar/server/webhook/ProjectAnalysis.java
server/sonar-server-common/src/main/java/org/sonar/server/webhook/WebhookPayloadFactoryImpl.java
server/sonar-server-common/src/test/java/org/sonar/server/project/ProjectTest.java
server/sonar-server-common/src/test/java/org/sonar/server/webhook/ProjectAnalysisTest.java
server/sonar-server-common/src/test/java/org/sonar/server/webhook/WebhookPayloadFactoryImplTest.java
server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java
server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java
server/sonar-server/src/main/java/org/sonar/server/webhook/WebhookQGChangeEventListener.java
server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java
server/sonar-server/src/test/java/org/sonar/server/issue/ws/SetTagsActionTest.java
server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java
server/sonar-server/src/test/java/org/sonar/server/project/ProjectLifeCycleListenersImplTest.java
server/sonar-server/src/test/java/org/sonar/server/project/RekeyedProjectTest.java
server/sonar-server/src/test/java/org/sonar/server/webhook/WebhookQGChangeEventListenerTest.java
sonar-plugin-api/src/main/java/org/sonar/api/server/rule/RuleTagFormat.java
sonar-plugin-api/src/test/java/org/sonar/api/server/rule/RuleTagFormatTest.java
sonar-ws/src/main/java/org/sonarqube/ws/client/views/SetTagsModeRequest.java [new file with mode: 0644]
sonar-ws/src/main/java/org/sonarqube/ws/client/views/ViewsService.java

index 5b09d232bdd32831d1e6624f88e2436f8bbf11e3..de73734d68a660e9608bd42bdb30773a8f37eee0 100644 (file)
@@ -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(
index a4262fc1cf9b5d7495d374cbe51b11a0ef640953..df697738b020325d60b7b8ad364fdf397fe0b1d9 100644 (file)
@@ -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<String, String> 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);
   }
 }
index d5084ce526c9f91e1b2b133f8e5a9531e6cb9759..d777dc9c40d8a5ba87a8f648c05bf016048439c4 100644 (file)
@@ -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
index 6cf1c26a2f24f30d3990535026a7fc97937808dc..822a25f67eb665a2d1b33dc07e94dd278d101070 100644 (file)
@@ -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
index 5cc30eff7ed79cd2c1466895c4e80a7e5087665c..a598a179dbb72994df2ab34ea4569df9aeebb1ba 100644 (file)
@@ -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() {
index edf912d9b64a8ebe3062c14d98aec9585e6b43c8..a9f81e8d6154f97e94017533749cee1344f22371 100644 (file)
@@ -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");
index c7bd59039dfddb0502003b994feb503b70bbf22a..5cf3b26fe1f71a2ede07af8d569df4d76383b395 100644 (file)
@@ -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<Context> 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
index 2ac9cdbab6dc42fb33beddfbba19af8e0d559dc3..6268cdd3af5d7414fc05fff543914fabe1f8287e 100644 (file)
@@ -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)));
   }
 
 }
index a9e6aaf92057e352bfd3b6d6d6876dd3478111cc..53a143118bd3dc630b39c3b5a039c63cc1fff4c2 100644 (file)
@@ -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) {
index 32caea964ba4c958e6081de81231ea255613fae0..25157a69c85728d01c6add6dfa5664e5ca1e87f2 100644 (file)
@@ -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);
   }
index 454d6f910e4c7faffb41b7194b2fb0b9eacfe353..2c284e10b9ee894b7f9cab982e492764fd2df44d 100644 (file)
@@ -895,14 +895,14 @@ public class ReportPersistComponentsStepTest extends BaseStepTest {
 
   private ComponentDto prepareProject(Consumer<ComponentDto>... 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<ComponentDto>... 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;
   }
index 829f1b4f4184913b9c110d41e9d9824d0d2dd6d4..762f4c3d25f08648b1a77f6866920e89baf67b8b 100644 (file)
@@ -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())),
index ef7891ea62f7682fe7ea14bf8ea2cd5e741b2027..1e531a1e63a76af60a74ac965322e79607fdc3ca 100644 (file)
@@ -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<ComponentDto> selectAllRootsByOrganization(DbSession dbSession, String organizationUuid) {
-    return mapper(dbSession).selectAllRootsByOrganization(organizationUuid);
+  public List<ComponentDto> selectProjectsByOrganization(DbSession dbSession, String organizationUuid) {
+    return mapper(dbSession).selectProjectsByOrganization(organizationUuid);
   }
 
   public List<ComponentDto> selectGhostProjects(DbSession session, String organizationUuid, @Nullable String query, int offset, int limit) {
index c8247cd6393b9c952c8101754155ec0986a60c7f..7d041d714ec86b44c54565c39ff24d06b2233600 100644 (file)
@@ -87,7 +87,7 @@ public interface ComponentMapper {
    */
   List<ComponentDto> selectProjects();
 
-  List<ComponentDto> selectAllRootsByOrganization(@Param("organizationUuid") String organizationUuid);
+  List<ComponentDto> selectProjectsByOrganization(@Param("organizationUuid") String organizationUuid);
 
   /**
    * Return all descendant modules (including itself) from a given component uuid and scope
index 4dcc61d5277f51299222f620215b5c85c721f9ff..28e539a7de91ddeb64cfde1801c23c68330bab47 100644 (file)
       AND p.main_branch_project_uuid IS NULL
   </select>
 
-  <select id="selectAllRootsByOrganization" resultType="Component">
+  <select id="selectProjectsByOrganization" resultType="Component">
     select
       <include refid="componentColumns"/>
     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
   </select>
index b58ef55e99f890c21c3cb5644b6e720052e3afe6..650c82a000097a941ca84de9e3a48dae2b861ddb 100644 (file)
@@ -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));
 
index 077bd3b6a1cb1debf7086e49d8e1e4ed6e689392..7f5abde500d955005de333948aeb7bb906818ceb 100644 (file)
@@ -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<String> tags, IssueChangeContext context) {
-    Set<String> newTags = tags.stream()
-      .filter(Objects::nonNull)
-      .filter(tag -> !tag.isEmpty())
-      .map(tag -> RuleTagFormat.validate(tag.toLowerCase(Locale.ENGLISH)))
-      .collect(MoreCollectors.toSet());
+    Set<String> newTags = RuleTagFormat.validate(tags);
 
     Set<String> oldTags = new HashSet<>(issue.tags());
     if (!oldTags.equals(newTags)) {
index af220370e3417ec4748668518d31b0c6d5ea03ee..4576e716aaef332d06ea0cd58d5937c3b3d042c9 100644 (file)
@@ -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<String> 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<String> 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<String> 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 (file)
index 0000000..9ec7c2c
--- /dev/null
@@ -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();
+  }
+
+}
index 3a9175adbfb8b9cfdaf4daafa242975ea24b513e..2e055ad77f6cc200a7edd0077a73dc2e1a418b16 100644 (file)
@@ -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;
index d6b9b298ec634c0f8bf78501cb632066737a33b1..51f93e22328c1979fae9131a31af2565ea28d345 100644 (file)
@@ -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;
index 4f2a351e8444aff1217df595fef529c60555aa88..0f620f33b2bbfd36aa727be8faa937c9001b94c7 100644 (file)
@@ -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());
   }
 
index 0a453709b6779f0d0140312bddf49d72a2acd953..21c7cb873906b22f2f7d32b30cfd226edb76fde8 100644 (file)
@@ -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}}");
   }
 }
index a494b9a86e759b3eb66383b3b9adc6c29fc3bfc4..be8df1f933e495306da660addac83e6a551b9f6b 100644 (file)
@@ -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;
index b96e28f37dc19e4c648d12e75e348c0d4b823094..9ad3ba014583a25fcf92755978fda53b23d7961a 100644 (file)
@@ -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());
   }
 
index 57f5f96696a369a2e0f28fa7f65811c1b693a226..7b1685f8385ced143b5be21b3fda1e16cda7d0f9 100644 (file)
@@ -128,7 +128,7 @@ public class DeleteAction implements OrganizationsWsAction {
   }
 
   private void deleteProjects(DbSession dbSession, OrganizationDto organization) {
-    List<ComponentDto> roots = dbClient.componentDao().selectAllRootsByOrganization(dbSession, organization.getUuid());
+    List<ComponentDto> roots = dbClient.componentDao().selectProjectsByOrganization(dbSession, organization.getUuid());
     try {
       componentCleanerService.delete(dbSession, roots);
     } finally {
index ee040fa0afca4b34c587ffe2a6ef5f1fc64ebaf0..f7d3310af8bad71288b66fafaa34f098900340bd 100644 (file)
@@ -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;
index e638592f57a0c07c1ad7bb81cdab1603dcd676c1..08443427b215664437ad5b522fce26f06489e0e0 100644 (file)
@@ -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")
     ));
   }
 
index 1f4aa2f20c52a88b9d08931b527425b263508317..928c255de3a6a7f43df5d65c86b6e29837cd94d7 100644 (file)
@@ -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");
   }
index e78a9e140bb5e38e0f0298620a9dadc34978ade8..3d709dc4c991ee758a66742ddf392096f313ceb6 100644 (file)
@@ -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());
index c1bf5acf7fa3903194f825201929ca1686cc505a..797714fd4b4aaec688f9aaec645c1c0e45dd69c0 100644 (file)
@@ -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");
   }
 }
index 3e9ddd7afa3b42dbd1271e1486d58827ac38b4d9..dbbd90e9f1b64abba9888021b54c7374203baec8 100644 (file)
@@ -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()));
   }
 }
index ca1adbb94d55f0733f0498911a86069389a63855..2c49567e31a10abbf6d3f1f571f5230a6aa0365d 100644 (file)
@@ -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}
     };
index a7cb441fb503efcba5664220c9761586a2c988ce..3ca072a2adbba7c9a664453ed6b115085309c29d 100644 (file)
  */
 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<String> validate(Collection<String> tags) {
+    Set<String> sanitizedTags = tags.stream()
+      .filter(Objects::nonNull)
+      .filter(tag -> !tag.isEmpty())
+      .map(tag -> tag.toLowerCase(ENGLISH))
+      .collect(toSet());
+    Set<String> 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));
+  }
+
 }
index 597e52fcbdbf0755a804b83cc8ac9daed00f725a..381f31b570aa7701bf517ab80ad17ab2338bb7aa 100644 (file)
  */
 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 (file)
index 0000000..7b42954
--- /dev/null
@@ -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 <a href="https://next.sonarqube.com/sonarqube/web_api/api/views/set_tags_mode">Further information about this action online (including a response example)</a>
+ * @since 7.4
+ */
+@Generated("sonar-ws-generator")
+public class SetTagsModeRequest {
+
+  private String portfolio;
+  private List<String> 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<String> tags) {
+    this.tags = tags;
+    return this;
+  }
+
+  public List<String> getTags() {
+    return tags;
+  }
+}
index 802a66883003c050c4f1cc96a1bbac23081769af..79839f0eb8e51ec0113c6eb8ed607643b366f145 100644 (file)
@@ -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 <a href="https://next.sonarqube.com/sonarqube/web_api/api/views/set_tags_mode">Further information about this action online (including a response example)</a>
+   * @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.