From 209c36aefa2c16b5b156b21604664ff32ef13a86 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Mon, 12 Feb 2018 10:44:44 +0100 Subject: [PATCH] SONAR-10411 Do not persist name of provided project links --- .../org/sonar/db/version/schema-h2.ddl | 12 +- .../src/main/java/org/sonar/db/DaoModule.java | 4 +- .../src/main/java/org/sonar/db/DbClient.java | 10 +- .../src/main/java/org/sonar/db/MyBatis.java | 6 +- .../sonar/db/component/ComponentLinkDao.java | 61 ----- .../sonar/db/component/ProjectLinkDao.java | 69 +++++ ...ponentLinkDto.java => ProjectLinkDto.java} | 62 +++-- ...LinkMapper.java => ProjectLinkMapper.java} | 14 +- .../org/sonar/db/purge/PurgeCommands.java | 2 +- .../java/org/sonar/db/purge/PurgeMapper.java | 2 +- .../db/component/ComponentLinkMapper.xml | 57 ----- .../sonar/db/component/ProjectLinkMapper.xml | 69 +++++ .../org/sonar/db/purge/PurgeMapper.xml | 4 +- .../src/test/java/org/sonar/db/DbTester.java | 9 +- .../db/component/ComponentLinkDaoTest.java | 137 ---------- .../db/component/ProjectLinkDaoTest.java | 166 ++++++++++++ .../db/component/ProjectLinkDbTester.java | 59 +++++ ...nkDtoTest.java => ProjectLinkDtoTest.java} | 20 +- .../db/component/ProjectLinkTesting.java | 52 ++++ .../component/ComponentLinkDaoTest/delete.xml | 5 - .../component/ComponentLinkDaoTest/empty.xml | 3 - .../ComponentLinkDaoTest/insert-result.xml | 5 - .../component/ComponentLinkDaoTest/shared.xml | 7 - .../ComponentLinkDaoTest/update-result.xml | 5 - .../component/ComponentLinkDaoTest/update.xml | 5 - .../db/migration/sql/RenameTableBuilder.java | 28 +- .../version/v71/CreateTableProjectLinks2.java | 77 ++++++ .../db/migration/version/v71/DbVersion71.java | 5 + .../version/v71/DropTableProjectLinks.java} | 26 +- .../v71/PopulateTableProjectLinks2.java | 112 ++++++++ ...enameTableProjectLinks2ToProjectLinks.java | 56 ++++ .../migration/sql/RenameTableBuilderTest.java | 14 + .../v71/CreateTableProjectLinks2Test.java | 66 +++++ .../version/v71/DbVersion71Test.java | 2 +- .../v71/DropTableProjectLinksTest.java | 54 ++++ .../v71/PopulateTableProjectLinks2Test.java | 209 +++++++++++++++ ...eTableProjectLinks2ToProjectLinksTest.java | 58 +++++ .../CreateTableProjectLinks2Test/empty.sql | 0 .../project_links.sql | 7 + .../project_links2.sql | 64 +++++ .../project_links2.sql | 9 + .../qa/util/pageobjects/ProjectLinksPage.java | 2 +- .../step/PersistProjectLinksStep.java | 115 +++------ .../project/ws/SearchMyProjectsAction.java | 8 +- .../project/ws/SearchMyProjectsData.java | 16 +- .../server/projectlink/ws/CreateAction.java | 27 +- .../server/projectlink/ws/DeleteAction.java | 17 +- .../server/projectlink/ws/ProjectLinksWs.java | 12 + .../server/projectlink/ws/SearchAction.java | 29 +-- ...{list-example.json => search-example.json} | 1 + .../step/PersistProjectLinksStepTest.java | 141 +++++----- .../ws/SearchMyProjectsActionTest.java | 8 +- .../projectlink/ws/CreateActionTest.java | 84 +++--- .../projectlink/ws/DeleteActionTest.java | 152 ++++------- .../projectlink/ws/ProjectLinksWsTest.java | 88 ------- .../projectlink/ws/SearchActionTest.java | 241 +++++++----------- ...add_links_on_project_and_module-result.xml | 11 - .../delete_link.xml | 5 - .../PersistProjectLinksStepTest/empty.xml | 3 - .../not_delete_custom_link.xml | 5 - ...nothing_to_do_when_link_already_exists.xml | 5 - .../update_link-result.xml | 5 - .../update_link.xml | 5 - .../tests/project/ProjectLinksTest.java | 8 +- 64 files changed, 1642 insertions(+), 978 deletions(-) delete mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentLinkDao.java create mode 100644 server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkDao.java rename server/sonar-db-dao/src/main/java/org/sonar/db/component/{ComponentLinkDto.java => ProjectLinkDto.java} (64%) rename server/sonar-db-dao/src/main/java/org/sonar/db/component/{ComponentLinkMapper.java => ProjectLinkMapper.java} (72%) delete mode 100644 server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentLinkMapper.xml create mode 100644 server/sonar-db-dao/src/main/resources/org/sonar/db/component/ProjectLinkMapper.xml delete mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkDaoTest.java create mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDaoTest.java create mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDbTester.java rename server/sonar-db-dao/src/test/java/org/sonar/db/component/{ComponentLinkDtoTest.java => ProjectLinkDtoTest.java} (70%) create mode 100644 server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkTesting.java delete mode 100644 server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/delete.xml delete mode 100644 server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/empty.xml delete mode 100644 server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/insert-result.xml delete mode 100644 server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/shared.xml delete mode 100644 server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/update-result.xml delete mode 100644 server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/update.xml create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2.java rename server/{sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkTesting.java => sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinks.java} (61%) create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2.java create mode 100644 server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinks.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2Test.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinksTest.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2Test.java create mode 100644 server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinksTest.java create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2Test/empty.sql create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinksTest/project_links.sql create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2Test/project_links2.sql create mode 100644 server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinksTest/project_links2.sql rename server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/{list-example.json => search-example.json} (90%) delete mode 100644 server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/add_links_on_project_and_module-result.xml delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/delete_link.xml delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/empty.xml delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/not_delete_custom_link.xml delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/nothing_to_do_when_link_already_exists.xml delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/update_link-result.xml delete mode 100644 server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/update_link.xml diff --git a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl index 5c287cea0ec..c63f060e839 100644 --- a/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl +++ b/server/sonar-db-core/src/main/resources/org/sonar/db/version/schema-h2.ddl @@ -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), diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java index 31a49c77346..ef1d147fe33 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DaoModule.java @@ -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, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java index 3b671e1b682..ee6e3b82eb9 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/DbClient.java @@ -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() { diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java index c14eba4110d..5559074c5c7 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/MyBatis.java @@ -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 index 9a1b058112c..00000000000 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentLinkDao.java +++ /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 selectByComponentUuid(DbSession session, String componentUuid) { - return session.getMapper(ComponentLinkMapper.class).selectByComponentUuid(componentUuid); - } - - public List selectByComponentUuids(DbSession dbSession, List 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/ProjectLinkDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkDao.java new file mode 100644 index 00000000000..d375b1e5368 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkDao.java @@ -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 selectByProjectUuid(DbSession session, String projectUuid) { + return session.getMapper(ProjectLinkMapper.class).selectByProjectUuid(projectUuid); + } + + public List selectByProjectUuids(DbSession dbSession, List 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/ComponentLinkDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkDto.java similarity index 64% rename from server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentLinkDto.java rename to server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkDto.java index 8cd7d0a000b..9930ee9b9d7 100644 --- 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/ProjectLinkDto.java @@ -21,42 +21,53 @@ package org.sonar.db.component; import com.google.common.collect.ImmutableList; import java.util.List; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; -/** - * 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 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 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 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 ComponentLinkDto setName(String name) { + public ProjectLinkDto setName(@Nullable String name) { this.name = name; return this; } - public String getComponentUuid() { - return componentUuid; + public String getProjectUuid() { + return projectUuid; } - public ComponentLinkDto setComponentUuid(String componentUuid) { - this.componentUuid = componentUuid; + public ProjectLinkDto setProjectUuid(String projectUuid) { + this.projectUuid = projectUuid; return this; } @@ -64,30 +75,35 @@ public class ComponentLinkDto { return href; } - public ComponentLinkDto setHref(String href) { + public ProjectLinkDto setHref(String href) { this.href = href; return this; } - public Long getId() { - return id; + public String getType() { + return type; + } + + public ProjectLinkDto setType(String type) { + this.type = type; + return this; } - public String getIdAsString() { - return String.valueOf(id); + public long getCreatedAt() { + return createdAt; } - public ComponentLinkDto setId(Long id) { - this.id = id; + public ProjectLinkDto setCreatedAt(long createdAt) { + this.createdAt = createdAt; return this; } - public String getType() { - return type; + public long getUpdatedAt() { + return updatedAt; } - public ComponentLinkDto setType(String type) { - this.type = type; + public ProjectLinkDto setUpdatedAt(long updatedAt) { + this.updatedAt = updatedAt; 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/ProjectLinkMapper.java similarity index 72% rename from server/sonar-db-dao/src/main/java/org/sonar/db/component/ComponentLinkMapper.java rename to server/sonar-db-dao/src/main/java/org/sonar/db/component/ProjectLinkMapper.java index fe955f73275..9fc084f3e2c 100644 --- 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/ProjectLinkMapper.java @@ -22,17 +22,17 @@ package org.sonar.db.component; import java.util.List; import org.apache.ibatis.annotations.Param; -public interface ComponentLinkMapper { +public interface ProjectLinkMapper { - List selectByComponentUuid(String componentUuid); + List selectByProjectUuid(String projectUuid); - List selectByComponentUuids(@Param("componentUuids") List componentUuids); + List selectByProjectUuids(@Param("projectUuids") List projectUuids); - ComponentLinkDto selectById(@Param("id") long id); + ProjectLinkDto selectByUuid(@Param("uuid") String uuid); - void insert(ComponentLinkDto dto); + void insert(ProjectLinkDto dto); - void update(ComponentLinkDto dto); + void update(ProjectLinkDto dto); - void delete(long id); + void delete(String uuid); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java index edbe6931d79..4e346364ee9 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java @@ -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(); } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java index 2db61704c6d..a9dec7bb5b2 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java @@ -54,7 +54,7 @@ public interface PurgeMapper { void resolveComponentIssuesNotAlreadyResolved(@Param("componentUuids") List componentUuids, @Param("dateAsLong") Long dateAsLong); - void deleteProjectLinksByComponentUuid(@Param("rootUuid") String rootUuid); + void deleteProjectLinksByProjectUuid(@Param("rootUuid") String rootUuid); void deletePropertiesByComponentIds(@Param("componentIds") List 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 index cbc4412649e..00000000000 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ComponentLinkMapper.xml +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - p.id, - p.component_uuid as "componentUuid", - p.link_type as "type", - p.name as name, - p.href as href - - - - - - - - - - INSERT INTO project_links (component_uuid, link_type, name, href) - VALUES (#{componentUuid,jdbcType=VARCHAR}, #{type,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR}, - #{href,jdbcType=VARCHAR}) - - - - 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} - - - - DELETE FROM project_links WHERE id=#{id,jdbcType=BIGINT} - - - - 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 index 00000000000..248b7ae067b --- /dev/null +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/component/ProjectLinkMapper.xml @@ -0,0 +1,69 @@ + + + + + + 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" + + + + + + + + + + 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}) + + + + 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} + + + + DELETE FROM project_links WHERE uuid=#{uuid,jdbcType=VARCHAR} + + + + diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml index 4f652c7cdc8..b8af25a7598 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml @@ -187,10 +187,10 @@ and resolution is null - + delete from project_links where - component_uuid = #{rootUuid,jdbcType=VARCHAR} + project_uuid = #{rootUuid,jdbcType=VARCHAR} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java index 01e64c19a3c..f4d7d81ffd7 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/DbTester.java @@ -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 { 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 { 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 { 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 index 1cb63786c9c..00000000000 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkDaoTest.java +++ /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 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 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 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/ProjectLinkDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDaoTest.java new file mode 100644 index 00000000000..eaba69b0c37 --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDaoTest.java @@ -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 index 00000000000..dcc0d2d5db2 --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDbTester.java @@ -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... dtoPopulators) { + return insertLink(project, newProvidedLinkDto(), dtoPopulators); + } + + @SafeVarargs + public final ProjectLinkDto insertCustomLink(ComponentDto project, Consumer... dtoPopulators) { + return insertLink(project, newCustomLinkDto(), dtoPopulators); + } + + @SafeVarargs + private final ProjectLinkDto insertLink(ComponentDto project, ProjectLinkDto componentLink, Consumer... 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/ComponentLinkDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDtoTest.java similarity index 70% rename from server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkDtoTest.java rename to server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkDtoTest.java index 6e3c8ef5e2f..3301f42d1ff 100644 --- 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/ProjectLinkDtoTest.java @@ -23,26 +23,30 @@ import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; -public class ComponentLinkDtoTest { +public class ProjectLinkDtoTest { @Test public void test_getters_and_setters() { - ComponentLinkDto dto = new ComponentLinkDto() - .setId(1L) - .setComponentUuid("ABCD") + ProjectLinkDto dto = new ProjectLinkDto() + .setUuid("ABCD") + .setProjectUuid("EFGH") .setType("homepage") .setName("Home") - .setHref("http://www.sonarqube.org"); + .setHref("http://www.sonarqube.org") + .setCreatedAt(1_000_000_000L) + .setUpdatedAt(5_000_000_000L); - assertThat(dto.getId()).isEqualTo(1L); - assertThat(dto.getComponentUuid()).isEqualTo("ABCD"); + 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(ComponentLinkDto.PROVIDED_TYPES).hasSize(5); + 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 index 00000000000..f79d90aa655 --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/component/ProjectLinkTesting.java @@ -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 index 8b89e7223ec..00000000000 --- a/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/delete.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - 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 index 871dedcb5e9..00000000000 --- a/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/empty.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - 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 index 8b89e7223ec..00000000000 --- a/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/insert-result.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - 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 index 33e9449d1dd..00000000000 --- a/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/shared.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - 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 index 8b89e7223ec..00000000000 --- a/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/update-result.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - 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 index 6b80fc9d01c..00000000000 --- a/server/sonar-db-dao/src/test/resources/org/sonar/db/component/ComponentLinkDaoTest/update.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/RenameTableBuilder.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/RenameTableBuilder.java index f5adc72ba9a..ef381ab3f82 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/RenameTableBuilder.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/sql/RenameTableBuilder.java @@ -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 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 index 00000000000..d1be6af38fc --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2.java @@ -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()); + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71.java index a1cdf16d6e3..1e18627e81c 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71.java @@ -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-dao/src/test/java/org/sonar/db/component/ComponentLinkTesting.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinks.java similarity index 61% rename from server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkTesting.java rename to server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinks.java index 6eea21b6867..ca2950b8bdb 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/component/ComponentLinkTesting.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinks.java @@ -17,20 +17,24 @@ * 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; +package org.sonar.server.platform.db.migration.version.v71; -import org.sonar.core.util.Uuids; +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; -import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; -import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; +public class DropTableProjectLinks extends DdlChange { -public class ComponentLinkTesting { - public static ComponentLinkDto newComponentLinkDto() { - return new ComponentLinkDto() - .setComponentUuid(Uuids.createFast()) - .setHref(randomAlphanumeric(128)) - .setName(randomAlphabetic(128)) - .setType(ComponentLinkDto.TYPE_SOURCES); + 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 index 00000000000..a42f8694604 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2.java @@ -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 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 index 00000000000..dd9f12c647d --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinks.java @@ -0,0 +1,56 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.db.migration.version.v71; + +import java.sql.SQLException; +import org.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()); + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/sql/RenameTableBuilderTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/sql/RenameTableBuilderTest.java index 1f32d803d9f..cfe6cffad87 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/sql/RenameTableBuilderTest.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/sql/RenameTableBuilderTest.java @@ -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 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 index 00000000000..ff65f3fcf36 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/CreateTableProjectLinks2Test.java @@ -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 diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71Test.java index 15fee0317ac..a93444604fd 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71Test.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DbVersion71Test.java @@ -36,7 +36,7 @@ public class DbVersion71Test { @Test public void verify_migration_count() { - verifyMigrationCount(underTest, 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 index 00000000000..09e78e36aa1 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinksTest.java @@ -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 index 00000000000..487d57fac52 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2Test.java @@ -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 index 00000000000..0440fc4e00d --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinksTest.java @@ -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 index 00000000000..e69de29bb2d 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 index 00000000000..9588643a463 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/DropTableProjectLinksTest/project_links.sql @@ -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 index 00000000000..ef474cc6421 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/PopulateTableProjectLinks2Test/project_links2.sql @@ -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 index 00000000000..c29900dbd64 --- /dev/null +++ b/server/sonar-db-migration/src/test/resources/org/sonar/server/platform/db/migration/version/v71/RenameTableProjectLinks2ToProjectLinksTest/project_links2.sql @@ -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 +); diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectLinksPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectLinksPage.java index f114bce59ef..fd8bb42438f 100644 --- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectLinksPage.java +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectLinksPage.java @@ -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 getLinksAsItems() { diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStep.java b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStep.java index eb5b14a3d3e..c255ba1189f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStep.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStep.java @@ -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 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 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 links) { - List previousLinks = dbClient.componentLinkDao().selectByComponentUuid(session, componentUuid); - mergeLinks(session, componentUuid, links, previousLinks); - } - - private void mergeLinks(DbSession session, String componentUuid, List links, List previousLinks) { - Set linkType = new HashSet<>(); - for (final ScannerReport.ComponentLink link : links) { + private void mergeLinks(DbSession session, String componentUuid, List links, List previousLinks) { + Set 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 previousLink = previousLinks.stream() + Optional 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 diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java index 13a77a87161..1ec0e29c2a1 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsAction.java @@ -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 { + private enum ProjectLinkDtoToWs implements Function { 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 projects = searchResult.projects; List projectUuids = Lists.transform(projects, ComponentDto::projectUuid); - List projectLinks = dbClient.componentLinkDao().selectByComponentUuids(dbSession, projectUuids); + List projectLinks = dbClient.projectLinkDao().selectByProjectUuids(dbSession, projectUuids); List snapshots = dbClient.snapshotDao().selectLastAnalysesByRootComponentUuids(dbSession, projectUuids); List qualityGates = dbClient.liveMeasureDao() .selectByComponentUuidsAndMetricKeys(dbSession, projectUuids, singletonList(CoreMetrics.ALERT_STATUS_KEY)); diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsData.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsData.java index 21361d0998b..29f9069eedb 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsData.java +++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/SearchMyProjectsData.java @@ -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 projects; - private final ListMultimap projectLinksByProjectUuid; + private final ListMultimap projectLinksByProjectUuid; private final Map lastAnalysisDates; private final Map qualityGateStatuses; private final int totalNbOfProject; @@ -58,7 +58,7 @@ class SearchMyProjectsData { return projects; } - List projectLinksFor(String projectUuid) { + List projectLinksFor(String projectUuid) { return projectLinksByProjectUuid.get(projectUuid); } @@ -74,9 +74,9 @@ class SearchMyProjectsData { return totalNbOfProject; } - private static ListMultimap buildProjectLinks(List dtos) { - ImmutableListMultimap.Builder projectLinks = ImmutableListMultimap.builder(); - dtos.forEach(projectLink -> projectLinks.put(projectLink.getComponentUuid(), projectLink)); + private static ListMultimap buildProjectLinks(List dtos) { + ImmutableListMultimap.Builder 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 projects; - private List projectLinks; + private List projectLinks; private List snapshots; private List qualityGates; private Integer totalNbOfProjects; @@ -107,7 +107,7 @@ class SearchMyProjectsData { return this; } - public Builder setProjectLinks(List projectLinks) { + public Builder setProjectLinks(List projectLinks) { this.projectLinks = projectLinks; return this; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/CreateAction.java index a4c810cbfaa..2730840dde5 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/CreateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/CreateAction.java @@ -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(), diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/DeleteAction.java index 5dfaeb57bfb..bcba6beb4ed 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/DeleteAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/DeleteAction.java @@ -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()); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksWs.java b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksWs.java index bcf5ccc4c80..eb2b7fe0add 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksWs.java +++ b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/ProjectLinksWs.java @@ -19,7 +19,13 @@ */ 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; + } + } diff --git a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/SearchAction.java index 5de432d37aa..2548fd3876f 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/projectlink/ws/SearchAction.java @@ -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 { "", 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 links = dbClient.componentLinkDao() - .selectByComponentUuid(dbSession, component.uuid()); + List links = dbClient.projectLinkDao() + .selectByProjectUuid(dbSession, component.uuid()); return buildResponse(links); } } - private static SearchWsResponse buildResponse(List links) { + private static SearchWsResponse buildResponse(List 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/search-example.json similarity index 90% rename from server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/list-example.json rename to server/sonar-server/src/main/resources/org/sonar/server/projectlink/ws/search-example.json index 8638919d9a0..5f0afb8c2db 100644 --- 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/search-example.json @@ -9,6 +9,7 @@ { "id": "2", "name": "Custom", + "type": "custom", "url": "http://example.org/custom" } ] diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest.java index 9b13a24ea77..b13cc434b1a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest.java @@ -19,32 +19,37 @@ */ 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(); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java index e9551d7315c..bcb8a89785b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/SearchMyProjectsActionTest.java @@ -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)); diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java index c97eaedbb19..ed689e9649b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/CreateActionTest.java @@ -19,23 +19,22 @@ */ 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"); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/DeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/DeleteActionTest.java index 6b990660466..eee39643a1b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/DeleteActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/DeleteActionTest.java @@ -19,18 +19,16 @@ */ 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 index 38bc02d6ca4..00000000000 --- a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/ProjectLinksWsTest.java +++ /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); - } -} diff --git a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/SearchActionTest.java index 6d288e3b487..8974e886bb5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/projectlink/ws/SearchActionTest.java @@ -19,23 +19,19 @@ */ 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 index 008f804103e..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/add_links_on_project_and_module-result.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - 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 index 8b89e7223ec..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/delete_link.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - 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 index 871dedcb5e9..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/empty.xml +++ /dev/null @@ -1,3 +0,0 @@ - - - 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 index 78c6c1dd41a..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/not_delete_custom_link.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - 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 index 8b89e7223ec..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/nothing_to_do_when_link_already_exists.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - 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 index 8b89e7223ec..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/update_link-result.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - 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 index d40281088f3..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/computation/task/projectanalysis/step/PersistProjectLinksStepTest/update_link.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectLinksTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectLinksTest.java index a2a63ee87c9..1cbb95dd0e0 100644 --- a/tests/src/test/java/org/sonarqube/tests/project/ProjectLinksTest.java +++ b/tests/src/test/java/org/sonarqube/tests/project/ProjectLinksTest.java @@ -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 } -- 2.39.5