]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10411 Do not persist name of provided project links
authorJulien Lancelot <julien.lancelot@sonarsource.com>
Mon, 12 Feb 2018 09:44:44 +0000 (10:44 +0100)
committerStas Vilchik <stas.vilchik@sonarsource.com>
Tue, 20 Feb 2018 09:06:52 +0000 (10:06 +0100)
69 files changed:
server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl
server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java
server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java
server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentLinkDao.java [deleted file]
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentLinkDto.java [deleted file]
server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentLinkMapper.java [deleted file]
server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkDao.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkDto.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkMapper.java [new file with mode: 0644]
server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java
server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java
server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentLinkMapper.xml [deleted file]
server/sonar-db-dao/src/main/resources/org/sonar/db/component/ProjectLinkMapper.xml [new file with mode: 0644]
server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java
server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkDaoTest.java [deleted file]
server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkDtoTest.java [deleted file]
server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkTesting.java [deleted file]
server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDaoTest.java [new file with mode: 0644]
server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDbTester.java [new file with mode: 0644]
server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDtoTest.java [new file with mode: 0644]
server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkTesting.java [new file with mode: 0644]
server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/delete.xml [deleted file]
server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/empty.xml [deleted file]
server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/insert-result.xml [deleted file]
server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/shared.xml [deleted file]
server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/update-result.xml [deleted file]
server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/update.xml [deleted file]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/RenameTableBuilder.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71.java
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinks.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2.java [new file with mode: 0644]
server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinks.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/sql/RenameTableBuilderTest.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2Test.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71Test.java
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinksTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2Test.java [new file with mode: 0644]
server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinksTest.java [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2Test/empty.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinksTest/project_links.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2Test/project_links2.sql [new file with mode: 0644]
server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinksTest/project_links2.sql [new file with mode: 0644]
server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectLinksPage.java
server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStep.java
server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java
server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsData.java
server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/CreateAction.java
server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/DeleteAction.java
server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksWs.java
server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/SearchAction.java
server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/list-example.json [deleted file]
server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/search-example.json [new file with mode: 0644]
server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest.java
server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java
server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java
server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/DeleteActionTest.java
server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java [deleted file]
server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/SearchActionTest.java
server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/add_links_on_project_and_module-result.xml [deleted file]
server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/delete_link.xml [deleted file]
server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/empty.xml [deleted file]
server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/not_delete_custom_link.xml [deleted file]
server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/nothing_to_do_when_link_already_exists.xml [deleted file]
server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/update_link-result.xml [deleted file]
server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/update_link.xml [deleted file]
tests/src/test/java/org/sonarqube/tests/project/ProjectLinksTest.java

index 5c287cea0ec0b925a63207ce5dbd69d87217ad04..c63f060e839121f87fadfa2caf0b8b6d39e761b1 100644 (file)
@@ -283,13 +283,17 @@ CREATE INDEX "PROPERTIES_KEY" ON "PROPERTIES" ("PROP_KEY");
 
 
 CREATE TABLE "PROJECT_LINKS" (
-  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
-  "COMPONENT_UUID" VARCHAR(50),
-  "LINK_TYPE" VARCHAR(20),
+  "UUID" VARCHAR(40) NOT NULL,
+  "PROJECT_UUID" VARCHAR(50) NOT NULL,
+  "LINK_TYPE" VARCHAR(20) NOT NULL,
   "NAME" VARCHAR(128),
-  "HREF" VARCHAR(2048) NOT NULL
+  "HREF" VARCHAR(2048) NOT NULL,
+  "CREATED_AT" BIGINT,
+  "UPDATED_AT" BIGINT
 );
 
+CREATE INDEX "PROJECT_LINKS_PROJECT" ON "PROJECT_LINKS" ("PROJECT_UUID");
+
 
 CREATE TABLE "DUPLICATIONS_INDEX" (
   "ID" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
index 31a49c77346417f7570f53b56ef3d430d8180d91..ef1d147fe339b0a6949004105ebac224bf114853 100644 (file)
@@ -32,7 +32,7 @@ import org.sonar.db.component.AnalysisPropertiesDao;
 import org.sonar.db.component.BranchDao;
 import org.sonar.db.component.ComponentDao;
 import org.sonar.db.component.ComponentKeyUpdaterDao;
-import org.sonar.db.component.ComponentLinkDao;
+import org.sonar.db.component.ProjectLinkDao;
 import org.sonar.db.component.SnapshotDao;
 import org.sonar.db.duplication.DuplicationDao;
 import org.sonar.db.es.EsQueueDao;
@@ -92,7 +92,7 @@ public class DaoModule extends Module {
     CeTaskInputDao.class,
     ComponentDao.class,
     ComponentKeyUpdaterDao.class,
-    ComponentLinkDao.class,
+    ProjectLinkDao.class,
     LiveMeasureDao.class,
     CustomMeasureDao.class,
     DefaultQProfileDao.class,
index 3b671e1b6822e139b0570cfc856df78011ad33d8..ee6e3b82eb9b66665c208d0a48b8eafadbc7ecde 100644 (file)
@@ -30,7 +30,7 @@ import org.sonar.db.component.AnalysisPropertiesDao;
 import org.sonar.db.component.BranchDao;
 import org.sonar.db.component.ComponentDao;
 import org.sonar.db.component.ComponentKeyUpdaterDao;
-import org.sonar.db.component.ComponentLinkDao;
+import org.sonar.db.component.ProjectLinkDao;
 import org.sonar.db.component.SnapshotDao;
 import org.sonar.db.duplication.DuplicationDao;
 import org.sonar.db.es.EsQueueDao;
@@ -107,7 +107,7 @@ public class DbClient {
   private final CeTaskCharacteristicDao ceTaskCharacteristicsDao;
   private final CeScannerContextDao ceScannerContextDao;
   private final FileSourceDao fileSourceDao;
-  private final ComponentLinkDao componentLinkDao;
+  private final ProjectLinkDao projectLinkDao;
   private final EventDao eventDao;
   private final PurgeDao purgeDao;
   private final QualityGateDao qualityGateDao;
@@ -169,7 +169,7 @@ public class DbClient {
     ceTaskCharacteristicsDao = getDao(map, CeTaskCharacteristicDao.class);
     ceScannerContextDao = getDao(map, CeScannerContextDao.class);
     fileSourceDao = getDao(map, FileSourceDao.class);
-    componentLinkDao = getDao(map, ComponentLinkDao.class);
+    projectLinkDao = getDao(map, ProjectLinkDao.class);
     eventDao = getDao(map, EventDao.class);
     purgeDao = getDao(map, PurgeDao.class);
     qualityGateDao = getDao(map, QualityGateDao.class);
@@ -316,8 +316,8 @@ public class DbClient {
     return fileSourceDao;
   }
 
-  public ComponentLinkDao componentLinkDao() {
-    return componentLinkDao;
+  public ProjectLinkDao projectLinkDao() {
+    return projectLinkDao;
   }
 
   public EventDao eventDao() {
index c14eba4110dd565152e691e3362152acc2f4e9ac..5559074c5c747636d2e3f63056eb61234df68304 100644 (file)
@@ -40,11 +40,10 @@ import org.sonar.db.component.BranchMapper;
 import org.sonar.db.component.ComponentDto;
 import org.sonar.db.component.ComponentDtoWithSnapshotId;
 import org.sonar.db.component.ComponentKeyUpdaterMapper;
-import org.sonar.db.component.ComponentLinkDto;
-import org.sonar.db.component.ComponentLinkMapper;
 import org.sonar.db.component.ComponentMapper;
 import org.sonar.db.component.FilePathWithHashDto;
 import org.sonar.db.component.KeyWithUuidDto;
+import org.sonar.db.component.ProjectLinkMapper;
 import org.sonar.db.component.ResourceDto;
 import org.sonar.db.component.ScrapAnalysisPropertyDto;
 import org.sonar.db.component.SnapshotDto;
@@ -149,7 +148,6 @@ public class MyBatis implements Startable {
     confBuilder.loadAlias("ActiveRuleParam", ActiveRuleParamDto.class);
     confBuilder.loadAlias("CeTaskCharacteristic", CeTaskCharacteristicDto.class);
     confBuilder.loadAlias("Component", ComponentDto.class);
-    confBuilder.loadAlias("ComponentLink", ComponentLinkDto.class);
     confBuilder.loadAlias("ComponentWithSnapshot", ComponentDtoWithSnapshotId.class);
     confBuilder.loadAlias("CustomMeasure", CustomMeasureDto.class);
     confBuilder.loadAlias("DuplicationUnit", DuplicationUnitDto.class);
@@ -206,7 +204,7 @@ public class MyBatis implements Startable {
       CeTaskInputMapper.class,
       CeTaskCharacteristicMapper.class,
       ComponentKeyUpdaterMapper.class,
-      ComponentLinkMapper.class,
+      ProjectLinkMapper.class,
       ComponentMapper.class,
       LiveMeasureMapper.class,
       CustomMeasureMapper.class,
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentLinkDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentLinkDao.java
deleted file mode 100644 (file)
index 9a1b058..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.db.component;
-
-import java.util.List;
-import javax.annotation.CheckForNull;
-import org.sonar.db.Dao;
-import org.sonar.db.DbSession;
-
-import static java.util.Collections.emptyList;
-
-public class ComponentLinkDao implements Dao {
-
-  public List<ComponentLinkDto> selectByComponentUuid(DbSession session, String componentUuid) {
-    return session.getMapper(ComponentLinkMapper.class).selectByComponentUuid(componentUuid);
-  }
-
-  public List<ComponentLinkDto> selectByComponentUuids(DbSession dbSession, List<String> componentUuids) {
-    return componentUuids.isEmpty() ? emptyList() : mapper(dbSession).selectByComponentUuids(componentUuids);
-  }
-
-  @CheckForNull
-  public ComponentLinkDto selectById(DbSession session, long id) {
-    return session.getMapper(ComponentLinkMapper.class).selectById(id);
-  }
-
-  public ComponentLinkDto insert(DbSession session, ComponentLinkDto dto) {
-    session.getMapper(ComponentLinkMapper.class).insert(dto);
-    return dto;
-  }
-
-  public void update(DbSession session, ComponentLinkDto dto) {
-    session.getMapper(ComponentLinkMapper.class).update(dto);
-  }
-
-  public void delete(DbSession session, long id) {
-    session.getMapper(ComponentLinkMapper.class).delete(id);
-  }
-
-  private static ComponentLinkMapper mapper(DbSession dbSession) {
-    return dbSession.getMapper(ComponentLinkMapper.class);
-  }
-
-}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentLinkDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentLinkDto.java
deleted file mode 100644 (file)
index 8cd7d0a..0000000
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.db.component;
-
-import com.google.common.collect.ImmutableList;
-import java.util.List;
-
-/**
- * Component links should be merge in a 'links' column (using protobuf for instance) of the projects table.
- * But to do this we'll have to wait for the measure filters page (where links are displayed) to be rewritten in JS/WS (because it's in Rails for the moment).
- */
-public class ComponentLinkDto {
-
-  public static final String TYPE_HOME_PAGE = "homepage";
-  public static final String TYPE_CI = "ci";
-  public static final String TYPE_ISSUE_TRACKER = "issue";
-  public static final String TYPE_SOURCES = "scm";
-  public static final String TYPE_SOURCES_DEV = "scm_dev";
-
-  public static final List<String> PROVIDED_TYPES = ImmutableList.of(TYPE_HOME_PAGE, TYPE_CI, TYPE_ISSUE_TRACKER, TYPE_SOURCES, TYPE_SOURCES_DEV);
-
-  private Long id;
-  private String componentUuid;
-  private String type;
-  private String name;
-  private String href;
-
-  public String getName() {
-    return name;
-  }
-
-  public ComponentLinkDto setName(String name) {
-    this.name = name;
-    return this;
-  }
-
-  public String getComponentUuid() {
-    return componentUuid;
-  }
-
-  public ComponentLinkDto setComponentUuid(String componentUuid) {
-    this.componentUuid = componentUuid;
-    return this;
-  }
-
-  public String getHref() {
-    return href;
-  }
-
-  public ComponentLinkDto setHref(String href) {
-    this.href = href;
-    return this;
-  }
-
-  public Long getId() {
-    return id;
-  }
-
-  public String getIdAsString() {
-    return String.valueOf(id);
-  }
-
-  public ComponentLinkDto setId(Long id) {
-    this.id = id;
-    return this;
-  }
-
-  public String getType() {
-    return type;
-  }
-
-  public ComponentLinkDto setType(String type) {
-    this.type = type;
-    return this;
-  }
-}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentLinkMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentLinkMapper.java
deleted file mode 100644 (file)
index fe955f7..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.db.component;
-
-import java.util.List;
-import org.apache.ibatis.annotations.Param;
-
-public interface ComponentLinkMapper {
-
-  List<ComponentLinkDto> selectByComponentUuid(String componentUuid);
-
-  List<ComponentLinkDto> selectByComponentUuids(@Param("componentUuids") List<String> componentUuids);
-
-  ComponentLinkDto selectById(@Param("id") long id);
-
-  void insert(ComponentLinkDto dto);
-
-  void update(ComponentLinkDto dto);
-
-  void delete(long id);
-}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkDao.java
new file mode 100644 (file)
index 0000000..d375b1e
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.db.component;
+
+import java.util.List;
+import javax.annotation.CheckForNull;
+import org.sonar.api.utils.System2;
+import org.sonar.db.Dao;
+import org.sonar.db.DbSession;
+
+import static org.sonar.db.DatabaseUtils.executeLargeInputs;
+
+public class ProjectLinkDao implements Dao {
+
+  private final System2 system2;
+
+  public ProjectLinkDao(System2 system2) {
+    this.system2 = system2;
+  }
+
+  public List<ProjectLinkDto> selectByProjectUuid(DbSession session, String projectUuid) {
+    return session.getMapper(ProjectLinkMapper.class).selectByProjectUuid(projectUuid);
+  }
+
+  public List<ProjectLinkDto> selectByProjectUuids(DbSession dbSession, List<String> projectUuids) {
+    return executeLargeInputs(projectUuids, mapper(dbSession)::selectByProjectUuids);
+  }
+
+  @CheckForNull
+  public ProjectLinkDto selectByUuid(DbSession session, String uuid) {
+    return session.getMapper(ProjectLinkMapper.class).selectByUuid(uuid);
+  }
+
+  public ProjectLinkDto insert(DbSession session, ProjectLinkDto dto) {
+    long now = system2.now();
+    session.getMapper(ProjectLinkMapper.class).insert(dto.setCreatedAt(now).setUpdatedAt(now));
+    return dto;
+  }
+
+  public void update(DbSession session, ProjectLinkDto dto) {
+    session.getMapper(ProjectLinkMapper.class).update(dto.setUpdatedAt(system2.now()));
+  }
+
+  public void delete(DbSession session, String uuid) {
+    session.getMapper(ProjectLinkMapper.class).delete(uuid);
+  }
+
+  private static ProjectLinkMapper mapper(DbSession dbSession) {
+    return dbSession.getMapper(ProjectLinkMapper.class);
+  }
+
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkDto.java
new file mode 100644 (file)
index 0000000..9930ee9
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.db.component;
+
+import com.google.common.collect.ImmutableList;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nullable;
+
+public class ProjectLinkDto {
+
+  public static final String TYPE_HOME_PAGE = "homepage";
+  public static final String TYPE_CI = "ci";
+  public static final String TYPE_ISSUE_TRACKER = "issue";
+  public static final String TYPE_SOURCES = "scm";
+
+  public static final String TYPE_SOURCES_DEV = "scm_dev";
+
+  public static final List<String> PROVIDED_TYPES = ImmutableList.of(TYPE_HOME_PAGE, TYPE_CI, TYPE_ISSUE_TRACKER, TYPE_SOURCES, TYPE_SOURCES_DEV);
+
+  private String uuid;
+  private String projectUuid;
+  private String type;
+  private String name;
+  private String href;
+  private long createdAt;
+  private long updatedAt;
+
+  public String getUuid() {
+    return uuid;
+  }
+
+  public ProjectLinkDto setUuid(String uuid) {
+    this.uuid = uuid;
+    return this;
+  }
+
+  @CheckForNull
+  public String getName() {
+    return name;
+  }
+
+  public ProjectLinkDto setName(@Nullable String name) {
+    this.name = name;
+    return this;
+  }
+
+  public String getProjectUuid() {
+    return projectUuid;
+  }
+
+  public ProjectLinkDto setProjectUuid(String projectUuid) {
+    this.projectUuid = projectUuid;
+    return this;
+  }
+
+  public String getHref() {
+    return href;
+  }
+
+  public ProjectLinkDto setHref(String href) {
+    this.href = href;
+    return this;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  public ProjectLinkDto setType(String type) {
+    this.type = type;
+    return this;
+  }
+
+  public long getCreatedAt() {
+    return createdAt;
+  }
+
+  public ProjectLinkDto setCreatedAt(long createdAt) {
+    this.createdAt = createdAt;
+    return this;
+  }
+
+  public long getUpdatedAt() {
+    return updatedAt;
+  }
+
+  public ProjectLinkDto setUpdatedAt(long updatedAt) {
+    this.updatedAt = updatedAt;
+    return this;
+  }
+}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkMapper.java
new file mode 100644 (file)
index 0000000..9fc084f
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.db.component;
+
+import java.util.List;
+import org.apache.ibatis.annotations.Param;
+
+public interface ProjectLinkMapper {
+
+  List<ProjectLinkDto> selectByProjectUuid(String projectUuid);
+
+  List<ProjectLinkDto> selectByProjectUuids(@Param("projectUuids") List<String> projectUuids);
+
+  ProjectLinkDto selectByUuid(@Param("uuid") String uuid);
+
+  void insert(ProjectLinkDto dto);
+
+  void update(ProjectLinkDto dto);
+
+  void delete(String uuid);
+}
index edbe6931d792dfc45a51d3fef253236851866f48..4e346364ee94e6aaf5f2e5c6660e4df5bc6dbbfd 100644 (file)
@@ -169,7 +169,7 @@ class PurgeCommands {
 
   void deleteLinks(String rootUuid) {
     profiler.start("deleteLinks (project_links)");
-    purgeMapper.deleteProjectLinksByComponentUuid(rootUuid);
+    purgeMapper.deleteProjectLinksByProjectUuid(rootUuid);
     session.commit();
     profiler.stop();
   }
index 2db61704c6dfcd89a85c39b4ef98137eb2bae743..a9dec7bb5b213f4df6cb57a586df0b3cfa675017 100644 (file)
@@ -54,7 +54,7 @@ public interface PurgeMapper {
 
   void resolveComponentIssuesNotAlreadyResolved(@Param("componentUuids") List<String> componentUuids, @Param("dateAsLong") Long dateAsLong);
 
-  void deleteProjectLinksByComponentUuid(@Param("rootUuid") String rootUuid);
+  void deleteProjectLinksByProjectUuid(@Param("rootUuid") String rootUuid);
 
   void deletePropertiesByComponentIds(@Param("componentIds") List<Long> componentIds);
 
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentLinkMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentLinkMapper.xml
deleted file mode 100644 (file)
index cbc4412..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" ?>
-<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
-<mapper namespace="org.sonar.db.component.ComponentLinkMapper">
-
-  <sql id="componentLinkColumns">
-    p.id,
-    p.component_uuid as "componentUuid",
-    p.link_type as "type",
-    p.name as name,
-    p.href as href
-  </sql>
-
-  <select id="selectByComponentUuid" parameterType="String" resultType="ComponentLink">
-    SELECT
-    <include refid="componentLinkColumns"/>
-    FROM project_links p
-    where p.component_uuid=#{uuid,jdbcType=VARCHAR}
-    ORDER BY p.id
-  </select>
-
-  <select id="selectByComponentUuids" parameterType="String" resultType="ComponentLink">
-    SELECT
-    <include refid="componentLinkColumns"/>
-    FROM project_links p
-    where
-      p.component_uuid in
-      <foreach collection="componentUuids" open="(" close=")" item="uuid" separator=",">
-        #{uuid,jdbcType=VARCHAR}
-      </foreach>
-    order by p.id
-  </select>
-
-  <select id="selectById" parameterType="long" resultType="ComponentLink">
-    SELECT
-    <include refid="componentLinkColumns"/>
-    FROM project_links p
-    where p.id=#{id,jdbcType=BIGINT}
-  </select>
-
-  <insert id="insert" parameterType="ComponentLink" keyColumn="id" useGeneratedKeys="true" keyProperty="id">
-    INSERT INTO project_links (component_uuid, link_type, name, href)
-    VALUES (#{componentUuid,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
-    #{href,jdbcType=VARCHAR})
-  </insert>
-
-  <insert id="update" parameterType="ComponentLink" useGeneratedKeys="false">
-    UPDATE project_links SET component_uuid=#{componentUuid,jdbcType=VARCHAR}, link_type=#{type,jdbcType=VARCHAR},
-    name=#{name,jdbcType=VARCHAR}, href=#{href,jdbcType=VARCHAR}
-    WHERE id=#{id,jdbcType=BIGINT}
-  </insert>
-
-  <delete id="delete">
-    DELETE FROM project_links WHERE id=#{id,jdbcType=BIGINT}
-  </delete>
-
-</mapper>
-
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ProjectLinkMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ProjectLinkMapper.xml
new file mode 100644 (file)
index 0000000..248b7ae
--- /dev/null
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd">
+<mapper namespace="org.sonar.db.component.ProjectLinkMapper">
+
+    <sql id="columns">
+        p.uuid,
+        p.project_uuid as "projectUuid",
+        p.link_type as "type",
+        p.name as name,
+        p.href as href,
+        p.created_at as "createdAt",
+        p.updated_at as "updatedAt"
+    </sql>
+
+    <select id="selectByProjectUuid" parameterType="String" resultType="org.sonar.db.component.ProjectLinkDto">
+        SELECT
+        <include refid="columns"/>
+        FROM project_links p
+        where p.project_uuid=#{uuid,jdbcType=VARCHAR}
+        ORDER BY p.uuid
+    </select>
+
+    <select id="selectByProjectUuids" parameterType="String" resultType="org.sonar.db.component.ProjectLinkDto">
+        SELECT
+        <include refid="columns"/>
+        FROM project_links p
+        where
+        p.project_uuid in
+        <foreach collection="projectUuids" open="(" close=")" item="uuid" separator=",">
+            #{uuid,jdbcType=VARCHAR}
+        </foreach>
+        order by p.uuid
+    </select>
+
+    <select id="selectByUuid" parameterType="String" resultType="org.sonar.db.component.ProjectLinkDto">
+        SELECT
+        <include refid="columns"/>
+        FROM project_links p
+        where p.uuid=#{uuid,jdbcType=VARCHAR}
+    </select>
+
+    <insert id="insert" parameterType="org.sonar.db.component.ProjectLinkDto" useGeneratedKeys="false">
+        INSERT INTO project_links (uuid, project_uuid, link_type, name, href, created_at, updated_at)
+        VALUES (
+        #{uuid,jdbcType=VARCHAR},
+        #{projectUuid,jdbcType=VARCHAR},
+        #{type,jdbcType=VARCHAR},
+        #{name,jdbcType=VARCHAR},
+        #{href,jdbcType=VARCHAR},
+        #{createdAt,jdbcType=BIGINT},
+        #{updatedAt,jdbcType=BIGINT})
+    </insert>
+
+    <insert id="update" parameterType="org.sonar.db.component.ProjectLinkDto" useGeneratedKeys="false">
+        UPDATE project_links SET
+        project_uuid=#{projectUuid,jdbcType=VARCHAR},
+        link_type=#{type,jdbcType=VARCHAR},
+        name=#{name,jdbcType=VARCHAR},
+        href=#{href,jdbcType=VARCHAR},
+        updated_at=#{updatedAt,jdbcType=BIGINT}
+        WHERE uuid=#{uuid,jdbcType=VARCHAR}
+    </insert>
+
+    <delete id="delete">
+        DELETE FROM project_links WHERE uuid=#{uuid,jdbcType=VARCHAR}
+    </delete>
+
+</mapper>
+
index 4f652c7cdc8a316a6b21be49c960ade31ff66e9d..b8af25a75987f681a6ae6096615c0f9df5e22552 100644 (file)
       and resolution is null
   </update>
 
-  <delete id="deleteProjectLinksByComponentUuid" parameterType="map">
+  <delete id="deleteProjectLinksByProjectUuid" parameterType="map">
     delete from project_links
     where
-      component_uuid = #{rootUuid,jdbcType=VARCHAR}
+      project_uuid = #{rootUuid,jdbcType=VARCHAR}
   </delete>
 
   <delete id="deletePropertiesByComponentIds" parameterType="map">
index 01e64c19a3c9ef4e54b52d5447d5f153e8f08d62..f4d7d81ffd73e1b6874944d5a93ee928c5dc8506 100644 (file)
@@ -30,6 +30,7 @@ import org.picocontainer.containers.TransientPicoContainer;
 import org.sonar.api.utils.System2;
 import org.sonar.core.util.SequenceUuidFactory;
 import org.sonar.db.component.ComponentDbTester;
+import org.sonar.db.component.ProjectLinkDbTester;
 import org.sonar.db.event.EventDbTester;
 import org.sonar.db.favorite.FavoriteDbTester;
 import org.sonar.db.issue.IssueDbTester;
@@ -42,7 +43,6 @@ import org.sonar.db.permission.template.PermissionTemplateDbTester;
 import org.sonar.db.plugin.PluginDbTester;
 import org.sonar.db.property.PropertyDbTester;
 import org.sonar.db.qualitygate.QualityGateDbTester;
-import org.sonar.db.qualitygate.QualityGateDto;
 import org.sonar.db.qualityprofile.QualityProfileDbTester;
 import org.sonar.db.rule.RuleDbTester;
 import org.sonar.db.source.FileSourceTester;
@@ -65,10 +65,10 @@ public class DbTester extends AbstractDbTester<TestDb> {
   private boolean started = false;
   private String defaultOrganizationUuid = randomAlphanumeric(40);
   private OrganizationDto defaultOrganization;
-  private QualityGateDto builtInQualityGate;
 
   private final UserDbTester userTester;
   private final ComponentDbTester componentTester;
+  private final ProjectLinkDbTester componentLinkTester;
   private final FavoriteDbTester favoriteTester;
   private final EventDbTester eventTester;
   private final OrganizationDbTester organizationTester;
@@ -91,6 +91,7 @@ public class DbTester extends AbstractDbTester<TestDb> {
     initDbClient();
     this.userTester = new UserDbTester(this);
     this.componentTester = new ComponentDbTester(this);
+    this.componentLinkTester = new ProjectLinkDbTester(this);
     this.favoriteTester = new FavoriteDbTester(this);
     this.eventTester = new EventDbTester(this);
     this.organizationTester = new OrganizationDbTester(this);
@@ -187,6 +188,10 @@ public class DbTester extends AbstractDbTester<TestDb> {
     return componentTester;
   }
 
+  public ProjectLinkDbTester componentLinks() {
+    return componentLinkTester;
+  }
+
   public FavoriteDbTester favorites() {
     return favoriteTester;
   }
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkDaoTest.java
deleted file mode 100644 (file)
index 1cb6378..0000000
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.db.component;
-
-import java.util.List;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.utils.System2;
-import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
-import org.sonar.db.DbTester;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static java.util.Collections.emptyList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.db.component.ComponentLinkDto.TYPE_HOME_PAGE;
-import static org.sonar.db.component.ComponentLinkDto.TYPE_SOURCES;
-import static org.sonar.db.component.ComponentLinkTesting.newComponentLinkDto;
-
-
-public class ComponentLinkDaoTest {
-
-  @Rule
-  public DbTester db = DbTester.create(System2.INSTANCE);
-  private DbClient dbClient = db.getDbClient();
-  private DbSession dbSession = db.getSession();
-
-  private ComponentLinkDao underTest = db.getDbClient().componentLinkDao();
-
-  @Test
-  public void select_by_component_uuid() {
-    db.prepareDbUnit(getClass(), "shared.xml");
-
-    List<ComponentLinkDto> links = underTest.selectByComponentUuid(db.getSession(), "ABCD");
-    assertThat(links).hasSize(2);
-
-    links = underTest.selectByComponentUuid(db.getSession(), "BCDE");
-    assertThat(links).hasSize(1);
-
-    ComponentLinkDto link = links.get(0);
-    assertThat(link.getId()).isEqualTo(3L);
-    assertThat(link.getComponentUuid()).isEqualTo("BCDE");
-    assertThat(link.getType()).isEqualTo("homepage");
-    assertThat(link.getName()).isEqualTo("Home");
-    assertThat(link.getHref()).isEqualTo("http://www.struts.org");
-  }
-
-  @Test
-  public void select_by_component_uuids() {
-    String firstUuid = "COMPONENT_UUID_1";
-    String secondUuid = "COMPONENT_UUID_2";
-    dbClient.componentLinkDao().insert(dbSession, newComponentLinkDto().setComponentUuid(firstUuid).setType(TYPE_HOME_PAGE));
-    dbClient.componentLinkDao().insert(dbSession, newComponentLinkDto().setComponentUuid(firstUuid).setType(TYPE_SOURCES));
-    dbClient.componentLinkDao().insert(dbSession, newComponentLinkDto().setComponentUuid(secondUuid).setType(TYPE_HOME_PAGE));
-    dbClient.componentLinkDao().insert(dbSession, newComponentLinkDto().setComponentUuid("ANOTHER_COMPONENT_UUID").setType(TYPE_HOME_PAGE));
-    db.commit();
-
-    List<ComponentLinkDto> result = underTest.selectByComponentUuids(dbSession, newArrayList(firstUuid, secondUuid));
-
-    assertThat(result).hasSize(3).extracting(ComponentLinkDto::getComponentUuid).containsOnly(firstUuid, secondUuid);
-  }
-
-  @Test
-  public void select_by_component_uuids_with_empty_list() {
-    List<ComponentLinkDto> result = underTest.selectByComponentUuids(dbSession, emptyList());
-
-    assertThat(result).isEmpty();
-  }
-
-  @Test
-  public void select_by_id() {
-    ComponentLinkDto link = underTest.insert(dbSession, newComponentLinkDto());
-    db.commit();
-
-    ComponentLinkDto candidate = underTest.selectById(dbSession, link.getId());
-    assertThat(candidate.getId()).isNotNull();
-  }
-
-  @Test
-  public void insert() {
-    db.prepareDbUnit(getClass(), "empty.xml");
-
-    underTest.insert(db.getSession(), new ComponentLinkDto()
-      .setComponentUuid("ABCD")
-      .setType("homepage")
-      .setName("Home")
-      .setHref("http://www.sonarqube.org")
-      );
-    db.getSession().commit();
-
-    db.assertDbUnit(getClass(), "insert-result.xml", new String[] {"id"}, "project_links");
-  }
-
-  @Test
-  public void update() {
-    db.prepareDbUnit(getClass(), "update.xml");
-
-    underTest.update(db.getSession(), new ComponentLinkDto()
-      .setId(1L)
-      .setComponentUuid("ABCD")
-      .setType("homepage")
-      .setName("Home")
-      .setHref("http://www.sonarqube.org")
-      );
-    db.getSession().commit();
-
-    db.assertDbUnit(getClass(), "update-result.xml", "project_links");
-  }
-
-  @Test
-  public void delete() {
-    db.prepareDbUnit(getClass(), "delete.xml");
-
-    underTest.delete(db.getSession(), 1L);
-    db.getSession().commit();
-
-    assertThat(db.countRowsOfTable("project_links")).isEqualTo(0);
-  }
-
-}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkDtoTest.java
deleted file mode 100644 (file)
index 6e3c8ef..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.db.component;
-
-import org.junit.Test;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ComponentLinkDtoTest {
-
-  @Test
-  public void test_getters_and_setters() {
-    ComponentLinkDto dto = new ComponentLinkDto()
-      .setId(1L)
-      .setComponentUuid("ABCD")
-      .setType("homepage")
-      .setName("Home")
-      .setHref("http://www.sonarqube.org");
-
-    assertThat(dto.getId()).isEqualTo(1L);
-    assertThat(dto.getComponentUuid()).isEqualTo("ABCD");
-    assertThat(dto.getType()).isEqualTo("homepage");
-    assertThat(dto.getName()).isEqualTo("Home");
-    assertThat(dto.getHref()).isEqualTo("http://www.sonarqube.org");
-  }
-
-  @Test
-  public void test_provided_types() {
-    assertThat(ComponentLinkDto.PROVIDED_TYPES).hasSize(5);
-  }
-}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkTesting.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkTesting.java
deleted file mode 100644 (file)
index 6eea21b..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-package org.sonar.db.component;
-
-import org.sonar.core.util.Uuids;
-
-import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
-import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
-
-public class ComponentLinkTesting {
-  public static ComponentLinkDto newComponentLinkDto() {
-    return new ComponentLinkDto()
-      .setComponentUuid(Uuids.createFast())
-      .setHref(randomAlphanumeric(128))
-      .setName(randomAlphabetic(128))
-      .setType(ComponentLinkDto.TYPE_SOURCES);
-  }
-
-}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDaoTest.java
new file mode 100644 (file)
index 0000000..eaba69b
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.db.component;
+
+import java.util.Collections;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.db.DbTester;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProjectLinkDaoTest {
+
+  private final static long PAST = 5_000_000_000L;
+  private final static long NOW = 10_000_000_000L;
+
+  private System2 system2 = new TestSystem2().setNow(NOW);
+
+  @Rule
+  public DbTester db = DbTester.create(system2);
+
+  private ProjectLinkDao underTest = db.getDbClient().projectLinkDao();
+
+  @Test
+  public void select_by_id() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link = db.componentLinks().insertProvidedLink(project, c -> c
+      .setUuid("ABCD")
+      .setName("Home")
+      .setType("homepage")
+      .setHref("http://www.struts.org"));
+
+    ProjectLinkDto reloaded = underTest.selectByUuid(db.getSession(), link.getUuid());
+
+    assertThat(reloaded.getUuid()).isEqualTo("ABCD");
+    assertThat(reloaded.getProjectUuid()).isEqualTo(project.uuid());
+    assertThat(reloaded.getType()).isEqualTo("homepage");
+    assertThat(reloaded.getName()).isEqualTo("Home");
+    assertThat(reloaded.getHref()).isEqualTo("http://www.struts.org");
+    assertThat(reloaded.getCreatedAt()).isEqualTo(NOW);
+    assertThat(reloaded.getUpdatedAt()).isEqualTo(NOW);
+  }
+
+  @Test
+  public void select_by_project_uuid() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link1 = db.componentLinks().insertProvidedLink(project);
+    ProjectLinkDto link2 = db.componentLinks().insertProvidedLink(project);
+    ProjectLinkDto link3 = db.componentLinks().insertProvidedLink(project);
+    ComponentDto otherProject = db.components().insertPrivateProject();
+    ProjectLinkDto otherLink = db.componentLinks().insertProvidedLink(otherProject);
+
+    assertThat(underTest.selectByProjectUuid(db.getSession(), project.uuid()))
+      .extracting(ProjectLinkDto::getUuid)
+      .containsExactlyInAnyOrder(link1.getUuid(), link2.getUuid(), link3.getUuid());
+    assertThat(underTest.selectByProjectUuid(db.getSession(), otherProject.uuid()))
+      .extracting(ProjectLinkDto::getUuid)
+      .containsExactlyInAnyOrder(otherLink.getUuid());
+    assertThat(underTest.selectByProjectUuid(db.getSession(), "UNKNOWN")).isEmpty();
+  }
+
+  @Test
+  public void select_by_project_uuids() {
+    ComponentDto project1 = db.components().insertPrivateProject();
+    ProjectLinkDto link1 = db.componentLinks().insertProvidedLink(project1);
+    ProjectLinkDto link2 = db.componentLinks().insertProvidedLink(project1);
+    ComponentDto project2 = db.components().insertPrivateProject();
+    ProjectLinkDto link3 = db.componentLinks().insertProvidedLink(project2);
+
+    assertThat(underTest.selectByProjectUuids(db.getSession(), asList(project1.uuid(), project2.uuid())))
+      .extracting(ProjectLinkDto::getUuid)
+      .containsOnly(link1.getUuid(), link2.getUuid(), link3.getUuid());
+    assertThat(underTest.selectByProjectUuids(db.getSession(), singletonList(project1.uuid())))
+      .extracting(ProjectLinkDto::getUuid)
+      .containsOnly(link1.getUuid(), link2.getUuid());
+    assertThat(underTest.selectByProjectUuids(db.getSession(), Collections.emptyList())).isEmpty();
+  }
+
+  @Test
+  public void insert() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link = ProjectLinkTesting.newProvidedLinkDto()
+      .setUuid("ABCD")
+      .setProjectUuid(project.uuid())
+      .setName("Home")
+      .setType("homepage")
+      .setHref("http://www.struts.org")
+      // These fields will be set by the DAO
+      .setCreatedAt(0L)
+      .setUpdatedAt(0L);
+
+    underTest.insert(db.getSession(), link);
+    db.getSession().commit();
+
+    ProjectLinkDto reloaded = underTest.selectByUuid(db.getSession(), link.getUuid());
+    assertThat(reloaded.getUuid()).isEqualTo("ABCD");
+    assertThat(reloaded.getProjectUuid()).isEqualTo(project.uuid());
+    assertThat(reloaded.getType()).isEqualTo("homepage");
+    assertThat(reloaded.getName()).isEqualTo("Home");
+    assertThat(reloaded.getHref()).isEqualTo("http://www.struts.org");
+    assertThat(reloaded.getCreatedAt()).isEqualTo(NOW);
+    assertThat(reloaded.getUpdatedAt()).isEqualTo(NOW);
+  }
+
+  @Test
+  public void update() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link = db.componentLinks().insertProvidedLink(project, c -> c
+      .setUuid("ABCD")
+      .setType("ci")
+      .setName("Gihub")
+      .setHref("http://www.github.org"));
+    // Force dates to be in the past
+    db.executeUpdateSql("UPDATE project_links SET created_at=" + PAST + " ,updated_at=" + PAST);
+
+    ComponentDto project2 = db.components().insertPrivateProject();
+    underTest.update(db.getSession(), link
+      .setProjectUuid(project2.uuid())
+      .setType("homepage")
+      .setName("Home")
+      .setHref("http://www.sonarqube.org"));
+    db.getSession().commit();
+
+    ProjectLinkDto reloaded = underTest.selectByUuid(db.getSession(), link.getUuid());
+    assertThat(reloaded.getUuid()).isEqualTo("ABCD");
+    assertThat(reloaded.getProjectUuid()).isEqualTo(project2.uuid());
+    assertThat(reloaded.getType()).isEqualTo("homepage");
+    assertThat(reloaded.getName()).isEqualTo("Home");
+    assertThat(reloaded.getHref()).isEqualTo("http://www.sonarqube.org");
+    assertThat(reloaded.getCreatedAt()).isEqualTo(PAST);
+    assertThat(reloaded.getUpdatedAt()).isEqualTo(NOW);
+  }
+
+  @Test
+  public void delete() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link = db.componentLinks().insertProvidedLink(project);
+
+    underTest.delete(db.getSession(), link.getUuid());
+    db.getSession().commit();
+
+    assertThat(db.countRowsOfTable("project_links")).isEqualTo(0);
+  }
+
+}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDbTester.java
new file mode 100644 (file)
index 0000000..dcc0d2d
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.db.component;
+
+import java.util.Arrays;
+import java.util.function.Consumer;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.DbTester;
+
+import static org.sonar.db.component.ProjectLinkTesting.newCustomLinkDto;
+import static org.sonar.db.component.ProjectLinkTesting.newProvidedLinkDto;
+
+public class ProjectLinkDbTester {
+  private final DbTester db;
+  private final DbClient dbClient;
+  private final DbSession dbSession;
+
+  public ProjectLinkDbTester(DbTester db) {
+    this.db = db;
+    this.dbClient = db.getDbClient();
+    this.dbSession = db.getSession();
+  }
+
+  @SafeVarargs
+  public final ProjectLinkDto insertProvidedLink(ComponentDto project, Consumer<ProjectLinkDto>... dtoPopulators) {
+    return insertLink(project, newProvidedLinkDto(), dtoPopulators);
+  }
+
+  @SafeVarargs
+  public final ProjectLinkDto insertCustomLink(ComponentDto project, Consumer<ProjectLinkDto>... dtoPopulators) {
+    return insertLink(project, newCustomLinkDto(), dtoPopulators);
+  }
+
+  @SafeVarargs
+  private final ProjectLinkDto insertLink(ComponentDto project, ProjectLinkDto componentLink, Consumer<ProjectLinkDto>... dtoPopulators) {
+    Arrays.stream(dtoPopulators).forEach(dtoPopulator -> dtoPopulator.accept(componentLink));
+    dbClient.projectLinkDao().insert(dbSession, componentLink.setProjectUuid(project.uuid()));
+    db.commit();
+    return componentLink;
+  }
+}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDtoTest.java
new file mode 100644 (file)
index 0000000..3301f42
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.db.component;
+
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProjectLinkDtoTest {
+
+  @Test
+  public void test_getters_and_setters() {
+    ProjectLinkDto dto = new ProjectLinkDto()
+      .setUuid("ABCD")
+      .setProjectUuid("EFGH")
+      .setType("homepage")
+      .setName("Home")
+      .setHref("http://www.sonarqube.org")
+      .setCreatedAt(1_000_000_000L)
+      .setUpdatedAt(5_000_000_000L);
+
+    assertThat(dto.getUuid()).isEqualTo("ABCD");
+    assertThat(dto.getProjectUuid()).isEqualTo("EFGH");
+    assertThat(dto.getType()).isEqualTo("homepage");
+    assertThat(dto.getName()).isEqualTo("Home");
+    assertThat(dto.getHref()).isEqualTo("http://www.sonarqube.org");
+    assertThat(dto.getCreatedAt()).isEqualTo(1_000_000_000L);
+    assertThat(dto.getUpdatedAt()).isEqualTo(5_000_000_000L);
+  }
+
+  @Test
+  public void test_provided_types() {
+    assertThat(ProjectLinkDto.PROVIDED_TYPES).hasSize(5);
+  }
+}
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkTesting.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkTesting.java
new file mode 100644 (file)
index 0000000..f79d90a
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.db.component;
+
+import org.apache.commons.lang.math.RandomUtils;
+import org.sonar.core.util.Uuids;
+
+import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
+import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
+
+public class ProjectLinkTesting {
+
+  public static ProjectLinkDto newProvidedLinkDto() {
+    return newCommonLinkDto()
+      .setName(null)
+      .setType(ProjectLinkDto.PROVIDED_TYPES.get(RandomUtils.nextInt(ProjectLinkDto.PROVIDED_TYPES.size() - 1)));
+  }
+
+  public static ProjectLinkDto newCustomLinkDto() {
+    String nameAndType = randomAlphabetic(20);
+    return newCommonLinkDto()
+      .setName(nameAndType)
+      .setType(nameAndType);
+  }
+
+  private static ProjectLinkDto newCommonLinkDto() {
+    return new ProjectLinkDto()
+      .setUuid(Uuids.createFast())
+      .setProjectUuid(Uuids.createFast())
+      .setHref(randomAlphanumeric(128))
+      .setCreatedAt(System.currentTimeMillis())
+      .setUpdatedAt(System.currentTimeMillis());
+  }
+
+}
diff --git a/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/delete.xml b/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/delete.xml
deleted file mode 100644 (file)
index 8b89e72..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<dataset>
-
-  <project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/>
-
-</dataset>
diff --git a/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/empty.xml b/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/empty.xml
deleted file mode 100644 (file)
index 871dedc..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-<dataset>
-
-</dataset>
diff --git a/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/insert-result.xml b/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/insert-result.xml
deleted file mode 100644 (file)
index 8b89e72..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<dataset>
-
-  <project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/>
-
-</dataset>
diff --git a/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/shared.xml b/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/shared.xml
deleted file mode 100644 (file)
index 33e9449..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-<dataset>
-
-  <project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/>
-  <project_links id="2" component_uuid="ABCD" link_type="scm" name="Sources" href="https://github.com/SonarSource/sonar"/>
-  <project_links id="3" component_uuid="BCDE" link_type="homepage" name="Home" href="http://www.struts.org"/>
-
-</dataset>
diff --git a/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/update-result.xml b/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/update-result.xml
deleted file mode 100644 (file)
index 8b89e72..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<dataset>
-
-  <project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/>
-
-</dataset>
diff --git a/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/update.xml b/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/update.xml
deleted file mode 100644 (file)
index 6b80fc9..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<dataset>
-
-  <project_links id="1" component_uuid="BCDE" link_type="ci" name="CI" href="github"/>
-
-</dataset>
index f5adc72ba9acc59d4d41a1e38356b82731bdfdcf..ef381ab3f821c3fd48fd4449c999f1f8f4537c56 100644 (file)
@@ -19,7 +19,6 @@
  */
 package org.sonar.server.platform.db.migration.sql;
 
-import java.util.Arrays;
 import java.util.List;
 import org.sonar.db.dialect.Dialect;
 import org.sonar.db.dialect.H2;
@@ -29,19 +28,16 @@ import org.sonar.db.dialect.Oracle;
 import org.sonar.db.dialect.PostgreSql;
 
 import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
 import static org.sonar.server.platform.db.migration.def.Validations.validateTableName;
 
-/**
- * Limitation: only tables with auto-generated ID column can
- * be renamed as the Oracle implementation assumes that
- * the sequence and trigger related to ID column exist.
- */
 public class RenameTableBuilder {
 
   private final Dialect dialect;
   private String name;
   private String newName;
+  private boolean autoGeneratedId = true;
 
   public RenameTableBuilder(Dialect dialect) {
     this.dialect = dialect;
@@ -57,6 +53,18 @@ public class RenameTableBuilder {
     return this;
   }
 
+  /**
+   * When a table has no auto generated id, this parameter has to be set to false.
+   * On Oracle, it will allow to not try to drop and recreate the trigger.
+   * On other databases, this method is useless.
+   *
+   * Default value is true.
+   */
+  public RenameTableBuilder setAutoGeneratedId(boolean autoGeneratedId) {
+    this.autoGeneratedId = autoGeneratedId;
+    return this;
+  }
+
   public List<String> build() {
     validateTableName(name);
     validateTableName(newName);
@@ -73,11 +81,13 @@ public class RenameTableBuilder {
       case MsSql.ID:
         return singletonList("EXEC sp_rename '" + name + "', '" + newName + "'");
       case Oracle.ID:
-        return Arrays.asList(
+        String renameSqlCommand = "RENAME " + name + " TO " + newName;
+        return autoGeneratedId ? asList(
           "DROP TRIGGER " + name + "_idt",
-          "RENAME " + name + " TO " + newName,
+            renameSqlCommand,
           "RENAME " + name + "_seq TO " + newName + "_seq",
-          CreateTableBuilder.createOracleTriggerForTable(newName));
+            CreateTableBuilder.createOracleTriggerForTable(newName))
+          : singletonList(renameSqlCommand);
       default:
         throw new IllegalArgumentException("Unsupported dialect id " + dialect.getId());
     }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2.java
new file mode 100644 (file)
index 0000000..d1be6af
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.CreateTableBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.BigIntegerColumnDef.newBigIntegerColumnDefBuilder;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class CreateTableProjectLinks2 extends DdlChange {
+
+  private static final String TABLE_NAME = "project_links2";
+
+  public CreateTableProjectLinks2(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new CreateTableBuilder(getDialect(), TABLE_NAME)
+      .addPkColumn(newVarcharColumnDefBuilder()
+        .setColumnName("uuid")
+        .setLimit(UUID_SIZE)
+        .setIsNullable(false)
+        .build())
+      .addColumn(newVarcharColumnDefBuilder()
+        .setColumnName("project_uuid")
+        .setLimit(UUID_SIZE)
+        .setIsNullable(false)
+        .build())
+      .addColumn(newVarcharColumnDefBuilder()
+        .setColumnName("link_type")
+        .setLimit(20)
+        .setIsNullable(false)
+        .build())
+      .addColumn(newVarcharColumnDefBuilder()
+        .setColumnName("name")
+        .setLimit(128)
+        .setIsNullable(true)
+        .build())
+      .addColumn(newVarcharColumnDefBuilder()
+        .setColumnName("href")
+        .setLimit(2048)
+        .setIsNullable(false)
+        .build())
+      .addColumn(newBigIntegerColumnDefBuilder()
+        .setColumnName("created_at")
+        .setIsNullable(false)
+        .build())
+      .addColumn(newBigIntegerColumnDefBuilder()
+        .setColumnName("updated_at")
+        .setIsNullable(false)
+        .build())
+      .build());
+  }
+}
index a1cdf16d6e32c645e3b19825c9e712293350a846..1e18627e81c51a36115894c402829f8a94eb7fd0 100644 (file)
@@ -35,6 +35,11 @@ public class DbVersion71 implements DbVersion {
       .add(2005, "Create table DEPRECATED_RULE_KEYS", CreateDeprecatedRuleKeysTable.class)
       .add(2006, "Clean orphans in Compute Engine child tables", CleanCeChildTablesOrphans.class)
       .add(2007, "Update PERMISSION_TEMPLATES.KEYS ", UpdatePermissionTooLongTemplateKeys.class)
+      .add(2008, "Make scope not nullable in rules", MakeScopeNotNullableInRules.class)
+      .add(2009, "Create table PROJECT_LINKS2", CreateTableProjectLinks2.class)
+      .add(2010, "Populate table PROJECT_LINKS2", PopulateTableProjectLinks2.class)
+      .add(2011, "Drop table PROJECT_LINKS", DropTableProjectLinks.class)
+      .add(2012, "Rename table PROJECT_LINKS2 to PROJECT_LINKS", RenameTableProjectLinks2ToProjectLinks.class)
     ;
   }
 }
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinks.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinks.java
new file mode 100644 (file)
index 0000000..ca2950b
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.DropTableBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+public class DropTableProjectLinks extends DdlChange {
+
+  private static final String TABLE_NAME = "project_links";
+
+  public DropTableProjectLinks(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new DropTableBuilder(getDialect(), TABLE_NAME).build());
+  }
+
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2.java
new file mode 100644 (file)
index 0000000..a42f869
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v71;
+
+import com.google.common.collect.ImmutableList;
+import java.sql.SQLException;
+import java.util.List;
+import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.step.DataChange;
+import org.sonar.server.platform.db.migration.step.MassUpdate;
+
+/**
+ * Populate PROJECT_LINK2 data from PROJECT_LINK table and take the opportunity to do some cleanup :
+ * - Ignore link that are not set on project (only take component with qualifier TRK and scope PRJ)
+ * - Do not set a name on provided links (SONAR-10411)
+ * - Do not copy link on Developer Connection (SONAR-10299)
+ */
+public class PopulateTableProjectLinks2 extends DataChange {
+
+  private static final String TYPE_HOME_PAGE = "homepage";
+  private static final String TYPE_CI = "ci";
+  private static final String TYPE_ISSUE_TRACKER = "issue";
+  private static final String TYPE_SOURCES = "scm";
+  private static final String TYPE_SOURCES_DEV = "scm_dev";
+  private static final List<String> PROVIDED_TYPES = ImmutableList.of(TYPE_HOME_PAGE, TYPE_CI, TYPE_ISSUE_TRACKER, TYPE_SOURCES);
+
+  private static final String SCOPE_PROJECT = "PRJ";
+  private static final String QUALIFIER_PROJECT = "TRK";
+
+  private final UuidFactory uuidFactory;
+  private final System2 system2;
+
+  public PopulateTableProjectLinks2(Database db, UuidFactory uuidFactory, System2 system2) {
+    super(db);
+    this.uuidFactory = uuidFactory;
+    this.system2 = system2;
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    long now = system2.now();
+    MassUpdate massUpdate = context.prepareMassUpdate();
+    massUpdate.select("SELECT" +
+      " p.component_uuid, p.link_type, p.name, p.href" +
+      " from project_links p" +
+      // Join on projects in order to sanitize orphans (if any)
+      " inner join projects prj on prj.uuid=p.component_uuid and prj.scope=? and prj.qualifier=? " +
+      " left outer join project_links2 p2" +
+      " on p2.project_uuid=p.component_uuid " +
+      "    and p2.href=p.href" +
+      "    and p2.link_type=p.link_type" +
+      "    and (p2.name=p.name or (p2.name is null and p.link_type in (?, ?, ?, ?)))" +
+      " where" +
+      " p2.uuid is null" +
+      " order by p.id")
+      .setString(1, SCOPE_PROJECT)
+      .setString(2, QUALIFIER_PROJECT)
+      .setString(3, TYPE_HOME_PAGE)
+      .setString(4, TYPE_CI)
+      .setString(5, TYPE_ISSUE_TRACKER)
+      .setString(6, TYPE_SOURCES);
+    massUpdate.update("insert into project_links2" +
+      " (uuid, project_uuid, link_type, name, href, created_at, updated_at)" +
+      " values " +
+      " (?, ?, ?, ?, ?, ?, ?)");
+    massUpdate.rowPluralName("project links");
+    massUpdate.execute((row, update) -> {
+      String componentUuid = row.getString(1);
+      String linkType = row.getString(2);
+      String name = row.getString(3);
+      String href = row.getString(4);
+
+      // project link "developer connection" are removed
+      if (linkType.equals(TYPE_SOURCES_DEV)) {
+        return false;
+      }
+
+      update.setString(1, uuidFactory.create());
+      update.setString(2, componentUuid);
+      update.setString(3, linkType);
+      // provided type don't need anymore a name, the UI will display it by getting the i18 bundle of the link_type value
+      if (PROVIDED_TYPES.contains(linkType)) {
+        update.setString(4, null);
+      } else {
+        update.setString(4, name);
+      }
+      update.setString(5, href);
+      update.setLong(6, now);
+      update.setLong(7, now);
+      return true;
+    });
+  }
+}
diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinks.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinks.java
new file mode 100644 (file)
index 0000000..dd9f12c
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.sonar.db.Database;
+import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder;
+import org.sonar.server.platform.db.migration.sql.RenameTableBuilder;
+import org.sonar.server.platform.db.migration.step.DdlChange;
+
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.UUID_SIZE;
+import static org.sonar.server.platform.db.migration.def.VarcharColumnDef.newVarcharColumnDefBuilder;
+
+public class RenameTableProjectLinks2ToProjectLinks extends DdlChange {
+
+  public RenameTableProjectLinks2ToProjectLinks(Database db) {
+    super(db);
+  }
+
+  @Override
+  public void execute(Context context) throws SQLException {
+    context.execute(new RenameTableBuilder(getDialect())
+      .setName("project_links2")
+      .setNewName("project_links")
+      .setAutoGeneratedId(false)
+      .build());
+
+    context.execute(new CreateIndexBuilder(getDialect())
+      .setTable("project_links")
+      .setName("project_links_project")
+      .addColumn(newVarcharColumnDefBuilder()
+        .setColumnName("project_uuid")
+        .setLimit(UUID_SIZE)
+        .setIsNullable(false)
+        .build())
+      .build());
+  }
+
+}
index 1f32d803d9ff1654b9d1a5bb28e67d496bc5a8ab..cfe6cffad87f6157dffdd7ee97981fe0b3169484 100644 (file)
@@ -61,6 +61,11 @@ public class RenameTableBuilderTest {
       "CREATE OR REPLACE TRIGGER bar_idt BEFORE INSERT ON bar FOR EACH ROW BEGIN IF :new.id IS null THEN SELECT bar_seq.nextval INTO :new.id FROM dual; END IF; END;");
   }
 
+  @Test
+  public void rename_table_on_oracle_when_auto_generated_id_is_false() {
+    verifySqlWhenAutoGeneratedIdIsFalse(new Oracle(), "RENAME foo TO bar");
+  }
+
   @Test
   public void rename_table_on_postgresql() {
     verifySql(new PostgreSql(), "ALTER TABLE foo RENAME TO bar");
@@ -89,4 +94,13 @@ public class RenameTableBuilderTest {
       .build();
     assertThat(actual).containsExactly(expectedSql);
   }
+
+  private static void verifySqlWhenAutoGeneratedIdIsFalse(Dialect dialect, String... expectedSql) {
+    List<String> actual = new RenameTableBuilder(dialect)
+      .setName("foo")
+      .setNewName("bar")
+      .setAutoGeneratedId(false)
+      .build();
+    assertThat(actual).containsExactly(expectedSql);
+  }
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2Test.java
new file mode 100644 (file)
index 0000000..ff65f3f
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+package org.sonar.server.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import java.sql.Types;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class CreateTableProjectLinks2Test {
+
+  private static final String TABLE_NAME = "project_links2";
+
+  @Rule
+  public final CoreDbTester dbTester = CoreDbTester.createForSchema(CreateTableProjectLinks2Test.class, "empty.sql");
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private CreateTableProjectLinks2 underTest = new CreateTableProjectLinks2(dbTester.database());
+
+  @Test
+  public void creates_table_on_empty_db() throws SQLException {
+    underTest.execute();
+
+    assertThat(dbTester.countRowsOfTable(TABLE_NAME)).isEqualTo(0);
+
+    dbTester.assertColumnDefinition(TABLE_NAME, "uuid", Types.VARCHAR, 40, false);
+    dbTester.assertColumnDefinition(TABLE_NAME, "project_uuid", Types.VARCHAR, 40, false);
+    dbTester.assertColumnDefinition(TABLE_NAME, "link_type", Types.VARCHAR, 20, false);
+    dbTester.assertColumnDefinition(TABLE_NAME, "name", Types.VARCHAR, 128, true);
+    dbTester.assertColumnDefinition(TABLE_NAME, "href", Types.VARCHAR, 2048, false);
+    dbTester.assertColumnDefinition(TABLE_NAME, "created_at", Types.BIGINT, null, false);
+    dbTester.assertColumnDefinition(TABLE_NAME, "updated_at", Types.BIGINT, null, false);
+  }
+
+  @Test
+  public void migration_is_not_reentrant() throws SQLException {
+    underTest.execute();
+
+    expectedException.expect(IllegalStateException.class);
+
+    underTest.execute();
+  }
+
+}
\ No newline at end of file
index 15fee0317ac7d6d47574f05bb9a1c6dce33dce9b..a93444604fd9588112a88d5c70252ccc0928f0a9 100644 (file)
@@ -36,7 +36,7 @@ public class DbVersion71Test {
 
   @Test
   public void verify_migration_count() {
-    verifyMigrationCount(underTest, 8);
+    verifyMigrationCount(underTest, 13);
   }
 
 }
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinksTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinksTest.java
new file mode 100644 (file)
index 0000000..09e78e3
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+public class DropTableProjectLinksTest {
+
+  @Rule
+  public final CoreDbTester dbTester = CoreDbTester.createForSchema(DropTableProjectLinksTest.class, "project_links.sql");
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private DropTableProjectLinks underTest = new DropTableProjectLinks(dbTester.database());
+
+  @Test
+  public void creates_table_on_empty_db() throws SQLException {
+    underTest.execute();
+
+    dbTester.assertTableDoesNotExist("project_links");
+  }
+
+  @Test
+  public void migration_is_not_reentrant() throws SQLException {
+    underTest.execute();
+
+    expectedException.expect(IllegalStateException.class);
+
+    underTest.execute();
+  }
+
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2Test.java
new file mode 100644 (file)
index 0000000..487d57f
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import java.util.stream.Collectors;
+import org.assertj.core.groups.Tuple;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.utils.System2;
+import org.sonar.api.utils.internal.TestSystem2;
+import org.sonar.core.util.SequenceUuidFactory;
+import org.sonar.core.util.UuidFactory;
+import org.sonar.db.CoreDbTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.groups.Tuple.tuple;
+
+public class PopulateTableProjectLinks2Test {
+
+  private static final long PAST = 5_000_000_000L;
+  private static final long NOW = 10_000_000_000L;
+
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Rule
+  public CoreDbTester db = CoreDbTester.createForSchema(PopulateTableProjectLinks2Test.class, "project_links2.sql");
+
+  private System2 system2 = new TestSystem2().setNow(NOW);
+  private UuidFactory uuidFactory = new SequenceUuidFactory();
+
+  private PopulateTableProjectLinks2 underTest = new PopulateTableProjectLinks2(db.database(), uuidFactory, system2);
+
+  @Test
+  public void copy_custom_links() throws SQLException {
+    String project = insertProject();
+    insertProjectLink("Name1", "custom1", "http://link1", project);
+    insertProjectLink("Name2", "custom2", "http://link2", project);
+
+    underTest.execute();
+
+    assertProjectLinks2(
+      tuple("Name1", "custom1", "http://link1", project, NOW, NOW),
+      tuple("Name2", "custom2", "http://link2", project, NOW, NOW));
+  }
+
+  @Test
+  public void remove_name_of_provided_links() throws SQLException {
+    String project = insertProject();
+    insertProjectLink("Home", "homepage", "http://homepage", project);
+    insertProjectLink("CI", "ci", "http://ci", project);
+    insertProjectLink("Jira", "issue", "http://issue", project);
+    insertProjectLink("SCM", "scm", "http://scm", project);
+
+    underTest.execute();
+
+    assertProjectLinks2(
+      tuple(null, "homepage", "http://homepage", project, NOW, NOW),
+      tuple(null, "ci", "http://ci", project, NOW, NOW),
+      tuple(null, "issue", "http://issue", project, NOW, NOW),
+      tuple(null, "scm", "http://scm", project, NOW, NOW));
+  }
+
+  @Test
+  public void copy_links_from_different_projects() throws SQLException {
+    String project1 = insertProject();
+    insertProjectLink("Name", "custom", "http://link1", project1);
+    String project2 = insertProject();
+    insertProjectLink("Name", "custom", "http://link2", project2);
+
+    underTest.execute();
+
+    assertProjectLinks2(
+      tuple("Name", "custom", "http://link1", project1, NOW, NOW),
+      tuple("Name", "custom", "http://link2", project2, NOW, NOW));
+  }
+
+  @Test
+  public void do_not_copy_links_from_developer_connection_link() throws SQLException {
+    insertProjectLink("Dev", "scm_dev", "http://link1", insertProject());
+
+    underTest.execute();
+
+    assertNoProjectLinks2();
+  }
+
+  @Test
+  public void do_not_copy_links_from_components_that_are_not_projects() throws SQLException {
+    insertProjectLink("Name", "custom", "http://link1", insertComponent("PRJ", "BRC"));
+    insertProjectLink("Name", "custom", "http://link2", insertComponent("PRJ", "VW"));
+    insertProjectLink("Name", "custom", "http://link1", insertComponent("DIR", "DIR"));
+    insertProjectLink("Name", "custom", "http://link1", "UNKNOWN");
+
+    underTest.execute();
+
+    assertNoProjectLinks2();
+  }
+
+  @Test
+  public void do_not_copy_already_copied_data() throws SQLException {
+    String project = insertProject();
+    insertProjectLink("Name", "custom", "http://link", project);
+    insertProjectLink("Home", "homepage", "http://homepage", project);
+    insertProjectLink2("UUID1", "Name", "custom", "http://link", project, PAST);
+    insertProjectLink2("UUID2", null, "homepage", "http://homepage", project, PAST);
+
+    underTest.execute();
+
+    assertThat(db.select("SELECT UUID, NAME, LINK_TYPE, HREF, PROJECT_UUID, CREATED_AT FROM PROJECT_LINKS2")
+      .stream()
+      .map(map -> new Tuple(map.get("UUID"), map.get("NAME"), map.get("LINK_TYPE"), map.get("HREF"), map.get("PROJECT_UUID"), map.get("CREATED_AT")))
+      .collect(Collectors.toList()))
+        .containsExactlyInAnyOrder(
+          tuple("UUID1", "Name", "custom", "http://link", project, PAST),
+          tuple("UUID2", null, "homepage", "http://homepage", project, PAST));
+  }
+
+  @Test
+  public void migration_is_reentrant() throws SQLException {
+    String project = insertProject();
+    insertProjectLink("Name", "custom", "http://link", project);
+
+    underTest.execute();
+    underTest.execute();
+
+    assertProjectLinks2(tuple("Name", "custom", "http://link", project, NOW, NOW));
+  }
+
+  @Test
+  public void has_no_effect_if_table_is_empty() throws SQLException {
+    underTest.execute();
+
+    assertThat(db.countRowsOfTable("project_links2")).isZero();
+  }
+
+  private void assertNoProjectLinks2() {
+    assertProjectLinks2();
+  }
+
+  private void assertProjectLinks2(Tuple... expectedTuples) {
+    assertThat(db.select("SELECT NAME, LINK_TYPE, HREF, PROJECT_UUID, CREATED_AT, UPDATED_AT FROM PROJECT_LINKS2")
+      .stream()
+      .map(map -> new Tuple(map.get("NAME"), map.get("LINK_TYPE"), map.get("HREF"), map.get("PROJECT_UUID"), map.get("CREATED_AT"), map.get("UPDATED_AT")))
+      .collect(Collectors.toList()))
+        .containsExactlyInAnyOrder(expectedTuples);
+  }
+
+  private void insertProjectLink(String name, String linkType, String href, String componentUuid) {
+    db.executeInsert(
+      "PROJECT_LINKS",
+      "COMPONENT_UUID", componentUuid,
+      "NAME", name,
+      "LINK_TYPE", linkType,
+      "HREF", href);
+  }
+
+  private void insertProjectLink2(String uuid, String name, String linkType, String href, String componentUuid, Long createdAt) {
+    db.executeInsert(
+      "PROJECT_LINKS2",
+      "UUID", uuid,
+      "PROJECT_UUID", componentUuid,
+      "NAME", name,
+      "LINK_TYPE", linkType,
+      "HREF", href,
+      "CREATED_AT", createdAt,
+      "UPDATED_AT", createdAt);
+  }
+
+  private String insertProject() {
+    return insertComponent("PRJ", "TRK");
+  }
+
+  private String insertComponent(String scope, String qualifier) {
+    String uuid = uuidFactory.create();
+    db.executeInsert("PROJECTS",
+      "ORGANIZATION_UUID", "O1",
+      "KEE", uuid,
+      "UUID", uuid,
+      "PROJECT_UUID", uuid,
+      "MAIN_BRANCH_PROJECT_UUID", uuid,
+      "UUID_PATH", ".",
+      "ROOT_UUID", uuid,
+      "PRIVATE", "true",
+      "QUALIFIER", qualifier,
+      "SCOPE", scope);
+    return uuid;
+  }
+
+}
diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinksTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinksTest.java
new file mode 100644 (file)
index 0000000..0440fc4
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+package org.sonar.server.platform.db.migration.version.v71;
+
+import java.sql.SQLException;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.db.CoreDbTester;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class RenameTableProjectLinks2ToProjectLinksTest {
+
+  @Rule
+  public final CoreDbTester dbTester = CoreDbTester.createForSchema(RenameTableProjectLinks2ToProjectLinksTest.class, "project_links2.sql");
+  @Rule
+  public ExpectedException expectedException = ExpectedException.none();
+
+  private RenameTableProjectLinks2ToProjectLinks underTest = new RenameTableProjectLinks2ToProjectLinks(dbTester.database());
+
+  @Test
+  public void creates_table_on_empty_db() throws SQLException {
+    underTest.execute();
+
+    dbTester.assertTableDoesNotExist("project_links2");
+    assertThat(dbTester.countRowsOfTable("project_links")).isZero();
+    dbTester.assertIndex("project_links", "project_links_project", "project_uuid");
+  }
+
+  @Test
+  public void migration_is_not_reentrant() throws SQLException {
+    underTest.execute();
+
+    expectedException.expect(IllegalStateException.class);
+
+    underTest.execute();
+  }
+
+}
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2Test/empty.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2Test/empty.sql
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinksTest/project_links.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinksTest/project_links.sql
new file mode 100644 (file)
index 0000000..9588643
--- /dev/null
@@ -0,0 +1,7 @@
+CREATE TABLE "PROJECT_LINKS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "COMPONENT_UUID" VARCHAR(50),
+  "LINK_TYPE" VARCHAR(20),
+  "NAME" VARCHAR(128),
+  "HREF" VARCHAR(2048) NOT NULL
+);
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2Test/project_links2.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2Test/project_links2.sql
new file mode 100644 (file)
index 0000000..ef474cc
--- /dev/null
@@ -0,0 +1,64 @@
+CREATE TABLE "PROJECT_LINKS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "COMPONENT_UUID" VARCHAR(50),
+  "LINK_TYPE" VARCHAR(20),
+  "NAME" VARCHAR(128),
+  "HREF" VARCHAR(2048) NOT NULL
+);
+
+CREATE TABLE "PROJECT_LINKS2" (
+  "UUID" VARCHAR(40) NOT NULL PRIMARY KEY,
+  "PROJECT_UUID" VARCHAR(50) NOT NULL,
+  "LINK_TYPE" VARCHAR(20) NOT NULL,
+  "NAME" VARCHAR(128),
+  "HREF" VARCHAR(2048) NOT NULL,
+  "CREATED_AT" BIGINT NOT NULL,
+  "UPDATED_AT" BIGINT NOT NULL
+);
+
+CREATE TABLE "PROJECTS" (
+  "ID" INTEGER NOT NULL GENERATED BY DEFAULT AS IDENTITY (START WITH 1, INCREMENT BY 1),
+  "ORGANIZATION_UUID" VARCHAR(40) NOT NULL,
+  "KEE" VARCHAR(400),
+  "UUID" VARCHAR(50) NOT NULL,
+  "UUID_PATH" VARCHAR(1500) NOT NULL,
+  "ROOT_UUID" VARCHAR(50) NOT NULL,
+  "PROJECT_UUID" VARCHAR(50) NOT NULL,
+  "MODULE_UUID" VARCHAR(50),
+  "MODULE_UUID_PATH" VARCHAR(1500),
+  "MAIN_BRANCH_PROJECT_UUID" VARCHAR(50),
+  "NAME" VARCHAR(2000),
+  "DESCRIPTION" VARCHAR(2000),
+  "PRIVATE" BOOLEAN NOT NULL,
+  "TAGS" VARCHAR(500),
+  "ENABLED" BOOLEAN NOT NULL DEFAULT TRUE,
+  "SCOPE" VARCHAR(3),
+  "QUALIFIER" VARCHAR(10),
+  "DEPRECATED_KEE" VARCHAR(400),
+  "PATH" VARCHAR(2000),
+  "LANGUAGE" VARCHAR(20),
+  "COPY_COMPONENT_UUID" VARCHAR(50),
+  "LONG_NAME" VARCHAR(2000),
+  "DEVELOPER_UUID" VARCHAR(50),
+  "CREATED_AT" TIMESTAMP,
+  "AUTHORIZATION_UPDATED_AT" BIGINT,
+  "B_CHANGED" BOOLEAN,
+  "B_COPY_COMPONENT_UUID" VARCHAR(50),
+  "B_DESCRIPTION" VARCHAR(2000),
+  "B_ENABLED" BOOLEAN,
+  "B_UUID_PATH" VARCHAR(1500),
+  "B_LANGUAGE" VARCHAR(20),
+  "B_LONG_NAME" VARCHAR(500),
+  "B_MODULE_UUID" VARCHAR(50),
+  "B_MODULE_UUID_PATH" VARCHAR(1500),
+  "B_NAME" VARCHAR(500),
+  "B_PATH" VARCHAR(2000),
+  "B_QUALIFIER" VARCHAR(10)
+);
+CREATE INDEX "PROJECTS_ORGANIZATION" ON "PROJECTS" ("ORGANIZATION_UUID");
+CREATE UNIQUE INDEX "PROJECTS_KEE" ON "PROJECTS" ("KEE");
+CREATE INDEX "PROJECTS_ROOT_UUID" ON "PROJECTS" ("ROOT_UUID");
+CREATE UNIQUE INDEX "PROJECTS_UUID" ON "PROJECTS" ("UUID");
+CREATE INDEX "PROJECTS_PROJECT_UUID" ON "PROJECTS" ("PROJECT_UUID");
+CREATE INDEX "PROJECTS_MODULE_UUID" ON "PROJECTS" ("MODULE_UUID");
+CREATE INDEX "PROJECTS_QUALIFIER" ON "PROJECTS" ("QUALIFIER");
\ No newline at end of file
diff --git a/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinksTest/project_links2.sql b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinksTest/project_links2.sql
new file mode 100644 (file)
index 0000000..c29900d
--- /dev/null
@@ -0,0 +1,9 @@
+CREATE TABLE "PROJECT_LINKS2" (
+  "UUID" VARCHAR(40) NOT NULL PRIMARY KEY,
+  "PROJECT_UUID" VARCHAR(50) NOT NULL,
+  "LINK_TYPE" VARCHAR(20) NOT NULL,
+  "NAME" VARCHAR(128),
+  "HREF" VARCHAR(2048) NOT NULL,
+  "CREATED_AT" BIGINT NOT NULL,
+  "UPDATED_AT" BIGINT NOT NULL
+);
index f114bce59efbef0b9321ac74c11336068a43955a..fd8bb42438fed1d23d5f7857ac352400a64ace29 100644 (file)
@@ -32,7 +32,7 @@ public class ProjectLinksPage {
   }
 
   public ElementsCollection getLinks() {
-    return Selenide.$$("#project-links tr[data-name]");
+    return Selenide.$$("#project-links tbody tr");
   }
 
   public List<ProjectLinkItem> getLinksAsItems() {
index eb5b14a3d3e8c10fe83b179cf96aa3d1066ef549..c255ba1189fca8efc422cb46336655ed593f1416 100644 (file)
@@ -22,129 +22,88 @@ package org.sonar.server.computation.task.projectanalysis.step;
 import com.google.common.collect.ImmutableMap;
 import java.util.HashSet;
 import java.util.List;
-import java.util.Locale;
 import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
-import org.sonar.api.i18n.I18n;
+import org.sonar.core.util.UuidFactory;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentLinkDto;
+import org.sonar.db.component.ProjectLinkDto;
 import org.sonar.scanner.protocol.output.ScannerReport;
 import org.sonar.scanner.protocol.output.ScannerReport.ComponentLink.ComponentLinkType;
 import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReader;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
-import org.sonar.server.computation.task.projectanalysis.component.CrawlerDepthLimit;
-import org.sonar.server.computation.task.projectanalysis.component.DepthTraversalTypeAwareCrawler;
 import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolder;
-import org.sonar.server.computation.task.projectanalysis.component.TypeAwareVisitorAdapter;
 import org.sonar.server.computation.task.step.ComputationStep;
 
-import static org.sonar.server.computation.task.projectanalysis.component.ComponentVisitor.Order.PRE_ORDER;
+import static com.google.common.base.Preconditions.checkArgument;
 
-/**
- * Persist project and module links
- */
 public class PersistProjectLinksStep implements ComputationStep {
 
   private final DbClient dbClient;
-  private final I18n i18n;
   private final TreeRootHolder treeRootHolder;
   private final BatchReportReader reportReader;
+  private final UuidFactory uuidFactory;
 
   private static final Map<ComponentLinkType, String> typesConverter = ImmutableMap.of(
-    ComponentLinkType.HOME, ComponentLinkDto.TYPE_HOME_PAGE,
-    ComponentLinkType.SCM, ComponentLinkDto.TYPE_SOURCES,
-    ComponentLinkType.SCM_DEV, ComponentLinkDto.TYPE_SOURCES_DEV,
-    ComponentLinkType.CI, ComponentLinkDto.TYPE_CI,
-    ComponentLinkType.ISSUE, ComponentLinkDto.TYPE_ISSUE_TRACKER);
+    ComponentLinkType.HOME, ProjectLinkDto.TYPE_HOME_PAGE,
+    ComponentLinkType.SCM, ProjectLinkDto.TYPE_SOURCES,
+    ComponentLinkType.SCM_DEV, ProjectLinkDto.TYPE_SOURCES_DEV,
+    ComponentLinkType.CI, ProjectLinkDto.TYPE_CI,
+    ComponentLinkType.ISSUE, ProjectLinkDto.TYPE_ISSUE_TRACKER);
 
-  public PersistProjectLinksStep(DbClient dbClient, I18n i18n, TreeRootHolder treeRootHolder, BatchReportReader reportReader) {
+  public PersistProjectLinksStep(DbClient dbClient, TreeRootHolder treeRootHolder, BatchReportReader reportReader, UuidFactory uuidFactory) {
     this.dbClient = dbClient;
-    this.i18n = i18n;
     this.treeRootHolder = treeRootHolder;
     this.reportReader = reportReader;
+    this.uuidFactory = uuidFactory;
   }
 
   @Override
   public void execute() {
     try (DbSession session = dbClient.openSession(false)) {
-      new DepthTraversalTypeAwareCrawler(new ProjectLinkVisitor(session))
-        .visit(treeRootHolder.getRoot());
+      Component project = treeRootHolder.getRoot();
+      ScannerReport.Component batchComponent = reportReader.readComponent(project.getReportAttributes().getRef());
+      List<ProjectLinkDto> previousLinks = dbClient.projectLinkDao().selectByProjectUuid(session, project.getUuid());
+      mergeLinks(session, project.getUuid(), batchComponent.getLinkList(), previousLinks);
       session.commit();
     }
   }
 
-  private class ProjectLinkVisitor extends TypeAwareVisitorAdapter {
-
-    private final DbSession session;
-
-    private ProjectLinkVisitor(DbSession session) {
-      super(CrawlerDepthLimit.FILE, PRE_ORDER);
-      this.session = session;
-    }
-
-    @Override
-    public void visitProject(Component project) {
-      processComponent(project);
-    }
-
-    @Override
-    public void visitModule(Component module) {
-      processComponent(module);
-    }
-
-    private void processComponent(Component component) {
-      ScannerReport.Component batchComponent = reportReader.readComponent(component.getReportAttributes().getRef());
-      processLinks(component.getUuid(), batchComponent.getLinkList());
-    }
-
-    private void processLinks(String componentUuid, List<ScannerReport.ComponentLink> links) {
-      List<ComponentLinkDto> previousLinks = dbClient.componentLinkDao().selectByComponentUuid(session, componentUuid);
-      mergeLinks(session, componentUuid, links, previousLinks);
-    }
-
-    private void mergeLinks(DbSession session, String componentUuid, List<ScannerReport.ComponentLink> links, List<ComponentLinkDto> previousLinks) {
-      Set<String> linkType = new HashSet<>();
-      for (final ScannerReport.ComponentLink link : links) {
+  private void mergeLinks(DbSession session, String componentUuid, List<ScannerReport.ComponentLink> links, List<ProjectLinkDto> previousLinks) {
+    Set<String> linkType = new HashSet<>();
+    links.forEach(
+      link -> {
         String type = convertType(link.getType());
-        if (!linkType.contains(type)) {
-          linkType.add(type);
-        } else {
-          throw new IllegalArgumentException(String.format("Link of type '%s' has already been declared on component '%s'", type, componentUuid));
-        }
+        checkArgument(!linkType.contains(type), "Link of type '%s' has already been declared on component '%s'", type, componentUuid);
+        linkType.add(type);
 
-        Optional<ComponentLinkDto> previousLink = previousLinks.stream()
+        Optional<ProjectLinkDto> previousLink = previousLinks.stream()
           .filter(input -> input != null && input.getType().equals(convertType(link.getType())))
           .findFirst();
         if (previousLink.isPresent()) {
           previousLink.get().setHref(link.getHref());
-          dbClient.componentLinkDao().update(session, previousLink.get());
+          dbClient.projectLinkDao().update(session, previousLink.get());
         } else {
-          dbClient.componentLinkDao().insert(session,
-            new ComponentLinkDto()
-              .setComponentUuid(componentUuid)
+          dbClient.projectLinkDao().insert(session,
+            new ProjectLinkDto()
+              .setUuid(uuidFactory.create())
+              .setProjectUuid(componentUuid)
               .setType(type)
-              .setName(i18n.message(Locale.ENGLISH, "project_links." + type, null))
               .setHref(link.getHref()));
         }
-      }
+      });
 
-      for (ComponentLinkDto dto : previousLinks) {
-        if (!linkType.contains(dto.getType()) && ComponentLinkDto.PROVIDED_TYPES.contains(dto.getType())) {
-          dbClient.componentLinkDao().delete(session, dto.getId());
-        }
-      }
-    }
+    previousLinks.stream()
+      .filter(dto -> !linkType.contains(dto.getType()))
+      .filter(dto -> ProjectLinkDto.PROVIDED_TYPES.contains(dto.getType()))
+      .forEach(dto -> dbClient.projectLinkDao().delete(session, dto.getUuid()));
+  }
 
-    private String convertType(ComponentLinkType reportType) {
-      String type = typesConverter.get(reportType);
-      if (type != null) {
-        return type;
-      } else {
-        throw new IllegalArgumentException(String.format("Unsupported type %s", reportType.name()));
-      }
-    }
+  private static String convertType(ComponentLinkType reportType) {
+    String type = typesConverter.get(reportType);
+    checkArgument(type != null, "Unsupported type %s", reportType.name());
+    return type;
   }
 
   @Override
index 13a77a87161725f8236b27b4e92ed50294a8d6b3..1ec0e29c2a1d01943be66fb2611f16f08733b5b3 100644 (file)
@@ -37,8 +37,8 @@ import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentLinkDto;
 import org.sonar.db.component.ComponentQuery;
+import org.sonar.db.component.ProjectLinkDto;
 import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.measure.LiveMeasureDto;
 import org.sonar.server.user.UserSession;
@@ -148,11 +148,11 @@ public class SearchMyProjectsAction implements ProjectsWsAction {
     }
   }
 
-  private enum ProjectLinkDtoToWs implements Function<ComponentLinkDto, Link> {
+  private enum ProjectLinkDtoToWs implements Function<ProjectLinkDto, Link> {
     INSTANCE;
 
     @Override
-    public Link apply(ComponentLinkDto dto) {
+    public Link apply(ProjectLinkDto dto) {
       Link.Builder link = Link.newBuilder();
       link.setHref(dto.getHref());
 
@@ -172,7 +172,7 @@ public class SearchMyProjectsAction implements ProjectsWsAction {
     ProjectsResult searchResult = searchProjects(dbSession, request);
     List<ComponentDto> projects = searchResult.projects;
     List<String> projectUuids = Lists.transform(projects, ComponentDto::projectUuid);
-    List<ComponentLinkDto> projectLinks = dbClient.componentLinkDao().selectByComponentUuids(dbSession, projectUuids);
+    List<ProjectLinkDto> projectLinks = dbClient.projectLinkDao().selectByProjectUuids(dbSession, projectUuids);
     List<SnapshotDto> snapshots = dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, projectUuids);
     List<LiveMeasureDto> qualityGates = dbClient.liveMeasureDao()
       .selectByComponentUuidsAndMetricKeys(dbSession, projectUuids, singletonList(CoreMetrics.ALERT_STATUS_KEY));
index 21361d0998b5377d1d714a61a231212e6df3dda3..29f9069eedb7584abd314cc19945066063b207ca 100644 (file)
@@ -27,7 +27,7 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.stream.Collectors;
 import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentLinkDto;
+import org.sonar.db.component.ProjectLinkDto;
 import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.measure.LiveMeasureDto;
 
@@ -37,7 +37,7 @@ import static org.sonar.api.utils.DateUtils.formatDateTime;
 
 class SearchMyProjectsData {
   private final List<ComponentDto> projects;
-  private final ListMultimap<String, ComponentLinkDto> projectLinksByProjectUuid;
+  private final ListMultimap<String, ProjectLinkDto> projectLinksByProjectUuid;
   private final Map<String, String> lastAnalysisDates;
   private final Map<String, String> qualityGateStatuses;
   private final int totalNbOfProject;
@@ -58,7 +58,7 @@ class SearchMyProjectsData {
     return projects;
   }
 
-  List<ComponentLinkDto> projectLinksFor(String projectUuid) {
+  List<ProjectLinkDto> projectLinksFor(String projectUuid) {
     return projectLinksByProjectUuid.get(projectUuid);
   }
 
@@ -74,9 +74,9 @@ class SearchMyProjectsData {
     return totalNbOfProject;
   }
 
-  private static ListMultimap<String, ComponentLinkDto> buildProjectLinks(List<ComponentLinkDto> dtos) {
-    ImmutableListMultimap.Builder<String, ComponentLinkDto> projectLinks = ImmutableListMultimap.builder();
-    dtos.forEach(projectLink -> projectLinks.put(projectLink.getComponentUuid(), projectLink));
+  private static ListMultimap<String, ProjectLinkDto> buildProjectLinks(List<ProjectLinkDto> dtos) {
+    ImmutableListMultimap.Builder<String, ProjectLinkDto> projectLinks = ImmutableListMultimap.builder();
+    dtos.forEach(projectLink -> projectLinks.put(projectLink.getProjectUuid(), projectLink));
     return projectLinks.build();
   }
 
@@ -93,7 +93,7 @@ class SearchMyProjectsData {
 
   static class Builder {
     private List<ComponentDto> projects;
-    private List<ComponentLinkDto> projectLinks;
+    private List<ProjectLinkDto> projectLinks;
     private List<SnapshotDto> snapshots;
     private List<LiveMeasureDto> qualityGates;
     private Integer totalNbOfProjects;
@@ -107,7 +107,7 @@ class SearchMyProjectsData {
       return this;
     }
 
-    public Builder setProjectLinks(List<ComponentLinkDto> projectLinks) {
+    public Builder setProjectLinks(List<ProjectLinkDto> projectLinks) {
       this.projectLinks = projectLinks;
       return this;
     }
index a4c810cbfaa1a7a8159143b0ea6b52b6761ea24e..2730840dde542be8676f0db5043cc4809eb85efe 100644 (file)
@@ -23,10 +23,11 @@ import org.sonar.api.server.ws.Request;
 import org.sonar.api.server.ws.Response;
 import org.sonar.api.server.ws.WebService;
 import org.sonar.api.web.UserRole;
+import org.sonar.core.util.UuidFactory;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentLinkDto;
+import org.sonar.db.component.ProjectLinkDto;
 import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.ProjectLinks;
@@ -34,27 +35,30 @@ import org.sonarqube.ws.ProjectLinks.CreateWsResponse;
 
 import static org.sonar.core.util.Slug.slugify;
 import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
-import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
-import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import static org.sonar.server.projectlink.ws.ProjectLinksWs.checkProject;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.ACTION_CREATE;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_NAME;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_PROJECT_ID;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_PROJECT_KEY;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_URL;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
 
 public class CreateAction implements ProjectLinksWsAction {
   private final DbClient dbClient;
   private final UserSession userSession;
   private final ComponentFinder componentFinder;
+  private final UuidFactory uuidFactory;
 
   private static final int LINK_NAME_MAX_LENGTH = 128;
   private static final int LINK_URL_MAX_LENGTH = 2048;
   private static final int LINK_TYPE_MAX_LENGTH = 20;
 
-  public CreateAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder) {
+  public CreateAction(DbClient dbClient, UserSession userSession, ComponentFinder componentFinder, UuidFactory uuidFactory) {
     this.dbClient = dbClient;
     this.userSession = userSession;
     this.componentFinder = componentFinder;
+    this.uuidFactory = uuidFactory;
   }
 
   @Override
@@ -101,25 +105,26 @@ public class CreateAction implements ProjectLinksWsAction {
     String url = createWsRequest.getUrl();
 
     try (DbSession dbSession = dbClient.openSession(false)) {
-      ComponentDto component = getComponentByUuidOrKey(dbSession, createWsRequest);
+      ComponentDto component = checkProject(getComponentByUuidOrKey(dbSession, createWsRequest));
 
       userSession.checkComponentPermission(UserRole.ADMIN, component);
 
-      ComponentLinkDto link = new ComponentLinkDto()
-        .setComponentUuid(component.uuid())
+      ProjectLinkDto link = new ProjectLinkDto()
+        .setUuid(uuidFactory.create())
+        .setProjectUuid(component.uuid())
         .setName(name)
         .setHref(url)
         .setType(nameToType(name));
-      dbClient.componentLinkDao().insert(dbSession, link);
+      dbClient.projectLinkDao().insert(dbSession, link);
 
       dbSession.commit();
       return buildResponse(link);
     }
   }
 
-  private static CreateWsResponse buildResponse(ComponentLinkDto link) {
+  private static CreateWsResponse buildResponse(ProjectLinkDto link) {
     return CreateWsResponse.newBuilder().setLink(ProjectLinks.Link.newBuilder()
-      .setId(String.valueOf(link.getId()))
+      .setId(String.valueOf(link.getUuid()))
       .setName(link.getName())
       .setType(link.getType())
       .setUrl(link.getHref()))
@@ -127,7 +132,7 @@ public class CreateAction implements ProjectLinksWsAction {
   }
 
   private ComponentDto getComponentByUuidOrKey(DbSession dbSession, CreateRequest request) {
-    return componentFinder.getRootComponentByUuidOrKey(
+    return componentFinder.getByUuidOrKey(
       dbSession,
       request.getProjectId(),
       request.getProjectKey(),
index 5dfaeb57bfbdf6dccf078caeb38c847986f28ac7..bcba6beb4eda6cb6cce6246db66b9f01b3bbb16f 100644 (file)
@@ -25,11 +25,11 @@ import org.sonar.api.server.ws.WebService;
 import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
-import org.sonar.db.component.ComponentLinkDto;
+import org.sonar.db.component.ProjectLinkDto;
 import org.sonar.server.user.UserSession;
 import org.sonar.server.ws.WsUtils;
 
-import static org.sonar.db.component.ComponentLinkDto.PROVIDED_TYPES;
+import static org.sonar.db.component.ProjectLinkDto.PROVIDED_TYPES;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.ACTION_DELETE;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_ID;
 
@@ -64,27 +64,26 @@ public class DeleteAction implements ProjectLinksWsAction {
     response.noContent();
   }
 
-  private void doHandle(String idParam) {
+  private void doHandle(String id) {
     try (DbSession dbSession = dbClient.openSession(false)) {
-      long id = Long.parseLong(idParam);
-      ComponentLinkDto link = dbClient.componentLinkDao().selectById(dbSession, id);
+      ProjectLinkDto link = dbClient.projectLinkDao().selectByUuid(dbSession, id);
 
       link = WsUtils.checkFound(link, "Link with id '%s' not found", id);
       checkProjectAdminPermission(link);
       checkNotProvided(link);
 
-      dbClient.componentLinkDao().delete(dbSession, link.getId());
+      dbClient.projectLinkDao().delete(dbSession, link.getUuid());
       dbSession.commit();
     }
   }
 
-  private static void checkNotProvided(ComponentLinkDto link) {
+  private static void checkNotProvided(ProjectLinkDto link) {
     String type = link.getType();
     boolean isProvided = type != null && PROVIDED_TYPES.contains(type);
     WsUtils.checkRequest(!isProvided, "Provided link cannot be deleted.");
   }
 
-  private void checkProjectAdminPermission(ComponentLinkDto link) {
-    userSession.checkComponentUuidPermission(UserRole.ADMIN, link.getComponentUuid());
+  private void checkProjectAdminPermission(ProjectLinkDto link) {
+    userSession.checkComponentUuidPermission(UserRole.ADMIN, link.getProjectUuid());
   }
 }
index bcf5ccc4c801ab1bcab52e2c98d9f26ce4555144..eb2b7fe0addbc90c9ffd568b9d2b8fc1182deb3f 100644 (file)
  */
 package org.sonar.server.projectlink.ws;
 
+import org.sonar.api.resources.Qualifiers;
+import org.sonar.api.resources.Scopes;
 import org.sonar.api.server.ws.WebService;
+import org.sonar.db.component.ComponentDto;
+
+import static java.lang.String.format;
+import static org.sonar.server.ws.WsUtils.checkRequest;
 
 public class ProjectLinksWs implements WebService {
 
@@ -42,4 +48,10 @@ public class ProjectLinksWs implements WebService {
     controller.done();
   }
 
+  static ComponentDto checkProject(ComponentDto component) {
+    checkRequest(component.scope().equals(Scopes.PROJECT) && component.qualifier().equals(Qualifiers.PROJECT),
+      format("Component '%s' must be a project.", component.getKey()));
+    return component;
+  }
+
 }
index 5de432d37aa00edf00b9a45bf148bd66f41edaad..2548fd3876f23d5e977e87a8a6df5af126cdea26 100644 (file)
@@ -28,7 +28,7 @@ import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentLinkDto;
+import org.sonar.db.component.ProjectLinkDto;
 import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.user.UserSession;
 import org.sonarqube.ws.ProjectLinks.Link;
@@ -36,12 +36,13 @@ import org.sonarqube.ws.ProjectLinks.SearchWsResponse;
 
 import static org.sonar.core.util.Protobuf.setNullable;
 import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
-import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
-import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
-import static org.sonar.server.ws.WsUtils.writeProtobuf;
+import static org.sonar.server.projectlink.ws.ProjectLinksWs.checkProject;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.ACTION_SEARCH;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_PROJECT_ID;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_PROJECT_KEY;
+import static org.sonar.server.user.AbstractUserSession.insufficientPrivilegesException;
+import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.server.ws.WsUtils.writeProtobuf;
 
 public class SearchAction implements ProjectLinksWsAction {
   private final DbClient dbClient;
@@ -67,7 +68,7 @@ public class SearchAction implements ProjectLinksWsAction {
         "</ul>",
         PARAM_PROJECT_ID, PARAM_PROJECT_KEY)
       .setHandler(this)
-      .setResponseExample(getClass().getResource("list-example.json"))
+      .setResponseExample(getClass().getResource("search-example.json"))
       .setSince("6.1");
 
     action.createParam(PARAM_PROJECT_ID)
@@ -90,13 +91,13 @@ public class SearchAction implements ProjectLinksWsAction {
   private SearchWsResponse doHandle(SearchRequest searchWsRequest) {
     try (DbSession dbSession = dbClient.openSession(false)) {
       ComponentDto component = getComponentByUuidOrKey(dbSession, searchWsRequest);
-      List<ComponentLinkDto> links = dbClient.componentLinkDao()
-        .selectByComponentUuid(dbSession, component.uuid());
+      List<ProjectLinkDto> links = dbClient.projectLinkDao()
+        .selectByProjectUuid(dbSession, component.uuid());
       return buildResponse(links);
     }
   }
 
-  private static SearchWsResponse buildResponse(List<ComponentLinkDto> links) {
+  private static SearchWsResponse buildResponse(List<ProjectLinkDto> links) {
     return SearchWsResponse.newBuilder()
       .addAllLinks(links.stream()
         .map(SearchAction::buildLink)
@@ -104,28 +105,26 @@ public class SearchAction implements ProjectLinksWsAction {
       .build();
   }
 
-  private static Link buildLink(ComponentLinkDto link) {
+  private static Link buildLink(ProjectLinkDto link) {
     Link.Builder builder = Link.newBuilder()
-      .setId(String.valueOf(link.getId()))
+      .setId(String.valueOf(link.getUuid()))
+      .setType(link.getType())
       .setUrl(link.getHref());
     setNullable(link.getName(), builder::setName);
-    setNullable(link.getType(), builder::setType);
     return builder.build();
   }
 
   private ComponentDto getComponentByUuidOrKey(DbSession dbSession, SearchRequest request) {
-    ComponentDto component = componentFinder.getRootComponentByUuidOrKey(
+    ComponentDto component = componentFinder.getByUuidOrKey(
       dbSession,
       request.getProjectId(),
       request.getProjectKey(),
       ComponentFinder.ParamNames.PROJECT_ID_AND_KEY);
-
     if (!userSession.hasComponentPermission(UserRole.ADMIN, component) &&
       !userSession.hasComponentPermission(UserRole.USER, component)) {
       throw insufficientPrivilegesException();
     }
-
-    return component;
+    return checkProject(component);
   }
 
   private static SearchRequest toSearchWsRequest(Request request) {
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/list-example.json b/server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/list-example.json
deleted file mode 100644 (file)
index 8638919..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-  "links": [
-    {
-      "id": "1",
-      "name": "Homepage",
-      "type": "homepage",
-      "url": "http://example.org"
-    },
-    {
-      "id": "2",
-      "name": "Custom",
-      "url": "http://example.org/custom"
-    }
-  ]
-}
\ No newline at end of file
diff --git a/server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/search-example.json b/server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/search-example.json
new file mode 100644 (file)
index 0000000..5f0afb8
--- /dev/null
@@ -0,0 +1,16 @@
+{
+  "links": [
+    {
+      "id": "1",
+      "name": "Homepage",
+      "type": "homepage",
+      "url": "http://example.org"
+    },
+    {
+      "id": "2",
+      "name": "Custom",
+      "type": "custom",
+      "url": "http://example.org/custom"
+    }
+  ]
+}
\ No newline at end of file
index 9b13a24ea77e2e1bbe763445fd61a09602601458..b13cc434b1a4f6e06bac912822fe1d6a408ed858 100644 (file)
  */
 package org.sonar.server.computation.task.projectanalysis.step;
 
-import java.util.Locale;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
-import org.sonar.api.i18n.I18n;
+import org.junit.rules.ExpectedException;
 import org.sonar.api.utils.System2;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbTester;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ProjectLinkDto;
 import org.sonar.scanner.protocol.output.ScannerReport;
 import org.sonar.scanner.protocol.output.ScannerReport.Component.ComponentType;
-import org.sonar.scanner.protocol.output.ScannerReport.ComponentLink.ComponentLinkType;
 import org.sonar.server.computation.task.projectanalysis.batch.BatchReportReaderRule;
-import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
 import org.sonar.server.computation.task.projectanalysis.component.Component;
 import org.sonar.server.computation.task.projectanalysis.component.ReportComponent;
-import org.sonar.server.computation.task.projectanalysis.component.VisitException;
+import org.sonar.server.computation.task.projectanalysis.component.TreeRootHolderRule;
 import org.sonar.server.computation.task.step.ComputationStep;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.failBecauseExceptionWasNotThrown;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import static org.assertj.core.api.Assertions.tuple;
+import static org.sonar.scanner.protocol.output.ScannerReport.ComponentLink.ComponentLinkType.CI;
+import static org.sonar.scanner.protocol.output.ScannerReport.ComponentLink.ComponentLinkType.HOME;
+import static org.sonar.scanner.protocol.output.ScannerReport.ComponentLink.ComponentLinkType.ISSUE;
+import static org.sonar.scanner.protocol.output.ScannerReport.ComponentLink.ComponentLinkType.SCM;
+import static org.sonar.scanner.protocol.output.ScannerReport.ComponentLink.ComponentLinkType.SCM_DEV;
 
 public class PersistProjectLinksStepTest extends BaseStepTest {
 
   @Rule
-  public DbTester dbTester = DbTester.create(System2.INSTANCE);
+  public ExpectedException expectedException = ExpectedException.none();
+
+  @Rule
+  public DbTester db = DbTester.create(System2.INSTANCE);
 
   @Rule
   public TreeRootHolderRule treeRootHolder = new TreeRootHolderRule();
@@ -52,19 +57,7 @@ public class PersistProjectLinksStepTest extends BaseStepTest {
   @Rule
   public BatchReportReaderRule reportReader = new BatchReportReaderRule();
 
-  PersistProjectLinksStep step;
-
-  @Before
-  public void setup() {
-    I18n i18n = mock(I18n.class);
-    when(i18n.message(Locale.ENGLISH, "project_links.homepage", null)).thenReturn("Home");
-    when(i18n.message(Locale.ENGLISH, "project_links.scm", null)).thenReturn("Sources");
-    when(i18n.message(Locale.ENGLISH, "project_links.scm_dev", null)).thenReturn("Developer connection");
-    when(i18n.message(Locale.ENGLISH, "project_links.ci", null)).thenReturn("Continuous integration");
-    when(i18n.message(Locale.ENGLISH, "project_links.issue", null)).thenReturn("Issues");
-
-    step = new PersistProjectLinksStep(dbTester.getDbClient(), i18n, treeRootHolder, reportReader);
-  }
+  PersistProjectLinksStep step = new PersistProjectLinksStep(db.getDbClient(), treeRootHolder, reportReader, UuidFactoryFast.getInstance());
 
   @Override
   protected ComputationStep step() {
@@ -72,56 +65,78 @@ public class PersistProjectLinksStepTest extends BaseStepTest {
   }
 
   @Test
-  public void add_links_on_project_and_module() {
-    dbTester.prepareDbUnit(getClass(), "empty.xml");
-
+  public void add_links_on_project() {
     treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").addChildren(
       ReportComponent.builder(Component.Type.MODULE, 2).setUuid("BCDE").build())
       .build());
 
-    // project and 1 module
+    // project
     reportReader.putComponent(ScannerReport.Component.newBuilder()
       .setRef(1)
       .setType(ComponentType.PROJECT)
       .addChildRef(2)
-      .addLink(ScannerReport.ComponentLink.newBuilder().setType(ComponentLinkType.HOME).setHref("http://www.sonarqube.org").build())
-      .addLink(ScannerReport.ComponentLink.newBuilder().setType(ComponentLinkType.SCM).setHref("https://github.com/SonarSource/sonar").build())
-      .addLink(ScannerReport.ComponentLink.newBuilder().setType(ComponentLinkType.SCM_DEV).setHref("scm:git:git@github.com:SonarSource/sonar.git/sonar").build())
-      .addLink(ScannerReport.ComponentLink.newBuilder().setType(ComponentLinkType.ISSUE).setHref("http://jira.sonarsource.com/").build())
-      .addLink(ScannerReport.ComponentLink.newBuilder().setType(ComponentLinkType.CI).setHref("http://bamboo.ci.codehaus.org/browse/SONAR").build())
-      .build());
-    reportReader.putComponent(ScannerReport.Component.newBuilder()
-      .setRef(2)
-      .setType(ComponentType.MODULE)
-      .addLink(ScannerReport.ComponentLink.newBuilder().setType(ComponentLinkType.SCM).setHref("https://github.com/SonarSource/sonar/server").build())
+      .addLink(ScannerReport.ComponentLink.newBuilder().setType(HOME).setHref("http://www.sonarqube.org").build())
+      .addLink(ScannerReport.ComponentLink.newBuilder().setType(SCM).setHref("https://github.com/SonarSource/sonar").build())
+      .addLink(ScannerReport.ComponentLink.newBuilder().setType(SCM_DEV).setHref("scm:git:git@github.com:SonarSource/sonar.git/sonar").build())
+      .addLink(ScannerReport.ComponentLink.newBuilder().setType(ISSUE).setHref("http://jira.sonarsource.com/").build())
+      .addLink(ScannerReport.ComponentLink.newBuilder().setType(CI).setHref("http://bamboo.ci.codehaus.org/browse/SONAR").build())
       .build());
 
     step.execute();
 
-    dbTester.assertDbUnit(getClass(), "add_links_on_project_and_module-result.xml", "project_links");
+    assertThat(db.getDbClient().projectLinkDao().selectByProjectUuid(db.getSession(), "ABCD"))
+      .extracting(ProjectLinkDto::getType, ProjectLinkDto::getHref, ProjectLinkDto::getName)
+      .containsExactlyInAnyOrder(
+        tuple("homepage", "http://www.sonarqube.org", null),
+        tuple("scm", "https://github.com/SonarSource/sonar", null),
+        tuple("scm_dev", "scm:git:git@github.com:SonarSource/sonar.git/sonar", null),
+        tuple("issue", "http://jira.sonarsource.com/", null),
+        tuple("ci", "http://bamboo.ci.codehaus.org/browse/SONAR", null));
   }
 
   @Test
   public void nothing_to_do_when_link_already_exists() {
-    dbTester.prepareDbUnit(getClass(), "nothing_to_do_when_link_already_exists.xml");
+    ComponentDto project = db.components().insertPrivateProject(p -> p.setUuid("ABCD"));
+    db.componentLinks().insertProvidedLink(project, l -> l.setType("homepage").setName("Home").setHref("http://www.sonarqube.org"));
 
     treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").build());
 
     reportReader.putComponent(ScannerReport.Component.newBuilder()
       .setRef(1)
       .setType(ComponentType.PROJECT)
-      .addLink(ScannerReport.ComponentLink.newBuilder().setType(ComponentLinkType.HOME).setHref("http://www.sonarqube.org").build())
+      .addLink(ScannerReport.ComponentLink.newBuilder().setType(HOME).setHref("http://www.sonarqube.org").build())
       .build());
 
     step.execute();
 
-    dbTester.assertDbUnit(getClass(), "nothing_to_do_when_link_already_exists.xml", "project_links");
+    assertThat(db.getDbClient().projectLinkDao().selectByProjectUuid(db.getSession(), "ABCD"))
+      .extracting(ProjectLinkDto::getType, ProjectLinkDto::getHref)
+      .containsExactlyInAnyOrder(tuple("homepage", "http://www.sonarqube.org"));
   }
 
   @Test
-  public void do_not_add_links_on_file() {
-    dbTester.prepareDbUnit(getClass(), "empty.xml");
+  public void do_not_add_links_on_module() {
+    treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").addChildren(
+      ReportComponent.builder(Component.Type.MODULE, 2).setUuid("BCDE").build())
+      .build());
+    reportReader.putComponent(ScannerReport.Component.newBuilder()
+      .setRef(1)
+      .setType(ComponentType.PROJECT)
+      .addChildRef(2)
+      .build());
+    reportReader.putComponent(ScannerReport.Component.newBuilder()
+      .setRef(2)
+      .setType(ComponentType.MODULE)
+      .addLink(ScannerReport.ComponentLink.newBuilder().setType(HOME).setHref("http://www.sonarqube.org").build())
+      .build());
 
+    step.execute();
+
+    assertThat(db.countRowsOfTable("project_links")).isZero();
+  }
+
+  @Test
+  public void do_not_add_links_on_file() {
     treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").addChildren(
       ReportComponent.builder(Component.Type.FILE, 2).setUuid("BCDE").build())
       .build());
@@ -134,34 +149,38 @@ public class PersistProjectLinksStepTest extends BaseStepTest {
     reportReader.putComponent(ScannerReport.Component.newBuilder()
       .setRef(2)
       .setType(ComponentType.FILE)
-      .addLink(ScannerReport.ComponentLink.newBuilder().setType(ComponentLinkType.HOME).setHref("http://www.sonarqube.org").build())
+      .addLink(ScannerReport.ComponentLink.newBuilder().setType(HOME).setHref("http://www.sonarqube.org").build())
       .build());
 
     step.execute();
 
-    assertThat(dbTester.countRowsOfTable("project_links")).isEqualTo(0);
+    assertThat(db.countRowsOfTable("project_links")).isZero();
   }
 
   @Test
   public void update_link() {
-    dbTester.prepareDbUnit(getClass(), "update_link.xml");
+    ComponentDto project = db.components().insertPrivateProject(p -> p.setUuid("ABCD"));
+    db.componentLinks().insertProvidedLink(project, l -> l.setType("homepage").setName("Home").setHref("http://www.sonar.org"));
 
     treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").build());
 
     reportReader.putComponent(ScannerReport.Component.newBuilder()
       .setRef(1)
       .setType(ComponentType.PROJECT)
-      .addLink(ScannerReport.ComponentLink.newBuilder().setType(ComponentLinkType.HOME).setHref("http://www.sonarqube.org").build())
+      .addLink(ScannerReport.ComponentLink.newBuilder().setType(HOME).setHref("http://www.sonarqube.org").build())
       .build());
 
     step.execute();
 
-    dbTester.assertDbUnit(getClass(), "update_link-result.xml", "project_links");
+    assertThat(db.getDbClient().projectLinkDao().selectByProjectUuid(db.getSession(), "ABCD"))
+      .extracting(ProjectLinkDto::getType, ProjectLinkDto::getHref)
+      .containsExactlyInAnyOrder(tuple("homepage", "http://www.sonarqube.org"));
   }
 
   @Test
   public void delete_link() {
-    dbTester.prepareDbUnit(getClass(), "delete_link.xml");
+    ComponentDto project = db.components().insertPrivateProject(p -> p.setUuid("ABCD"));
+    db.componentLinks().insertProvidedLink(project, l -> l.setType("homepage").setName("Home").setHref("http://www.sonar.org"));
 
     treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").build());
 
@@ -172,12 +191,13 @@ public class PersistProjectLinksStepTest extends BaseStepTest {
 
     step.execute();
 
-    assertThat(dbTester.countRowsOfTable("project_links")).isEqualTo(0);
+    assertThat(db.countRowsOfTable("project_links")).isZero();
   }
 
   @Test
   public void not_delete_custom_link() {
-    dbTester.prepareDbUnit(getClass(), "not_delete_custom_link.xml");
+    ComponentDto project = db.components().insertPrivateProject(p -> p.setUuid("ABCD"));
+    db.componentLinks().insertCustomLink(project);
 
     treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").build());
 
@@ -188,28 +208,23 @@ public class PersistProjectLinksStepTest extends BaseStepTest {
 
     step.execute();
 
-    dbTester.assertDbUnit(getClass(), "not_delete_custom_link.xml", "project_links");
+    assertThat(db.countRowsOfTable("project_links")).isEqualTo(1);
   }
 
   @Test
   public void fail_when_trying_to_add_same_link_type_multiple_times() {
-    dbTester.prepareDbUnit(getClass(), "empty.xml");
-
     treeRootHolder.setRoot(ReportComponent.builder(Component.Type.PROJECT, 1).setUuid("ABCD").build());
 
     reportReader.putComponent(ScannerReport.Component.newBuilder()
       .setRef(1)
       .setType(ComponentType.PROJECT)
-      .addLink(ScannerReport.ComponentLink.newBuilder().setType(ComponentLinkType.HOME).setHref("http://www.sonarqube.org").build())
-      .addLink(ScannerReport.ComponentLink.newBuilder().setType(ComponentLinkType.HOME).setHref("http://www.sonarqube.org").build())
+      .addLink(ScannerReport.ComponentLink.newBuilder().setType(HOME).setHref("http://www.sonarqube.org").build())
+      .addLink(ScannerReport.ComponentLink.newBuilder().setType(HOME).setHref("http://www.sonarqube.org").build())
       .build());
 
-    try {
-      step.execute();
-      failBecauseExceptionWasNotThrown(VisitException.class);
-    } catch (VisitException e) {
-      assertThat(e.getCause()).isInstanceOf(IllegalArgumentException.class);
-      assertThat(e.getCause()).hasMessage("Link of type 'homepage' has already been declared on component 'ABCD'");
-    }
+    expectedException.expect(IllegalArgumentException.class);
+    expectedException.expectMessage("Link of type 'homepage' has already been declared on component 'ABCD'");
+
+    step.execute();
   }
 }
index e9551d7315c056f665ac7795fefca948d2fa5f41..bcb8a89785b3c909fa211c627334ff8aade038c8 100644 (file)
@@ -34,8 +34,8 @@ import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentLinkDto;
 import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.component.ProjectLinkDto;
 import org.sonar.db.component.SnapshotDto;
 import org.sonar.db.metric.MetricDto;
 import org.sonar.db.organization.OrganizationDto;
@@ -87,10 +87,8 @@ public class SearchMyProjectsActionTest {
     OrganizationDto organizationDto = db.organizations().insert();
     ComponentDto jdk7 = insertJdk7(organizationDto);
     ComponentDto cLang = insertClang(organizationDto);
-    dbClient.componentLinkDao().insert(dbSession,
-      new ComponentLinkDto().setComponentUuid(jdk7.uuid()).setHref("http://www.oracle.com").setType(ComponentLinkDto.TYPE_HOME_PAGE).setName("Home"));
-    dbClient.componentLinkDao().insert(dbSession,
-      new ComponentLinkDto().setComponentUuid(jdk7.uuid()).setHref("http://download.java.net/openjdk/jdk8/").setType(ComponentLinkDto.TYPE_SOURCES).setName("Sources"));
+    db.componentLinks().insertProvidedLink(jdk7, l -> l.setHref("http://www.oracle.com").setType(ProjectLinkDto.TYPE_HOME_PAGE).setName("Home"));
+    db.componentLinks().insertProvidedLink(jdk7, l -> l.setHref("http://download.java.net/openjdk/jdk8/").setType(ProjectLinkDto.TYPE_SOURCES).setName("Sources"));
     long oneTime = DateUtils.parseDateTime("2016-06-10T13:17:53+0000").getTime();
     long anotherTime = DateUtils.parseDateTime("2016-06-11T14:25:53+0000").getTime();
     SnapshotDto jdk7Snapshot = dbClient.snapshotDao().insert(dbSession, newAnalysis(jdk7).setCreatedAt(oneTime));
index c97eaedbb193d63c0c070fa0f1c6dee559e749b2..ed689e9649bbfa19a882a59268f15af66536ba68 100644 (file)
  */
 package org.sonar.server.projectlink.ws;
 
-import java.io.IOException;
 import java.util.Random;
 import org.apache.commons.lang.StringUtils;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
+import org.sonar.core.util.UuidFactoryFast;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
 import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentLinkDto;
 import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.component.ProjectLinkDto;
 import org.sonar.db.organization.OrganizationDto;
-import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.component.TestComponentFinder;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
@@ -47,19 +46,14 @@ import org.sonarqube.ws.ProjectLinks;
 
 import static java.lang.String.format;
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
-import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
-import static org.sonar.test.JsonAssert.assertJson;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_NAME;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_PROJECT_ID;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_PROJECT_KEY;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_URL;
+import static org.sonar.test.JsonAssert.assertJson;
 
 public class CreateActionTest {
 
-  private final String PROJECT_KEY = KEY_PROJECT_EXAMPLE_001;
-  private final String PROJECT_UUID = UUID_EXAMPLE_01;
-
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
   @Rule
@@ -70,20 +64,11 @@ public class CreateActionTest {
   private DbClient dbClient = db.getDbClient();
   private DbSession dbSession = db.getSession();
 
-  private WsActionTester ws;
-
-  private CreateAction underTest;
-
-  @Before
-  public void setUp() {
-    ComponentFinder componentFinder = TestComponentFinder.from(db);
-    underTest = new CreateAction(dbClient, userSession, componentFinder);
-    ws = new WsActionTester(underTest);
-  }
+  private WsActionTester ws = new WsActionTester(new CreateAction(dbClient, userSession, TestComponentFinder.from(db), UuidFactoryFast.getInstance()));
 
   @Test
   public void example_with_key() {
-    ComponentDto project = insertProject();
+    ComponentDto project = db.components().insertPrivateProject();
     logInAsProjectAdministrator(project);
 
     String result = ws.newRequest()
@@ -98,7 +83,7 @@ public class CreateActionTest {
 
   @Test
   public void example_with_id() {
-    ComponentDto project = insertProject();
+    ComponentDto project = db.components().insertPrivateProject();
     logInAsProjectAdministrator(project);
 
     String result = ws.newRequest()
@@ -112,19 +97,20 @@ public class CreateActionTest {
   }
 
   @Test
-  public void require_project_admin() throws IOException {
-    ComponentDto project = insertProject();
+  public void require_project_admin() {
+    ComponentDto project = db.components().insertPrivateProject();
     logInAsProjectAdministrator(project);
+
     createAndTest(project);
   }
 
   @Test
-  public void with_long_name() throws IOException {
-    ComponentDto project = insertProject();
+  public void with_long_name() {
+    ComponentDto project = db.components().insertPrivateProject();
     logInAsProjectAdministrator(project);
-
     String longName = StringUtils.leftPad("", 60, "a");
     String expectedType = StringUtils.leftPad("", 20, "a");
+
     createAndTest(project, longName, "http://example.org", expectedType);
   }
 
@@ -180,11 +166,13 @@ public class CreateActionTest {
   @Test
   public void fail_if_anonymous() {
     userSession.anonymous();
-    insertProject();
+    ComponentDto project = db.components().insertPublicProject();
+    userSession.registerComponents(project);
 
     expectedException.expect(ForbiddenException.class);
+
     ws.newRequest()
-      .setParam(PARAM_PROJECT_KEY, PROJECT_KEY)
+      .setParam(PARAM_PROJECT_KEY, project.getKey())
       .setParam(PARAM_NAME, "Custom")
       .setParam(PARAM_URL, "http://example.org")
       .execute();
@@ -193,11 +181,12 @@ public class CreateActionTest {
   @Test
   public void fail_if_not_project_admin() {
     userSession.logIn();
-    insertProject();
+    ComponentDto project = db.components().insertPrivateProject();
 
     expectedException.expect(ForbiddenException.class);
+
     ws.newRequest()
-      .setParam(PARAM_PROJECT_KEY, PROJECT_KEY)
+      .setParam(PARAM_PROJECT_KEY, project.getKey())
       .setParam(PARAM_NAME, "Custom")
       .setParam(PARAM_URL, "http://example.org")
       .execute();
@@ -225,10 +214,9 @@ public class CreateActionTest {
   }
 
   @Test
-  public void fail_if_subview() {
+  public void fail_if_view() {
     ComponentDto view = db.components().insertView();
-    ComponentDto subview = db.components().insertComponent(ComponentTesting.newSubView(view));
-    failIfNotAProject(view, subview);
+    failIfNotAProject(view, view);
   }
 
   @Test
@@ -239,7 +227,7 @@ public class CreateActionTest {
     ComponentDto branch = db.components().insertProjectBranch(project);
 
     expectedException.expect(NotFoundException.class);
-    expectedException.expectMessage(format("Project key '%s' not found", branch.getDbKey()));
+    expectedException.expectMessage(format("Component key '%s' not found", branch.getDbKey()));
 
     ws.newRequest()
       .setParam(PARAM_PROJECT_KEY, branch.getDbKey())
@@ -256,7 +244,7 @@ public class CreateActionTest {
     ComponentDto branch = db.components().insertProjectBranch(project);
 
     expectedException.expect(NotFoundException.class);
-    expectedException.expectMessage(format("Project id '%s' not found", branch.uuid()));
+    expectedException.expectMessage(format("Component id '%s' not found", branch.uuid()));
 
     ws.newRequest()
       .setParam(PARAM_PROJECT_ID, branch.uuid())
@@ -265,11 +253,21 @@ public class CreateActionTest {
       .execute();
   }
 
+  @Test
+  public void define_create_action() {
+    WebService.Action action = ws.getDef();
+    assertThat(action).isNotNull();
+    assertThat(action.isPost()).isTrue();
+    assertThat(action.handler()).isNotNull();
+    assertThat(action.responseExampleAsString()).isNotEmpty();
+    assertThat(action.params()).hasSize(4);
+  }
+
   private void failIfNotAProject(ComponentDto root, ComponentDto component) {
     userSession.logIn().addProjectPermission(UserRole.ADMIN, root);
 
     expectedException.expect(BadRequestException.class);
-    expectedException.expectMessage("Component '" + component.getDbKey() + "' (id: " + component.uuid() + ") must be a project");
+    expectedException.expectMessage("Component '" + component.getDbKey() + "' must be a project");
 
     TestRequest testRequest = ws.newRequest();
     if (new Random().nextBoolean()) {
@@ -283,12 +281,6 @@ public class CreateActionTest {
       .execute();
   }
 
-  private ComponentDto insertProject() {
-    OrganizationDto org = db.organizations().insert();
-    return db.components().insertComponent(
-      ComponentTesting.newPrivateProjectDto(org, PROJECT_UUID).setDbKey(PROJECT_KEY));
-  }
-
   private void createAndTest(ComponentDto project, String name, String url, String type) {
     ProjectLinks.CreateWsResponse response = ws.newRequest()
       .setMethod("POST")
@@ -297,15 +289,15 @@ public class CreateActionTest {
       .setParam(PARAM_URL, url)
       .executeProtobuf(ProjectLinks.CreateWsResponse.class);
 
-    long newId = Long.valueOf(response.getLink().getId());
+    String newId = response.getLink().getId();
 
-    ComponentLinkDto link = dbClient.componentLinkDao().selectById(dbSession, newId);
+    ProjectLinkDto link = dbClient.projectLinkDao().selectByUuid(dbSession, newId);
     assertThat(link.getName()).isEqualTo(name);
     assertThat(link.getHref()).isEqualTo(url);
     assertThat(link.getType()).isEqualTo(type);
   }
 
-  private void createAndTest(ComponentDto project) throws IOException {
+  private void createAndTest(ComponentDto project) {
     createAndTest(project, "Custom", "http://example.org", "custom");
   }
 
index 6b990660466e86f2699f3cf15a6d03b5edf8f4c0..eee39643a1b0df5bd126f72cad608926961ccc84 100644 (file)
  */
 package org.sonar.server.projectlink.ws;
 
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.System2;
-import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
 import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDbTester;
 import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentLinkDto;
+import org.sonar.db.component.ProjectLinkDto;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
 import org.sonar.server.exceptions.NotFoundException;
@@ -39,15 +37,11 @@ import org.sonar.server.ws.TestResponse;
 import org.sonar.server.ws.WsActionTester;
 
 import static org.assertj.core.api.Assertions.assertThat;
-import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
-import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
+import static org.sonar.api.web.UserRole.ADMIN;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_ID;
 
 public class DeleteActionTest {
 
-  private static final String PROJECT_KEY = KEY_PROJECT_EXAMPLE_001;
-  private static final String PROJECT_UUID = UUID_EXAMPLE_01;
-
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
   @Rule
@@ -57,155 +51,115 @@ public class DeleteActionTest {
 
   private DbClient dbClient = db.getDbClient();
   private DbSession dbSession = db.getSession();
-  private ComponentDbTester componentDb = new ComponentDbTester(db);
-
-  private WsActionTester ws;
-
-  private DeleteAction underTest;
 
-  @Before
-  public void setUp() {
-    underTest = new DeleteAction(dbClient, userSession);
-    ws = new WsActionTester(underTest);
-  }
+  private WsActionTester ws = new WsActionTester(new DeleteAction(dbClient, userSession));
 
   @Test
   public void no_response() {
-    ComponentDto project = insertProject();
-    ComponentLinkDto link = insertCustomLink(project.uuid());
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link = db.componentLinks().insertCustomLink(project);
     logInAsProjectAdministrator(project);
 
-    TestResponse response = deleteLink(link.getId());
+    TestResponse response = deleteLink(link);
 
     assertThat(response.getStatus()).isEqualTo(204);
     assertThat(response.getInput()).isEmpty();
   }
 
   @Test
-  public void actual_removal() {
-    ComponentDto project = insertProject();
-    ComponentLinkDto link = insertCustomLink(project.uuid());
-    long id = link.getId();
+  public void remove_custom_link() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link = db.componentLinks().insertCustomLink(project);
     logInAsProjectAdministrator(project);
 
-    deleteLink(id);
-    assertLinkIsDeleted(id);
+    deleteLink(link);
+
+    assertLinkIsDeleted(link.getUuid());
   }
 
   @Test
   public void keep_links_of_another_project() {
-    ComponentDto project1 = insertProject();
-    ComponentDto project2 = insertProject("another", "abcd");
-    ComponentLinkDto customLink1 = insertCustomLink(project1.uuid());
-    ComponentLinkDto customLink2 = insertCustomLink(project2.uuid());
-    Long id1 = customLink1.getId();
-    Long id2 = customLink2.getId();
-    userSession.logIn().addProjectPermission(UserRole.ADMIN, project1, project2);
-
-    deleteLink(id1);
-    assertLinkIsDeleted(id1);
-    assertLinkIsNotDeleted(id2);
+    ComponentDto project1 = db.components().insertPrivateProject();
+    ComponentDto project2 = db.components().insertPrivateProject();
+    ProjectLinkDto customLink1 = db.componentLinks().insertCustomLink(project1);
+    ProjectLinkDto customLink2 = db.componentLinks().insertCustomLink(project2);
+    userSession.logIn().addProjectPermission(ADMIN, project1, project2);
+
+    deleteLink(customLink1);
+
+    assertLinkIsDeleted(customLink1.getUuid());
+    assertLinkIsNotDeleted(customLink2.getUuid());
   }
 
   @Test
   public void fail_when_delete_provided_link() {
-    ComponentDto project = insertProject();
-    ComponentLinkDto link = insertHomepageLink(project.uuid());
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link = db.componentLinks().insertProvidedLink(project);
     logInAsProjectAdministrator(project);
 
     expectedException.expect(BadRequestException.class);
+    expectedException.expectMessage("Provided link cannot be deleted");
 
-    deleteLink(link.getId());
+    deleteLink(link);
   }
 
   @Test
-  public void fail_when_no_link() {
+  public void fail_on_unknown_link() {
     expectedException.expect(NotFoundException.class);
 
-    deleteLink("175");
+    ws.newRequest()
+      .setMethod("POST")
+      .setParam(PARAM_ID, "UNKNOWN")
+      .execute();
   }
 
   @Test
   public void fail_if_anonymous() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link = db.componentLinks().insertCustomLink(project);
     userSession.anonymous();
 
-    ComponentDto project = insertProject();
-    ComponentLinkDto link = insertCustomLink(project.uuid());
-
     expectedException.expect(ForbiddenException.class);
 
-    deleteLink(link.getId());
+    deleteLink(link);
   }
 
   @Test
   public void fail_if_not_project_admin() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link = db.componentLinks().insertCustomLink(project);
     userSession.logIn();
 
-    ComponentDto project = insertProject();
-    ComponentLinkDto link = insertCustomLink(project.uuid());
-
     expectedException.expect(ForbiddenException.class);
-    deleteLink(link.getId());
+    deleteLink(link);
   }
 
-  private ComponentDto insertProject(String projectKey, String projectUuid) {
-    return componentDb.insertComponent(new ComponentDto()
-      .setOrganizationUuid("org1")
-      .setUuid(projectUuid)
-      .setDbKey(projectKey)
-      .setUuidPath("")
-      .setRootUuid("")
-      .setProjectUuid(""));
-  }
-
-  private ComponentDto insertProject() {
-    return insertProject(PROJECT_KEY, PROJECT_UUID);
-  }
-
-  private void insertLink(ComponentLinkDto linkDto) {
-    dbClient.componentLinkDao().insert(dbSession, linkDto);
-    dbSession.commit();
-  }
-
-  private ComponentLinkDto insertHomepageLink(String projectUuid) {
-    ComponentLinkDto link = new ComponentLinkDto()
-      .setComponentUuid(projectUuid)
-      .setName("Homepage")
-      .setType("homepage")
-      .setHref("http://example.org");
-    insertLink(link);
-    return link;
-  }
-
-  private ComponentLinkDto insertCustomLink(String projectUuid) {
-    ComponentLinkDto link = new ComponentLinkDto()
-      .setComponentUuid(projectUuid)
-      .setName("Custom")
-      .setHref("http://example.org/custom");
-    insertLink(link);
-    return link;
+  @Test
+  public void define_delete_action() {
+    WebService.Action action = ws.getDef();
+    assertThat(action).isNotNull();
+    assertThat(action.isPost()).isTrue();
+    assertThat(action.handler()).isNotNull();
+    assertThat(action.responseExample()).isNull();
+    assertThat(action.params()).hasSize(1);
   }
 
-  private TestResponse deleteLink(String id) {
+  private TestResponse deleteLink(ProjectLinkDto link) {
     return ws.newRequest()
       .setMethod("POST")
-      .setParam(PARAM_ID, id)
+      .setParam(PARAM_ID, link.getUuid())
       .execute();
   }
 
-  private TestResponse deleteLink(Long id) {
-    return deleteLink(String.valueOf(id));
-  }
-
-  private void assertLinkIsDeleted(Long id) {
-    assertThat(dbClient.componentLinkDao().selectById(dbSession, id)).isNull();
+  private void assertLinkIsDeleted(String uuid) {
+    assertThat(dbClient.projectLinkDao().selectByUuid(dbSession, uuid)).isNull();
   }
 
-  private void assertLinkIsNotDeleted(Long id) {
-    assertThat(dbClient.componentLinkDao().selectById(dbSession, id)).isNotNull();
+  private void assertLinkIsNotDeleted(String uuid) {
+    assertThat(dbClient.projectLinkDao().selectByUuid(dbSession, uuid)).isNotNull();
   }
 
   private void logInAsProjectAdministrator(ComponentDto project) {
-    userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
+    userSession.logIn().addProjectPermission(ADMIN, project);
   }
 }
diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java
deleted file mode 100644 (file)
index 38bc02d..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * 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.projectlink.ws;
-
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonar.api.server.ws.WebService;
-import org.sonar.db.DbClient;
-import org.sonar.server.component.ComponentFinder;
-import org.sonar.server.tester.UserSessionRule;
-import org.sonar.server.ws.WsTester;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.mockito.Mockito.mock;
-
-public class ProjectLinksWsTest {
-
-  @Rule
-  public UserSessionRule userSessionRule = UserSessionRule.standalone();
-
-  WebService.Controller controller;
-
-  @Before
-  public void setUp() {
-    WsTester tester = new WsTester(new ProjectLinksWs(
-      new SearchAction(mock(DbClient.class), userSessionRule, mock(ComponentFinder.class)),
-      new CreateAction(mock(DbClient.class), userSessionRule, mock(ComponentFinder.class)),
-      new DeleteAction(mock(DbClient.class), userSessionRule)
-    ));
-    controller = tester.controller("api/project_links");
-  }
-
-  @Test
-  public void define_controller() {
-    assertThat(controller).isNotNull();
-    assertThat(controller.description()).isNotEmpty();
-    assertThat(controller.since()).isEqualTo("6.1");
-    assertThat(controller.actions()).hasSize(3);
-  }
-
-  @Test
-  public void define_search_action() {
-    WebService.Action action = controller.action("search");
-    assertThat(action).isNotNull();
-    assertThat(action.isPost()).isFalse();
-    assertThat(action.handler()).isNotNull();
-    assertThat(action.responseExampleAsString()).isNotEmpty();
-    assertThat(action.params()).hasSize(2);
-  }
-
-  @Test
-  public void define_create_action() {
-    WebService.Action action = controller.action("create");
-    assertThat(action).isNotNull();
-    assertThat(action.isPost()).isTrue();
-    assertThat(action.handler()).isNotNull();
-    assertThat(action.responseExampleAsString()).isNotEmpty();
-    assertThat(action.params()).hasSize(4);
-  }
-
-  @Test
-  public void define_delete_action() {
-    WebService.Action action = controller.action("delete");
-    assertThat(action).isNotNull();
-    assertThat(action.isPost()).isTrue();
-    assertThat(action.handler()).isNotNull();
-    assertThat(action.responseExample()).isNull();
-    assertThat(action.params()).hasSize(1);
-  }
-}
index 6d288e3b487311b49862eba1715eb0c9bfdef423..8974e886bb57411297b41e5f50eacd07e9386c1b 100644 (file)
  */
 package org.sonar.server.projectlink.ws;
 
-import java.io.IOException;
 import java.util.Random;
-import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
+import org.sonar.api.server.ws.WebService;
 import org.sonar.api.utils.System2;
 import org.sonar.api.web.UserRole;
 import org.sonar.db.DbClient;
-import org.sonar.db.DbSession;
 import org.sonar.db.DbTester;
-import org.sonar.db.component.ComponentDbTester;
 import org.sonar.db.component.ComponentDto;
-import org.sonar.db.component.ComponentLinkDto;
 import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.component.ProjectLinkDto;
 import org.sonar.db.organization.OrganizationDto;
-import org.sonar.server.component.ComponentFinder;
 import org.sonar.server.component.TestComponentFinder;
 import org.sonar.server.exceptions.BadRequestException;
 import org.sonar.server.exceptions.ForbiddenException;
@@ -49,18 +45,12 @@ import org.sonarqube.ws.ProjectLinks.SearchWsResponse;
 import static java.lang.String.format;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.tuple;
-import static org.sonar.core.util.Uuids.UUID_EXAMPLE_01;
-import static org.sonar.db.component.ComponentTesting.newPrivateProjectDto;
-import static org.sonar.server.ws.KeyExamples.KEY_PROJECT_EXAMPLE_001;
-import static org.sonar.test.JsonAssert.assertJson;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_PROJECT_ID;
 import static org.sonar.server.projectlink.ws.ProjectLinksWsParameters.PARAM_PROJECT_KEY;
+import static org.sonar.test.JsonAssert.assertJson;
 
 public class SearchActionTest {
 
-  private final String PROJECT_KEY = KEY_PROJECT_EXAMPLE_001;
-  private final String PROJECT_UUID = UUID_EXAMPLE_01;
-
   @Rule
   public ExpectedException expectedException = ExpectedException.none();
   @Rule
@@ -69,125 +59,120 @@ public class SearchActionTest {
   public DbTester db = DbTester.create(System2.INSTANCE);
 
   private DbClient dbClient = db.getDbClient();
-  private DbSession dbSession = db.getSession();
-  private ComponentDbTester componentDb = new ComponentDbTester(db);
-
-  private SearchAction underTest;
-  private WsActionTester ws;
 
-  @Before
-  public void setUp() {
-    ComponentFinder componentFinder = TestComponentFinder.from(db);
-    underTest = new SearchAction(dbClient, userSession, componentFinder);
-    ws = new WsActionTester(underTest);
-  }
+  private WsActionTester ws = new WsActionTester(new SearchAction(dbClient, userSession, TestComponentFinder.from(db)));
 
   @Test
   public void example() {
-    ComponentDto project = insertProject();
-    insertHomepageLink(project.uuid());
-    insertCustomLink(project.uuid());
+    ComponentDto project = db.components().insertPrivateProject();
+    db.componentLinks().insertProvidedLink(project, l -> l.setUuid("1").setType("homepage").setName("Homepage").setHref("http://example.org"));
+    db.componentLinks().insertCustomLink(project, l -> l.setUuid("2").setType("custom").setName("Custom").setHref("http://example.org/custom"));
     logInAsProjectAdministrator(project);
 
     String result = ws.newRequest()
-      .setParam(PARAM_PROJECT_KEY, PROJECT_KEY)
+      .setParam(PARAM_PROJECT_KEY, project.getKey())
       .execute().getInput();
 
-    assertJson(result).ignoreFields("id").isSimilarTo(getClass().getResource("list-example.json"));
+    assertJson(result).isSimilarTo(getClass().getResource("search-example.json"));
   }
 
   @Test
-  public void request_by_project_id() throws IOException {
-    ComponentDto project = insertProject();
-    insertHomepageLink(project.uuid());
+  public void request_by_project_id() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link = db.componentLinks().insertCustomLink(project);
     logInAsProjectAdministrator(project);
 
     SearchWsResponse response = callByUuid(project.uuid());
 
-    assertThat(response.getLinksCount()).isEqualTo(1);
-    assertThat(response.getLinks(0).getName()).isEqualTo("Homepage");
+    assertThat(response.getLinksList())
+      .extracting(Link::getId, Link::getName)
+      .containsExactlyInAnyOrder(tuple(link.getUuid(), link.getName()));
   }
 
   @Test
-  public void request_by_project_key() throws IOException {
-    ComponentDto project = insertProject();
-    insertHomepageLink(project.uuid());
+  public void request_by_project_key() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link = db.componentLinks().insertCustomLink(project);
     logInAsProjectAdministrator(project);
 
-    SearchWsResponse response = callByKey(project.getDbKey());
+    SearchWsResponse response = callByKey(project.getKey());
 
-    assertThat(response.getLinksCount()).isEqualTo(1);
-    assertThat(response.getLinks(0).getName()).isEqualTo("Homepage");
+    assertThat(response.getLinksList())
+      .extracting(Link::getId, Link::getName)
+      .containsExactlyInAnyOrder(tuple(link.getUuid(), link.getName()));
   }
 
   @Test
-  public void response_fields() throws IOException {
-    ComponentDto project = insertProject();
-    ComponentLinkDto homepageLink = insertHomepageLink(project.uuid());
-    ComponentLinkDto customLink = insertCustomLink(project.uuid());
+  public void response_fields() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto homepageLink = db.componentLinks().insertProvidedLink(project);
+    ProjectLinkDto customLink = db.componentLinks().insertCustomLink(project);
     logInAsProjectAdministrator(project);
 
-    SearchWsResponse response = callByKey(project.getDbKey());
+    SearchWsResponse response = callByKey(project.getKey());
 
-    assertThat(response.getLinksCount()).isEqualTo(2);
     assertThat(response.getLinksList()).extracting(Link::getId, Link::getName, Link::getType, Link::getUrl)
-      .containsOnlyOnce(
-        tuple(homepageLink.getIdAsString(), homepageLink.getName(), homepageLink.getType(), homepageLink.getHref()),
-        tuple(customLink.getIdAsString(), customLink.getName(), customLink.getType(), customLink.getHref()));
+      .containsExactlyInAnyOrder(
+        tuple(homepageLink.getUuid(), "", homepageLink.getType(), homepageLink.getHref()),
+        tuple(customLink.getUuid(), customLink.getName(), customLink.getType(), customLink.getHref()));
   }
 
   @Test
-  public void several_projects() throws IOException {
-    ComponentDto project1 = insertProject();
-    ComponentDto project2 = insertProject("another", "abcd");
-    ComponentLinkDto customLink1 = insertCustomLink(project1.uuid());
-    insertCustomLink(project2.uuid());
+  public void several_projects() {
+    ComponentDto project1 = db.components().insertPrivateProject();
+    ComponentDto project2 = db.components().insertPrivateProject();
+    ProjectLinkDto link1 = db.componentLinks().insertCustomLink(project1);
+    ProjectLinkDto link2 = db.componentLinks().insertCustomLink(project2);
     userSession.logIn().setRoot();
 
-    SearchWsResponse response = callByKey(project1.getDbKey());
+    SearchWsResponse response = callByKey(project1.getKey());
 
-    assertThat(response.getLinksCount()).isEqualTo(1);
-    assertThat(response.getLinks(0).getId()).isEqualTo(customLink1.getIdAsString());
+    assertThat(response.getLinksList())
+      .extracting(Link::getId, Link::getName)
+      .containsExactlyInAnyOrder(tuple(link1.getUuid(), link1.getName()));
   }
 
   @Test
-  public void request_does_not_fail_when_link_has_no_name() throws IOException {
+  public void request_does_not_fail_when_link_has_no_name() {
     ComponentDto project = db.components().insertPrivateProject();
-    ComponentLinkDto foo = new ComponentLinkDto().setComponentUuid(project.uuid()).setHref("foo").setType("type");
-    insertLink(foo);
+    ProjectLinkDto link = db.componentLinks().insertProvidedLink(project);
     logInAsProjectAdministrator(project);
 
-    callByKey(project.getDbKey());
-  }
-
-  @Test
-  public void request_does_not_fail_when_link_has_no_type() throws IOException {
-    ComponentDto project = db.components().insertPrivateProject();
-    ComponentLinkDto foo = new ComponentLinkDto().setComponentUuid(project.uuid()).setHref("foo").setName("name");
-    insertLink(foo);
-    logInAsProjectAdministrator(project);
+    SearchWsResponse response = callByKey(project.getKey());
 
-    callByKey(project.getDbKey());
+    assertThat(response.getLinksList())
+      .extracting(Link::getId, Link::hasName)
+      .containsExactlyInAnyOrder(tuple(link.getUuid(), false));
   }
 
   @Test
-  public void project_administrator_can_search_for_links() throws IOException {
-    ComponentDto project = insertProject();
+  public void project_administrator_can_search_for_links() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link = db.componentLinks().insertCustomLink(project);
     userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
 
-    checkItWorks(project);
+    SearchWsResponse response = callByKey(project.getKey());
+
+    assertThat(response.getLinksList())
+      .extracting(Link::getId, Link::getName)
+      .containsExactlyInAnyOrder(tuple(link.getUuid(), link.getName()));
   }
 
   @Test
-  public void project_user_can_search_for_links() throws IOException {
-    ComponentDto project = insertProject();
+  public void project_user_can_search_for_links() {
+    ComponentDto project = db.components().insertPrivateProject();
+    ProjectLinkDto link = db.componentLinks().insertCustomLink(project);
     userSession.logIn().addProjectPermission(UserRole.USER, project);
 
-    checkItWorks(project);
+    SearchWsResponse response = callByKey(project.getKey());
+
+    assertThat(response.getLinksList())
+      .extracting(Link::getId, Link::getName)
+      .containsExactlyInAnyOrder(tuple(link.getUuid(), link.getName()));
   }
 
   @Test
-  public void fail_when_no_project() throws IOException {
+  public void fail_when_no_project() {
     expectedException.expect(NotFoundException.class);
     callByKey("unknown");
   }
@@ -214,56 +199,41 @@ public class SearchActionTest {
   }
 
   @Test
-  public void fail_if_subview() {
+  public void fail_if_view() {
     ComponentDto view = db.components().insertView();
-    ComponentDto subview = db.components().insertComponent(ComponentTesting.newSubView(view));
-    failIfNotAProject(view, subview);
-  }
-
-  private void failIfNotAProject(ComponentDto root, ComponentDto component) {
-    userSession.logIn().addProjectPermission(UserRole.ADMIN, root);
-
-    expectedException.expect(BadRequestException.class);
-    expectedException.expectMessage("Component '" + component.getDbKey() + "' (id: " + component.uuid() + ") must be a project");
-
-    TestRequest testRequest = ws.newRequest();
-    if (new Random().nextBoolean()) {
-      testRequest.setParam(PARAM_PROJECT_KEY, component.getDbKey());
-    } else {
-      testRequest.setParam(PARAM_PROJECT_ID, component.uuid());
-    }
-    testRequest.execute();
+    failIfNotAProject(view, view);
   }
 
   @Test
-  public void fail_if_insufficient_privileges() throws IOException {
+  public void fail_if_insufficient_privileges() {
     userSession.anonymous();
-    insertProject();
+    ComponentDto project = db.components().insertPrivateProject();
 
     expectedException.expect(ForbiddenException.class);
-    callByKey(PROJECT_KEY);
+
+    callByKey(project.getKey());
   }
 
   @Test
   public void fail_when_both_id_and_key_are_provided() {
-    ComponentDto project = insertProject();
+    ComponentDto project = db.components().insertPrivateProject();
     logInAsProjectAdministrator(project);
 
     expectedException.expect(IllegalArgumentException.class);
     ws.newRequest()
-      .setParam(PARAM_PROJECT_KEY, project.getDbKey())
+      .setParam(PARAM_PROJECT_KEY, project.getKey())
       .setParam(PARAM_PROJECT_ID, project.uuid())
       .execute();
   }
 
   @Test
   public void fail_when_no_id_nor_key_are_provided() {
-    insertProject();
+    ComponentDto project = db.components().insertPrivateProject();
 
     expectedException.expect(IllegalArgumentException.class);
     ws.newRequest()
-      .setParam(PARAM_PROJECT_KEY, PROJECT_KEY)
-      .setParam(PARAM_PROJECT_ID, PROJECT_UUID)
+      .setParam(PARAM_PROJECT_KEY, project.getKey())
+      .setParam(PARAM_PROJECT_ID, project.uuid())
       .execute();
   }
 
@@ -275,7 +245,7 @@ public class SearchActionTest {
     ComponentDto branch = db.components().insertProjectBranch(project);
 
     expectedException.expect(NotFoundException.class);
-    expectedException.expectMessage(format("Project key '%s' not found", branch.getDbKey()));
+    expectedException.expectMessage(format("Component key '%s' not found", branch.getDbKey()));
 
     ws.newRequest()
       .setParam(PARAM_PROJECT_KEY, branch.getDbKey())
@@ -290,44 +260,21 @@ public class SearchActionTest {
     ComponentDto branch = db.components().insertProjectBranch(project);
 
     expectedException.expect(NotFoundException.class);
-    expectedException.expectMessage(format("Project id '%s' not found", branch.uuid()));
+    expectedException.expectMessage(format("Component id '%s' not found", branch.uuid()));
 
     ws.newRequest()
       .setParam(PARAM_PROJECT_ID, branch.uuid())
       .execute();
   }
 
-  private ComponentDto insertProject(String projectKey, String projectUuid) {
-    return componentDb.insertComponent(newPrivateProjectDto(db.organizations().insert(), projectUuid).setDbKey(projectKey));
-  }
-
-  private ComponentDto insertProject() {
-    return insertProject(PROJECT_KEY, PROJECT_UUID);
-  }
-
-  private void insertLink(ComponentLinkDto linkDto) {
-    dbClient.componentLinkDao().insert(dbSession, linkDto);
-    dbSession.commit();
-  }
-
-  private ComponentLinkDto insertHomepageLink(String projectUuid) {
-    ComponentLinkDto link = new ComponentLinkDto()
-      .setComponentUuid(projectUuid)
-      .setName("Homepage")
-      .setType("homepage")
-      .setHref("http://example.org");
-    insertLink(link);
-    return link;
-  }
-
-  private ComponentLinkDto insertCustomLink(String projectUuid) {
-    ComponentLinkDto link = new ComponentLinkDto()
-      .setComponentUuid(projectUuid)
-      .setName("Custom")
-      .setType("Custom")
-      .setHref("http://example.org/custom");
-    insertLink(link);
-    return link;
+  @Test
+  public void define_search_action() {
+    WebService.Action action = ws.getDef();
+    assertThat(action).isNotNull();
+    assertThat(action.isPost()).isFalse();
+    assertThat(action.handler()).isNotNull();
+    assertThat(action.responseExampleAsString()).isNotEmpty();
+    assertThat(action.params()).hasSize(2);
   }
 
   private SearchWsResponse callByKey(String projectKey) {
@@ -342,14 +289,22 @@ public class SearchActionTest {
       .executeProtobuf(SearchWsResponse.class);
   }
 
-  private void checkItWorks(ComponentDto project) throws IOException {
-    insertHomepageLink(project.uuid());
-    SearchWsResponse response = callByKey(project.getDbKey());
-    assertThat(response.getLinksCount()).isEqualTo(1);
-    assertThat(response.getLinks(0).getName()).isEqualTo("Homepage");
-  }
-
   private void logInAsProjectAdministrator(ComponentDto project) {
     userSession.logIn().addProjectPermission(UserRole.ADMIN, project);
   }
+
+  private void failIfNotAProject(ComponentDto root, ComponentDto component) {
+    userSession.logIn().addProjectPermission(UserRole.ADMIN, root);
+
+    expectedException.expect(BadRequestException.class);
+    expectedException.expectMessage("Component '" + component.getKey() + "' must be a project");
+
+    TestRequest testRequest = ws.newRequest();
+    if (new Random().nextBoolean()) {
+      testRequest.setParam(PARAM_PROJECT_KEY, component.getDbKey());
+    } else {
+      testRequest.setParam(PARAM_PROJECT_ID, component.uuid());
+    }
+    testRequest.execute();
+  }
 }
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/add_links_on_project_and_module-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/add_links_on_project_and_module-result.xml
deleted file mode 100644 (file)
index 008f804..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-<dataset>
-
-  <project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/>
-  <project_links id="2" component_uuid="ABCD" link_type="scm" name="Sources" href="https://github.com/SonarSource/sonar"/>
-  <project_links id="3" component_uuid="ABCD" link_type="scm_dev" name="Developer connection" href="scm:git:git@github.com:SonarSource/sonar.git/sonar"/>
-  <project_links id="4" component_uuid="ABCD" link_type="issue" name="Issues" href="http://jira.sonarsource.com/"/>
-  <project_links id="5" component_uuid="ABCD" link_type="ci" name="Continuous integration" href="http://bamboo.ci.codehaus.org/browse/SONAR"/>
-
-  <project_links id="6" component_uuid="BCDE" link_type="scm" name="Sources" href="https://github.com/SonarSource/sonar/server"/>
-
-</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/delete_link.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/delete_link.xml
deleted file mode 100644 (file)
index 8b89e72..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<dataset>
-
-  <project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/>
-
-</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/empty.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/empty.xml
deleted file mode 100644 (file)
index 871dedc..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-<dataset>
-
-</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/not_delete_custom_link.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/not_delete_custom_link.xml
deleted file mode 100644 (file)
index 78c6c1d..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<dataset>
-
-  <project_links id="1" component_uuid="ABCD" link_type="custom" name="custom" href="http://www.custom.org"/>
-
-</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/nothing_to_do_when_link_already_exists.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/nothing_to_do_when_link_already_exists.xml
deleted file mode 100644 (file)
index 8b89e72..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<dataset>
-
-  <project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/>
-
-</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/update_link-result.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/update_link-result.xml
deleted file mode 100644 (file)
index 8b89e72..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<dataset>
-
-  <project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonarqube.org"/>
-
-</dataset>
diff --git a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/update_link.xml b/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/update_link.xml
deleted file mode 100644 (file)
index d402810..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-<dataset>
-
-  <project_links id="1" component_uuid="ABCD" link_type="homepage" name="Home" href="http://www.sonar.org"/>
-
-</dataset>
index a2a63ee87c9073aaadbbf157808f5536bf0cbd85..1cbb95dd0e051ba122665d369321f4ed5397381d 100644 (file)
@@ -50,7 +50,7 @@ public class ProjectLinksTest {
   @ClassRule
   public static RuleChain ruleChain = RuleChain.outerRule(orchestrator).around(tester);
 
-  private long customLinkId;
+  private String customLinkId;
   private String adminUser;
 
   @BeforeClass
@@ -62,7 +62,7 @@ public class ProjectLinksTest {
 
   @Before
   public void prepare() {
-    customLinkId = Long.parseLong(createCustomLink().getLink().getId());
+    customLinkId = createCustomLink().getLink().getId();
     adminUser = tester.users().generateAdministratorOnDefaultOrganization().getLogin();
   }
 
@@ -136,9 +136,9 @@ public class ProjectLinksTest {
       .setUrl("http://example.org/custom"));
   }
 
-  private void deleteLink(long id) {
+  private void deleteLink(String id) {
     try {
-      tester.wsClient().projectLinks().delete(new DeleteRequest().setId("" + id));
+      tester.wsClient().projectLinks().delete(new DeleteRequest().setId(id));
     } catch (Exception e) {
       // fail silently
     }