diff options
44 files changed, 1101 insertions, 130 deletions
diff --git a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java index abc064e3142..c4e1ff35582 100644 --- a/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java +++ b/server/sonar-ce/src/test/java/org/sonar/ce/container/ComputeEngineContainerImplTest.java @@ -108,7 +108,7 @@ public class ComputeEngineContainerImplTest { + 3 // CeCleaningModule + its content + 4 // WebhookModule + 1 // CeDistributedInformation - ); + ); assertThat(picoContainer.getParent().getComponentAdapters()).hasSize( CONTAINER_ITSELF + 8 // level 3 @@ -121,7 +121,7 @@ public class ComputeEngineContainerImplTest { assertThat(picoContainer.getParent().getParent().getParent().getComponentAdapters()).hasSize( COMPONENTS_IN_LEVEL_1_AT_CONSTRUCTION + 26 // level 1 - + 57 // content of DaoModule + + 58 // content of DaoModule + 3 // content of EsModule + 54 // content of CorePropertyDefinitions + 1 // StopFlagContainer diff --git a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java index 085d7de5625..0e699cba125 100644 --- a/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java +++ b/server/sonar-db-core/src/main/java/org/sonar/db/version/SqTables.java @@ -77,6 +77,7 @@ public final class SqTables { "metrics", "notifications", "organizations", + "organization_alm_bindings", "organization_members", "org_qprofiles", "org_quality_gates", 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 a1b83a1315d..cf18feb0316 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 @@ -902,3 +902,16 @@ CREATE TABLE "PROJECT_MAPPINGS" ( ); CREATE UNIQUE INDEX "KEY_TYPE_KEE" ON "PROJECT_MAPPINGS" ("KEY_TYPE", "KEE"); CREATE INDEX "PROJECT_UUID" ON "PROJECT_MAPPINGS" ("PROJECT_UUID"); + +CREATE TABLE "ORGANIZATION_ALM_BINDINGS" ( + "UUID" VARCHAR(40) NOT NULL, + "ORGANIZATION_UUID" VARCHAR(40) NOT NULL, + "ALM_APP_INSTALL_UUID" VARCHAR(40) NOT NULL, + "ALM_ID" VARCHAR(40) NOT NULL, + "URL" VARCHAR(2000) NOT NULL, + "USER_UUID" VARCHAR(255) NOT NULL, + "CREATED_AT" BIGINT NOT NULL, + CONSTRAINT "PK_ORGANIZATION_ALM_BINDINGS" PRIMARY KEY ("UUID") +); +CREATE UNIQUE INDEX "ORG_ALM_BINDINGS_ORG" ON "ORGANIZATION_ALM_BINDINGS" ("ORGANIZATION_UUID"); +CREATE UNIQUE INDEX "ORG_ALM_BINDINGS_INSTALL" ON "ORGANIZATION_ALM_BINDINGS" ("ALM_APP_INSTALL_UUID"); 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 fbc228cdc35..bdc4097099a 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 @@ -23,7 +23,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import org.sonar.core.platform.Module; -import org.sonar.db.alm.ProjectAlmBindingsDao; +import org.sonar.db.alm.AlmAppInstallDao; +import org.sonar.db.alm.OrganizationAlmBindingDao; +import org.sonar.db.alm.ProjectAlmBindingDao; import org.sonar.db.ce.CeActivityDao; import org.sonar.db.ce.CeQueueDao; import org.sonar.db.ce.CeScannerContextDao; @@ -41,7 +43,6 @@ import org.sonar.db.es.EsQueueDao; import org.sonar.db.event.EventDao; import org.sonar.db.issue.IssueChangeDao; import org.sonar.db.issue.IssueDao; -import org.sonar.db.alm.AlmAppInstallDao; import org.sonar.db.mapping.ProjectMappingsDao; import org.sonar.db.measure.LiveMeasureDao; import org.sonar.db.measure.MeasureDao; @@ -108,7 +109,7 @@ public class DaoModule extends Module { GroupMembershipDao.class, GroupPermissionDao.class, AlmAppInstallDao.class, - ProjectAlmBindingsDao.class, + ProjectAlmBindingDao.class, InternalPropertiesDao.class, IssueChangeDao.class, IssueDao.class, @@ -116,6 +117,7 @@ public class DaoModule extends Module { MeasureDao.class, MetricDao.class, NotificationQueueDao.class, + OrganizationAlmBindingDao.class, OrganizationDao.class, OrganizationMemberDao.class, PermissionTemplateCharacteristicDao.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 edc6731c31b..8ba7cf8362f 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 @@ -21,7 +21,9 @@ package org.sonar.db; import java.util.IdentityHashMap; import java.util.Map; -import org.sonar.db.alm.ProjectAlmBindingsDao; +import org.sonar.db.alm.AlmAppInstallDao; +import org.sonar.db.alm.OrganizationAlmBindingDao; +import org.sonar.db.alm.ProjectAlmBindingDao; import org.sonar.db.ce.CeActivityDao; import org.sonar.db.ce.CeQueueDao; import org.sonar.db.ce.CeScannerContextDao; @@ -39,7 +41,6 @@ import org.sonar.db.es.EsQueueDao; import org.sonar.db.event.EventDao; import org.sonar.db.issue.IssueChangeDao; import org.sonar.db.issue.IssueDao; -import org.sonar.db.alm.AlmAppInstallDao; import org.sonar.db.mapping.ProjectMappingsDao; import org.sonar.db.measure.LiveMeasureDao; import org.sonar.db.measure.MeasureDao; @@ -92,7 +93,7 @@ public class DbClient { private final QualityProfileDao qualityProfileDao; private final PropertiesDao propertiesDao; private final AlmAppInstallDao almAppInstallDao; - private final ProjectAlmBindingsDao projectAlmBindingsDao; + private final ProjectAlmBindingDao projectAlmBindingDao; private final InternalPropertiesDao internalPropertiesDao; private final SnapshotDao snapshotDao; private final ComponentDao componentDao; @@ -142,6 +143,7 @@ public class DbClient { private final WebhookDao webhookDao; private final WebhookDeliveryDao webhookDeliveryDao; private final ProjectMappingsDao projectMappingsDao; + private final OrganizationAlmBindingDao organizationAlmBindingDao; public DbClient(Database database, MyBatis myBatis, DBSessions dbSessions, Dao... daos) { this.database = database; @@ -153,7 +155,7 @@ public class DbClient { map.put(dao.getClass(), dao); } almAppInstallDao = getDao(map, AlmAppInstallDao.class); - projectAlmBindingsDao = getDao(map, ProjectAlmBindingsDao.class); + projectAlmBindingDao = getDao(map, ProjectAlmBindingDao.class); schemaMigrationDao = getDao(map, SchemaMigrationDao.class); authorizationDao = getDao(map, AuthorizationDao.class); organizationDao = getDao(map, OrganizationDao.class); @@ -209,6 +211,7 @@ public class DbClient { webhookDao = getDao(map, WebhookDao.class); webhookDeliveryDao = getDao(map, WebhookDeliveryDao.class); projectMappingsDao = getDao(map, ProjectMappingsDao.class); + organizationAlmBindingDao = getDao(map, OrganizationAlmBindingDao.class); } public DbSession openSession(boolean batch) { @@ -223,8 +226,8 @@ public class DbClient { return almAppInstallDao; } - public ProjectAlmBindingsDao projectAlmBindingsDao() { - return projectAlmBindingsDao; + public ProjectAlmBindingDao projectAlmBindingsDao() { + return projectAlmBindingDao; } public SchemaMigrationDao schemaMigrationDao() { @@ -456,4 +459,8 @@ public class DbClient { public ProjectMappingsDao projectMappingsDao() { return projectMappingsDao; } + + public OrganizationAlmBindingDao organizationAlmBindingDao() { + return organizationAlmBindingDao; + } } 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 93af5cc3914..846ca3724e2 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 @@ -31,8 +31,9 @@ import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.apache.ibatis.session.TransactionIsolationLevel; import org.sonar.api.Startable; import org.sonar.db.alm.AlmAppInstallMapper; +import org.sonar.db.alm.OrganizationAlmBindingMapper; import org.sonar.db.alm.ProjectAlmBindingDto; -import org.sonar.db.alm.ProjectAlmBindingsMapper; +import org.sonar.db.alm.ProjectAlmBindingMapper; import org.sonar.db.ce.CeActivityMapper; import org.sonar.db.ce.CeQueueMapper; import org.sonar.db.ce.CeScannerContextMapper; @@ -232,12 +233,13 @@ public class MyBatis implements Startable { MeasureMapper.class, MetricMapper.class, NotificationQueueMapper.class, + OrganizationAlmBindingMapper.class, OrganizationMapper.class, OrganizationMemberMapper.class, PermissionTemplateCharacteristicMapper.class, PermissionTemplateMapper.class, PluginMapper.class, - ProjectAlmBindingsMapper.class, + ProjectAlmBindingMapper.class, ProjectLinkMapper.class, ProjectMappingsMapper.class, ProjectQgateAssociationMapper.class, diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallDao.java index 04302fe6f06..005017eefd8 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallDao.java @@ -29,6 +29,7 @@ import org.sonar.db.Dao; import org.sonar.db.DbSession; import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; import static org.apache.commons.lang.StringUtils.isNotEmpty; /** @@ -44,17 +45,17 @@ public class AlmAppInstallDao implements Dao { this.uuidFactory = uuidFactory; } - public Optional<AlmAppInstallDto> selectByOwner(DbSession dbSession, ALM alm, String ownerId) { + public Optional<AlmAppInstallDto> selectByOwnerId(DbSession dbSession, ALM alm, String ownerId) { checkAlm(alm); checkOwnerId(ownerId); AlmAppInstallMapper mapper = getMapper(dbSession); - return Optional.ofNullable(mapper.selectByOwner(alm.getId(), ownerId)); + return Optional.ofNullable(mapper.selectByOwnerId(alm.getId(), ownerId)); } - public Optional<String> getOwerId(DbSession dbSession, ALM alm, String installationId) { + public Optional<AlmAppInstallDto> selectByInstallationId(DbSession dbSession, ALM alm, String installationId) { AlmAppInstallMapper mapper = getMapper(dbSession); - return Optional.ofNullable(mapper.selectOwnerId(alm.getId(), installationId)); + return Optional.ofNullable(mapper.selectByInstallationId(alm.getId(), installationId)); } public List<AlmAppInstallDto> findAllWithNoOwnerType(DbSession dbSession) { @@ -88,7 +89,7 @@ public class AlmAppInstallDao implements Dao { } private static void checkAlm(@Nullable ALM alm) { - Objects.requireNonNull(alm, "alm can't be null"); + requireNonNull(alm, "alm can't be null"); } private static void checkOwnerId(@Nullable String ownerId) { diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallMapper.java index cb979c2217f..f840a5aa07b 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmAppInstallMapper.java @@ -27,10 +27,10 @@ import org.apache.ibatis.annotations.Param; public interface AlmAppInstallMapper { @CheckForNull - AlmAppInstallDto selectByOwner(@Param("almId") String almId, @Param("ownerId") String ownerId); + AlmAppInstallDto selectByOwnerId(@Param("almId") String almId, @Param("ownerId") String ownerId); @CheckForNull - String selectOwnerId(@Param("almId") String almId, @Param("installId") String installId); + AlmAppInstallDto selectByInstallationId(@Param("almId") String almId, @Param("installId") String installId); List<AlmAppInstallDto> selectAllWithNoOwnerType(); diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmTesting.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmTesting.java new file mode 100644 index 00000000000..656be2fe655 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/AlmTesting.java @@ -0,0 +1,29 @@ +/* + * 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.alm; + +public class AlmTesting { + + private AlmTesting() { + // only statics + } + + +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/OrganizationAlmBindingDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/OrganizationAlmBindingDao.java new file mode 100644 index 00000000000..3612870461b --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/OrganizationAlmBindingDao.java @@ -0,0 +1,76 @@ +/* + * 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.alm; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import org.sonar.api.utils.System2; +import org.sonar.core.util.UuidFactory; +import org.sonar.core.util.stream.MoreCollectors; +import org.sonar.db.Dao; +import org.sonar.db.DbSession; +import org.sonar.db.organization.OrganizationDto; + +import static org.sonar.db.DatabaseUtils.executeLargeInputs; + +public class OrganizationAlmBindingDao implements Dao { + + private final System2 system2; + private final UuidFactory uuidFactory; + + public OrganizationAlmBindingDao(System2 system2, UuidFactory uuidFactory) { + this.system2 = system2; + this.uuidFactory = uuidFactory; + } + + public Optional<OrganizationAlmBindingDto> selectByOrganization(DbSession dbSession, OrganizationDto organization) { + return Optional.ofNullable(getMapper(dbSession).selectByOrganizationUuid(organization.getUuid())); + } + + public List<OrganizationAlmBindingDto> selectByOrganizations(DbSession dbSession, Collection<OrganizationDto> organizations) { + return executeLargeInputs(organizations.stream().map(OrganizationDto::getUuid).collect(MoreCollectors.toSet()), + organizationUuids -> getMapper(dbSession).selectByOrganizationUuids(organizationUuids)); + } + + public void insert(DbSession dbSession, OrganizationDto organization, AlmAppInstallDto almAppInstall, String url, String userUuid) { + long now = system2.now(); + getMapper(dbSession).insert(new OrganizationAlmBindingDto() + .setUuid(uuidFactory.create()) + .setOrganizationUuid(organization.getUuid()) + .setAlmAppInstallUuid(almAppInstall.getUuid()) + .setAlmId(almAppInstall.getAlm()) + .setUrl(url) + .setUserUuid(userUuid) + .setCreatedAt(now)); + } + + public void deleteByOrganization(DbSession dbSession, OrganizationDto organization) { + getMapper(dbSession).deleteByOrganizationUuid(organization.getUuid()); + } + + public void deleteByAlmAppInstall(DbSession dbSession, AlmAppInstallDto almAppInstall) { + getMapper(dbSession).deleteByAlmAppInstallUuid(almAppInstall.getUuid()); + } + + private static OrganizationAlmBindingMapper getMapper(DbSession dbSession) { + return dbSession.getMapper(OrganizationAlmBindingMapper.class); + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/OrganizationAlmBindingDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/OrganizationAlmBindingDto.java new file mode 100644 index 00000000000..30a2795a280 --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/OrganizationAlmBindingDto.java @@ -0,0 +1,125 @@ +/* + * 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.alm; + +import java.util.Arrays; + +/** + * This DTO is used to link an {@link org.sonar.db.organization.OrganizationDto} to a {@link AlmAppInstallDto} + */ +public class OrganizationAlmBindingDto { + + /** + * Not empty. Max size is 40. Obviously it is unique. + */ + private String uuid; + /** + * The UUID of the organization. Can't be null. Max size is 40. + * It's unique, as an organization is only linked to one installation (at least for the moment). + */ + private String organizationUuid; + /** + * The UUID of ALM installation. Can't be null. Max size is 40. + * It's unique, as an installation is related to only one organization. + */ + private String almAppInstallUuid; + /** + * The id of the ALM. Can't be null. Max size is 40. + */ + private String rawAlmId; + /** + * The url of the ALM organization. Can't be null. Max size is 2000. + */ + private String url; + /** + * The UUID of the user who has created the link between the organization and the ALN installation. Can't be null. Max size is 255. + */ + private String userUuid; + /** + * Technical creation date + */ + private long createdAt; + + public String getUuid() { + return uuid; + } + + OrganizationAlmBindingDto setUuid(String uuid) { + this.uuid = uuid; + return this; + } + + public String getOrganizationUuid() { + return organizationUuid; + } + + public OrganizationAlmBindingDto setOrganizationUuid(String organizationUuid) { + this.organizationUuid = organizationUuid; + return this; + } + + public String getAlmAppInstallUuid() { + return almAppInstallUuid; + } + + public OrganizationAlmBindingDto setAlmAppInstallUuid(String almAppInstallUuid) { + this.almAppInstallUuid = almAppInstallUuid; + return this; + } + + public ALM getAlm() { + return Arrays.stream(ALM.values()) + .filter(a -> a.getId().equals(rawAlmId)) + .findAny() + .orElseThrow(() -> new IllegalStateException("ALM id " + rawAlmId + " is invalid")); + } + + public OrganizationAlmBindingDto setAlmId(ALM alm) { + this.rawAlmId = alm.getId(); + return this; + } + + public String getUrl() { + return url; + } + + public OrganizationAlmBindingDto setUrl(String url) { + this.url = url; + return this; + } + + public String getUserUuid() { + return userUuid; + } + + public OrganizationAlmBindingDto setUserUuid(String userUuid) { + this.userUuid = userUuid; + return this; + } + + public long getCreatedAt() { + return createdAt; + } + + OrganizationAlmBindingDto setCreatedAt(long createdAt) { + this.createdAt = createdAt; + return this; + } +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/OrganizationAlmBindingMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/OrganizationAlmBindingMapper.java new file mode 100644 index 00000000000..f664a50f51b --- /dev/null +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/OrganizationAlmBindingMapper.java @@ -0,0 +1,39 @@ +/* + * 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.alm; + +import java.util.Collection; +import java.util.List; +import javax.annotation.CheckForNull; +import org.apache.ibatis.annotations.Param; + +public interface OrganizationAlmBindingMapper { + + @CheckForNull + OrganizationAlmBindingDto selectByOrganizationUuid(@Param("organizationUuid") String organizationUuid); + + List<OrganizationAlmBindingDto> selectByOrganizationUuids(@Param("organizationUuids") Collection<String> organizationUuids); + + void insert(@Param("dto") OrganizationAlmBindingDto dto); + + void deleteByOrganizationUuid(@Param("organizationUuid") String organizationUuid); + + void deleteByAlmAppInstallUuid(@Param("almAppInstallUuid") String almAppInstallUuid); +} diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/ProjectAlmBindingsDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/ProjectAlmBindingDao.java index 61b9bc5d56e..f2dc943914e 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/ProjectAlmBindingsDao.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/ProjectAlmBindingDao.java @@ -33,12 +33,12 @@ import static com.google.common.base.Preconditions.checkArgument; import static org.apache.commons.lang.StringUtils.isNotEmpty; import static org.sonar.db.DatabaseUtils.executeLargeInputs; -public class ProjectAlmBindingsDao implements Dao { +public class ProjectAlmBindingDao implements Dao { private final System2 system2; private final UuidFactory uuidFactory; - public ProjectAlmBindingsDao(System2 system2, UuidFactory uuidFactory) { + public ProjectAlmBindingDao(System2 system2, UuidFactory uuidFactory) { this.system2 = system2; this.uuidFactory = uuidFactory; } @@ -49,7 +49,7 @@ public class ProjectAlmBindingsDao implements Dao { checkArgument(isNotEmpty(projectUuid), "projectUuid can't be null nor empty"); checkArgument(isNotEmpty(url), "url can't be null nor empty"); - ProjectAlmBindingsMapper mapper = getMapper(dbSession); + ProjectAlmBindingMapper mapper = getMapper(dbSession); long now = system2.now(); if (mapper.update(alm.getId(), repoId, projectUuid, githubSlug, url, now) == 0) { @@ -78,7 +78,7 @@ public class ProjectAlmBindingsDao implements Dao { checkAlm(alm); checkRepoId(repoId); - ProjectAlmBindingsMapper mapper = getMapper(dbSession); + ProjectAlmBindingMapper mapper = getMapper(dbSession); return Optional.ofNullable(mapper.selectProjectKey(alm.getId(), repoId)); } @@ -90,8 +90,8 @@ public class ProjectAlmBindingsDao implements Dao { checkArgument(isNotEmpty(repoId), "repoId can't be null nor empty"); } - private static ProjectAlmBindingsMapper getMapper(DbSession dbSession) { - return dbSession.getMapper(ProjectAlmBindingsMapper.class); + private static ProjectAlmBindingMapper getMapper(DbSession dbSession) { + return dbSession.getMapper(ProjectAlmBindingMapper.class); } } diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/ProjectAlmBindingsMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/ProjectAlmBindingMapper.java index df0484e5780..1849cf1c177 100644 --- a/server/sonar-db-dao/src/main/java/org/sonar/db/alm/ProjectAlmBindingsMapper.java +++ b/server/sonar-db-dao/src/main/java/org/sonar/db/alm/ProjectAlmBindingMapper.java @@ -24,7 +24,7 @@ import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.apache.ibatis.annotations.Param; -public interface ProjectAlmBindingsMapper { +public interface ProjectAlmBindingMapper { int bindingCount(@Param("almId") String almId, @Param("repoId") String repoId); diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/AlmAppInstallMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/AlmAppInstallMapper.xml index d96a33dfe13..2fe08a4dd5e 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/AlmAppInstallMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/AlmAppInstallMapper.xml @@ -13,7 +13,7 @@ updated_at as updatedAt </sql> - <select id="selectByOwner" parameterType="Map" resultType="org.sonar.db.alm.AlmAppInstallDto"> + <select id="selectByOwnerId" parameterType="Map" resultType="org.sonar.db.alm.AlmAppInstallDto"> select <include refid="sqlColumns" /> from alm_app_installs @@ -22,14 +22,14 @@ and owner_id = #{ownerId, jdbcType=VARCHAR} </select> - <select id="selectOwnerId" parameterType="Map" resultType="String"> + <select id="selectByInstallationId" parameterType="Map" resultType="org.sonar.db.alm.AlmAppInstallDto"> select - owner_id as ownerId + <include refid="sqlColumns"/> from - alm_app_installs + alm_app_installs where - alm_id = #{almId, jdbcType=VARCHAR} - and install_id = #{installId, jdbcType=VARCHAR} + alm_id = #{almId, jdbcType=VARCHAR} + and install_id = #{installId, jdbcType=VARCHAR} </select> <select id="selectAllWithNoOwnerType" parameterType="Map" resultType="org.sonar.db.alm.AlmAppInstallDto"> diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/OrganizationAlmBindingMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/OrganizationAlmBindingMapper.xml new file mode 100644 index 00000000000..395eb9100e5 --- /dev/null +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/OrganizationAlmBindingMapper.xml @@ -0,0 +1,67 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd"> + +<mapper namespace="org.sonar.db.alm.OrganizationAlmBindingMapper"> + + <sql id="columns"> + uuid, + organization_uuid as organizationUuid, + alm_app_install_uuid as almAppInstallUuid, + alm_id as rawAlmId, + url, + user_uuid as userUuid, + created_at as createdAt + </sql> + + <select id="selectByOrganizationUuid" parameterType="String" resultType="org.sonar.db.alm.OrganizationAlmBindingDto"> + select + <include refid="columns"/> + from + organization_alm_bindings + where + organization_uuid = #{organizationUuid, jdbcType=VARCHAR} + </select> + + <select id="selectByOrganizationUuids" parameterType="String" resultType="org.sonar.db.alm.OrganizationAlmBindingDto"> + select + <include refid="columns"/> + from + organization_alm_bindings + where + organization_uuid in + <foreach collection="organizationUuids" open="(" close=")" item="organizationUuid" separator=","> + #{organizationUuid , jdbcType=VARCHAR} + </foreach> + </select> + + <insert id="insert" parameterType="Map" useGeneratedKeys="false"> + INSERT INTO organization_alm_bindings + ( + uuid, + organization_uuid, + alm_app_install_uuid, + alm_id, + url, + user_uuid, + created_at + ) + VALUES ( + #{dto.uuid, jdbcType=VARCHAR}, + #{dto.organizationUuid, jdbcType=VARCHAR}, + #{dto.almAppInstallUuid, jdbcType=VARCHAR}, + #{dto.rawAlmId, jdbcType=VARCHAR}, + #{dto.url, jdbcType=VARCHAR}, + #{dto.userUuid, jdbcType=VARCHAR}, + #{dto.createdAt, jdbcType=BIGINT} + ) + </insert> + + <delete id="deleteByOrganizationUuid" parameterType="String"> + DELETE FROM organization_alm_bindings WHERE organization_uuid = #{organizationUuid, jdbcType=VARCHAR} + </delete> + + <delete id="deleteByAlmAppInstallUuid" parameterType="String"> + DELETE FROM organization_alm_bindings WHERE alm_app_install_uuid = #{almAppInstallUuid, jdbcType=VARCHAR} + </delete> + +</mapper> diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/ProjectAlmBindingsMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/ProjectAlmBindingMapper.xml index 27f5d1f720a..845af9e7d2e 100644 --- a/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/ProjectAlmBindingsMapper.xml +++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/alm/ProjectAlmBindingMapper.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "mybatis-3-mapper.dtd"> -<mapper namespace="org.sonar.db.alm.ProjectAlmBindingsMapper"> +<mapper namespace="org.sonar.db.alm.ProjectAlmBindingMapper"> <sql id="columns"> uuid, diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java index 307a4db0861..116af4636e4 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/DaoModuleTest.java @@ -30,6 +30,6 @@ public class DaoModuleTest { public void verify_count_of_added_components() { ComponentContainer container = new ComponentContainer(); new DaoModule().configure(container); - assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 57); + assertThat(container.size()).isEqualTo(COMPONENTS_IN_EMPTY_COMPONENT_CONTAINER + 58); } } 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 15f11f5d2d0..82194a971a4 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 @@ -29,6 +29,7 @@ import org.apache.commons.lang.StringUtils; import org.picocontainer.containers.TransientPicoContainer; import org.sonar.api.utils.System2; import org.sonar.core.util.SequenceUuidFactory; +import org.sonar.db.alm.AlmDbTester; import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ProjectLinkDbTester; import org.sonar.db.event.EventDbTester; @@ -87,6 +88,7 @@ public class DbTester extends AbstractDbTester<TestDb> { private final PluginDbTester pluginDbTester; private final WebhookDbTester webhookDbTester; private final WebhookDeliveryDbTester webhookDeliveryDbTester; + private final AlmDbTester almDbTester; public DbTester(System2 system2, @Nullable String schemaPath) { super(TestDb.create(schemaPath)); @@ -112,6 +114,7 @@ public class DbTester extends AbstractDbTester<TestDb> { this.pluginDbTester = new PluginDbTester(this); this.webhookDbTester = new WebhookDbTester(this); this.webhookDeliveryDbTester = new WebhookDeliveryDbTester(this); + this.almDbTester = new AlmDbTester(this); } public static DbTester create() { @@ -262,6 +265,10 @@ public class DbTester extends AbstractDbTester<TestDb> { return webhookDeliveryDbTester; } + public AlmDbTester alm() { + return almDbTester; + } + @Override protected void after() { if (session != null) { diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmAppInstallDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmAppInstallDaoTest.java index 00955915f03..ddc76bdf316 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmAppInstallDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmAppInstallDaoTest.java @@ -60,47 +60,54 @@ public class AlmAppInstallDaoTest { private AlmAppInstallDao underTest = new AlmAppInstallDao(system2, uuidFactory); @Test - public void selectByOwner() { + public void selectByOwnerId() { when(uuidFactory.create()).thenReturn(A_UUID); when(system2.now()).thenReturn(DATE); underTest.insertOrUpdate(dbSession, GITHUB, A_OWNER, true, AN_INSTALL); - assertThat(underTest.selectByOwner(dbSession, GITHUB, A_OWNER).get()) - .extracting(AlmAppInstallDto::getUuid, AlmAppInstallDto::getInstallId, AlmAppInstallDto::getOwnerId, AlmAppInstallDto::getAlm, AlmAppInstallDto::getCreatedAt, AlmAppInstallDto::getUpdatedAt) - .containsExactlyInAnyOrder(A_UUID, AN_INSTALL, A_OWNER, ALM.GITHUB, DATE, DATE); - assertThat(underTest.selectByOwner(dbSession, GITHUB, "unknown")).isEmpty(); - assertThat(underTest.selectByOwner(dbSession, BITBUCKETCLOUD, A_OWNER)).isEmpty(); + assertThat(underTest.selectByOwnerId(dbSession, GITHUB, A_OWNER).get()) + .extracting(AlmAppInstallDto::getUuid, AlmAppInstallDto::getAlm, AlmAppInstallDto::getInstallId, AlmAppInstallDto::getOwnerId, + AlmAppInstallDto::getCreatedAt, AlmAppInstallDto::getUpdatedAt) + .contains(A_UUID, GITHUB, A_OWNER, AN_INSTALL, DATE, DATE); + + assertThat(underTest.selectByOwnerId(dbSession, BITBUCKETCLOUD, A_OWNER)).isNotPresent(); + assertThat(underTest.selectByOwnerId(dbSession, GITHUB, "Unknown owner")).isNotPresent(); } @Test public void selectByOwner_throws_NPE_when_alm_is_null() { expectAlmNPE(); - underTest.selectByOwner(dbSession, null, A_OWNER); + underTest.selectByOwnerId(dbSession, null, A_OWNER); } @Test public void selectByOwner_throws_IAE_when_owner_id_is_null() { expectOwnerIdNullOrEmptyIAE(); - underTest.selectByOwner(dbSession, GITHUB, null); + underTest.selectByOwnerId(dbSession, GITHUB, null); } @Test public void selectByOwner_throws_IAE_when_owner_id_is_empty() { expectOwnerIdNullOrEmptyIAE(); - underTest.selectByOwner(dbSession, GITHUB, EMPTY_STRING); + underTest.selectByOwnerId(dbSession, GITHUB, EMPTY_STRING); } @Test - public void getOwnerId() { + public void selectByInstallationId() { when(uuidFactory.create()).thenReturn(A_UUID); + when(system2.now()).thenReturn(DATE); underTest.insertOrUpdate(dbSession, GITHUB, A_OWNER, true, AN_INSTALL); - assertThat(underTest.getOwerId(dbSession, GITHUB, AN_INSTALL)).contains(A_OWNER); - assertThat(underTest.getOwerId(dbSession, GITHUB, "unknown")).isEmpty(); - assertThat(underTest.getOwerId(dbSession, BITBUCKETCLOUD, AN_INSTALL)).isEmpty(); + assertThat(underTest.selectByInstallationId(dbSession, GITHUB, AN_INSTALL).get()) + .extracting(AlmAppInstallDto::getUuid, AlmAppInstallDto::getAlm, AlmAppInstallDto::getInstallId, AlmAppInstallDto::getOwnerId, + AlmAppInstallDto::getCreatedAt, AlmAppInstallDto::getUpdatedAt) + .contains(A_UUID, GITHUB, A_OWNER, AN_INSTALL, DATE, DATE); + + assertThat(underTest.selectByInstallationId(dbSession, GITHUB, "unknown installation")).isEmpty(); + assertThat(underTest.selectByInstallationId(dbSession, BITBUCKETCLOUD, AN_INSTALL)).isEmpty(); } @Test @@ -166,7 +173,7 @@ public class AlmAppInstallDaoTest { } @Test - public void delete_doesn_t_fail() { + public void delete_does_not_fail() { assertThatAlmAppInstall(GITHUB, A_OWNER).doesNotExist(); underTest.delete(dbSession, GITHUB, A_OWNER); @@ -179,7 +186,7 @@ public class AlmAppInstallDaoTest { underTest.insertOrUpdate(dbSession, GITHUB, A_OWNER, true, AN_INSTALL); when(system2.now()).thenReturn(DATE_LATER); - underTest.insertOrUpdate(dbSession, GITHUB, A_OWNER,true, OTHER_INSTALL); + underTest.insertOrUpdate(dbSession, GITHUB, A_OWNER, true, OTHER_INSTALL); assertThatAlmAppInstall(GITHUB, A_OWNER) .hasInstallId(OTHER_INSTALL) @@ -235,7 +242,7 @@ public class AlmAppInstallDaoTest { } private static AlmAppInstallDto asAlmAppInstall(DbTester db, DbSession dbSession, ALM alm, String ownerId) { - Optional<AlmAppInstallDto> almAppInstall = db.getDbClient().almAppInstallDao().selectByOwner(dbSession, alm, ownerId); + Optional<AlmAppInstallDto> almAppInstall = db.getDbClient().almAppInstallDao().selectByOwnerId(dbSession, alm, ownerId); return almAppInstall.orElse(null); } diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmDbTester.java b/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmDbTester.java new file mode 100644 index 00000000000..f129b5923ae --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/alm/AlmDbTester.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.alm; + +import org.sonar.db.DbTester; +import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.user.UserDto; + +import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; +import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; +import static org.sonar.db.alm.ALM.GITHUB; + +public class AlmDbTester { + + private final DbTester db; + + public AlmDbTester(DbTester db) { + this.db = db; + } + + public OrganizationAlmBindingDto insertOrganizationAlmBinding(OrganizationDto organization, AlmAppInstallDto almAppInstall) { + UserDto user = db.users().insertUser(); + db.getDbClient().organizationAlmBindingDao().insert(db.getSession(), organization, almAppInstall, randomAlphabetic(10), user.getUuid()); + return db.getDbClient().organizationAlmBindingDao().selectByOrganization(db.getSession(), organization).get(); + } + + public AlmAppInstallDto insertAlmAppInstall() { + ALM alm = GITHUB; + String ownerId = randomAlphanumeric(10); + db.getDbClient().almAppInstallDao().insertOrUpdate(db.getSession(), alm, ownerId, false, randomAlphanumeric(10)); + return db.getDbClient().almAppInstallDao().selectByOwnerId(db.getSession(), alm, ownerId).get(); + } + +} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/alm/OrganizationAlmBindingDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/alm/OrganizationAlmBindingDaoTest.java new file mode 100644 index 00000000000..8972e200a09 --- /dev/null +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/alm/OrganizationAlmBindingDaoTest.java @@ -0,0 +1,158 @@ +/* + * 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.alm; + +import java.util.Optional; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.internal.TestSystem2; +import org.sonar.core.util.UuidFactory; +import org.sonar.core.util.Uuids; +import org.sonar.db.DbTester; +import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.user.UserDto; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; +import static org.assertj.core.groups.Tuple.tuple; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class OrganizationAlmBindingDaoTest { + + private static final long NOW = 1_600_000_000_000L; + + private System2 system2 = new TestSystem2().setNow(NOW); + + @Rule + public DbTester db = DbTester.create(system2); + + private UuidFactory uuidFactory = mock(UuidFactory.class); + + private OrganizationAlmBindingDao underTest = new OrganizationAlmBindingDao(system2, uuidFactory); + + @Test + public void selectByOrganization() { + OrganizationDto organization = db.organizations().insert(); + AlmAppInstallDto almAppInstall = db.alm().insertAlmAppInstall(); + OrganizationAlmBindingDto dto = db.alm().insertOrganizationAlmBinding(organization, almAppInstall); + + Optional<OrganizationAlmBindingDto> result = underTest.selectByOrganization(db.getSession(), organization); + + assertThat(result).isPresent(); + assertThat(result.get()) + .extracting(OrganizationAlmBindingDto::getUuid, OrganizationAlmBindingDto::getOrganizationUuid, OrganizationAlmBindingDto::getAlmAppInstallUuid, + OrganizationAlmBindingDto::getUrl, OrganizationAlmBindingDto::getAlm, + OrganizationAlmBindingDto::getUserUuid, OrganizationAlmBindingDto::getCreatedAt) + .containsExactlyInAnyOrder(dto.getUuid(), organization.getUuid(), dto.getAlmAppInstallUuid(), + dto.getUrl(), ALM.GITHUB, + dto.getUserUuid(), NOW); + } + + @Test + public void selectByOrganization_returns_empty_when_organization_is_not_bound_to_installation() { + OrganizationDto organization = db.organizations().insert(); + AlmAppInstallDto almAppInstall = db.alm().insertAlmAppInstall(); + db.alm().insertOrganizationAlmBinding(organization, almAppInstall); + // No binding on other organization + OrganizationDto otherOrganization = db.organizations().insert(); + + Optional<OrganizationAlmBindingDto> result = underTest.selectByOrganization(db.getSession(), otherOrganization); + + assertThat(result).isEmpty(); + } + + @Test + public void selectByOrganizations() { + OrganizationDto organization1 = db.organizations().insert(); + OrganizationAlmBindingDto organizationAlmBinding1 = db.alm().insertOrganizationAlmBinding(organization1, db.alm().insertAlmAppInstall()); + OrganizationDto organization2 = db.organizations().insert(); + OrganizationAlmBindingDto organizationAlmBinding2 = db.alm().insertOrganizationAlmBinding(organization2, db.alm().insertAlmAppInstall()); + OrganizationDto organizationNotBound = db.organizations().insert(); + + assertThat(underTest.selectByOrganizations(db.getSession(), asList(organization1, organization2, organizationNotBound))) + .extracting(OrganizationAlmBindingDto::getUuid, OrganizationAlmBindingDto::getOrganizationUuid) + .containsExactlyInAnyOrder( + tuple(organizationAlmBinding1.getUuid(), organization1.getUuid()), + tuple(organizationAlmBinding2.getUuid(), organization2.getUuid())); + + assertThat(underTest.selectByOrganizations(db.getSession(), singletonList(organizationNotBound))).isEmpty(); + } + + @Test + public void insert() { + when(uuidFactory.create()).thenReturn("ABCD"); + OrganizationDto organization = db.organizations().insert(); + UserDto user = db.users().insertUser(); + AlmAppInstallDto almAppInstall = db.alm().insertAlmAppInstall(); + + underTest.insert(db.getSession(), organization, almAppInstall, "http://myorg.com", user.getUuid()); + + assertThat(db.selectFirst(db.getSession(), + "select" + + " uuid as \"uuid\", organization_uuid as \"organizationUuid\", alm_app_install_uuid as \"almAppInstallUuid\", url as \"url\", alm_id as \"almId\"," + + " user_uuid as \"userUuid\", created_at as \"createdAt\"" + + " from organization_alm_bindings" + + " where organization_uuid='" + organization.getUuid() + "'")) + .contains( + entry("uuid", "ABCD"), + entry("organizationUuid", organization.getUuid()), + entry("almAppInstallUuid", almAppInstall.getUuid()), + entry("almId", "github"), + entry("url", "http://myorg.com"), + entry("userUuid", user.getUuid()), + entry("createdAt", NOW)); + } + + @Test + public void deleteByOrganization() { + OrganizationDto organization = db.organizations().insert(); + AlmAppInstallDto almAppInstall = db.alm().insertAlmAppInstall(); + db.alm().insertOrganizationAlmBinding(organization, almAppInstall); + OrganizationDto otherOrganization = db.organizations().insert(); + AlmAppInstallDto otherAlmAppInstall = db.alm().insertAlmAppInstall(); + db.alm().insertOrganizationAlmBinding(otherOrganization, otherAlmAppInstall); + + underTest.deleteByOrganization(db.getSession(), organization); + + assertThat(underTest.selectByOrganization(db.getSession(), organization)).isNotPresent(); + assertThat(underTest.selectByOrganization(db.getSession(), otherOrganization)).isPresent(); + } + + @Test + public void deleteByAlmAppInstall() { + OrganizationDto organization = db.organizations().insert(); + AlmAppInstallDto almAppInstall = db.alm().insertAlmAppInstall(); + db.alm().insertOrganizationAlmBinding(organization, almAppInstall); + OrganizationDto otherOrganization = db.organizations().insert(); + AlmAppInstallDto otherAlmAppInstall = db.alm().insertAlmAppInstall(); + db.alm().insertOrganizationAlmBinding(otherOrganization, otherAlmAppInstall); + + underTest.deleteByAlmAppInstall(db.getSession(), almAppInstall); + + assertThat(underTest.selectByOrganization(db.getSession(), organization)).isNotPresent(); + assertThat(underTest.selectByOrganization(db.getSession(), otherOrganization)).isPresent(); + } + +} diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/alm/ProjectAlmBindingsDaoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/alm/ProjectAlmBindingDaoTest.java index 0db93b0bac7..2dd6635c427 100644 --- a/server/sonar-db-dao/src/test/java/org/sonar/db/alm/ProjectAlmBindingsDaoTest.java +++ b/server/sonar-db-dao/src/test/java/org/sonar/db/alm/ProjectAlmBindingDaoTest.java @@ -45,7 +45,7 @@ import static org.mockito.Mockito.when; import static org.sonar.db.alm.ALM.BITBUCKETCLOUD; import static org.sonar.db.alm.ALM.GITHUB; -public class ProjectAlmBindingsDaoTest { +public class ProjectAlmBindingDaoTest { private static final String A_UUID = "abcde1234"; private static final String ANOTHER_UUID = "xyz789"; @@ -73,7 +73,7 @@ public class ProjectAlmBindingsDaoTest { private DbSession dbSession = dbTester.getSession(); private UuidFactory uuidFactory = mock(UuidFactory.class); - private ProjectAlmBindingsDao underTest = new ProjectAlmBindingsDao(system2, uuidFactory); + private ProjectAlmBindingDao underTest = new ProjectAlmBindingDao(system2, uuidFactory); @Test public void insert_throws_NPE_if_alm_is_null() { diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v75/CreateOrganizationsAlmBindingsTable.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v75/CreateOrganizationsAlmBindingsTable.java new file mode 100644 index 00000000000..676455672a8 --- /dev/null +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v75/CreateOrganizationsAlmBindingsTable.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.v75; + +import java.sql.Connection; +import java.sql.SQLException; +import org.sonar.db.Database; +import org.sonar.db.DatabaseUtils; +import org.sonar.server.platform.db.migration.def.BigIntegerColumnDef; +import org.sonar.server.platform.db.migration.def.VarcharColumnDef; +import org.sonar.server.platform.db.migration.sql.CreateIndexBuilder; +import org.sonar.server.platform.db.migration.sql.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.newVarcharColumnDefBuilder; + +public class CreateOrganizationsAlmBindingsTable extends DdlChange { + + private static final String TABLE_NAME = "organization_alm_bindings"; + + private static final VarcharColumnDef UUID_COLUMN = newVarcharColumnDefBuilder() + .setColumnName("uuid") + .setIsNullable(false) + .setLimit(VarcharColumnDef.UUID_SIZE) + .build(); + private static final VarcharColumnDef ORGANIZATION_UUID_COLUMN = newVarcharColumnDefBuilder() + .setColumnName("organization_uuid") + .setIsNullable(false) + .setLimit(VarcharColumnDef.UUID_SIZE) + .build(); + private static final VarcharColumnDef ALM_APP_INSTALL_UUID_COLUMN = newVarcharColumnDefBuilder() + .setColumnName("alm_app_install_uuid") + .setIsNullable(false) + .setLimit(VarcharColumnDef.UUID_SIZE) + .build(); + private static final VarcharColumnDef ALM_ID_COLUMN = newVarcharColumnDefBuilder() + .setColumnName("alm_id") + .setIsNullable(false) + .setLimit(40) + .build(); + private static final VarcharColumnDef URL_COLUMN = newVarcharColumnDefBuilder() + .setColumnName("url") + .setIsNullable(false) + .setLimit(2000) + .build(); + private static final VarcharColumnDef USER_UUID_COLUMN = newVarcharColumnDefBuilder() + .setColumnName("user_uuid") + .setIsNullable(false) + .setLimit(255) + .build(); + private static final BigIntegerColumnDef CREATED_AT_COLUMN = newBigIntegerColumnDefBuilder() + .setColumnName("created_at") + .setIsNullable(false) + .build(); + + public CreateOrganizationsAlmBindingsTable(Database db) { + super(db); + } + + @Override + public void execute(Context context) throws SQLException { + if (!tableExists()) { + context.execute(new CreateTableBuilder(getDialect(), TABLE_NAME) + .addPkColumn(UUID_COLUMN) + .addColumn(ORGANIZATION_UUID_COLUMN) + .addColumn(ALM_APP_INSTALL_UUID_COLUMN) + .addColumn(ALM_ID_COLUMN) + .addColumn(URL_COLUMN) + .addColumn(USER_UUID_COLUMN) + .addColumn(CREATED_AT_COLUMN) + .build()); + + context.execute(new CreateIndexBuilder(getDialect()) + .addColumn(ORGANIZATION_UUID_COLUMN) + .setUnique(true) + .setTable(TABLE_NAME) + .setName("org_alm_bindings_org") + .build()); + + context.execute(new CreateIndexBuilder(getDialect()) + .addColumn(ALM_APP_INSTALL_UUID_COLUMN) + .setUnique(true) + .setTable(TABLE_NAME) + .setName("org_alm_bindings_install") + .build()); + } + } + + private boolean tableExists() throws SQLException { + try (Connection connection = getDatabase().getDataSource().getConnection()) { + return DatabaseUtils.tableExists(TABLE_NAME, connection); + } + } +} diff --git a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v75/DbVersion75.java b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v75/DbVersion75.java index ec7c0d05c35..65c33aa424e 100644 --- a/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v75/DbVersion75.java +++ b/server/sonar-db-migration/src/main/java/org/sonar/server/platform/db/migration/version/v75/DbVersion75.java @@ -28,6 +28,7 @@ public class DbVersion75 implements DbVersion { public void addSteps(MigrationStepRegistry registry) { registry .add(2400, "Add column IS_OWNER_USER in ALM_APP_INSTALLS", AddIsOwnerUserColumnInAlmAppInstall.class) + .add(2401, "Create ORGANIZATION_ALM_BINDINGS table", CreateOrganizationsAlmBindingsTable.class) ; } } diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v75/CreateOrganizationsAlmBindingsTableTest.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v75/CreateOrganizationsAlmBindingsTableTest.java new file mode 100644 index 00000000000..7cac352e8f6 --- /dev/null +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v75/CreateOrganizationsAlmBindingsTableTest.java @@ -0,0 +1,67 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.platform.db.migration.version.v75; + +import java.sql.SQLException; +import java.sql.Types; +import org.junit.Rule; +import org.junit.Test; +import org.sonar.db.CoreDbTester; + +import static java.sql.Types.VARCHAR; + +public class CreateOrganizationsAlmBindingsTableTest { + + private static final String TABLE = "organization_alm_bindings"; + + @Rule + public final CoreDbTester db = CoreDbTester.createEmpty(); + + private CreateOrganizationsAlmBindingsTable underTest = new CreateOrganizationsAlmBindingsTable(db.database()); + + @Test + public void creates_table() throws SQLException { + underTest.execute(); + + checkTable(); + } + + @Test + public void migration_is_reentrant() throws SQLException { + underTest.execute(); + underTest.execute(); + + checkTable(); + } + + private void checkTable() { + db.assertColumnDefinition(TABLE, "uuid", Types.VARCHAR, 40, false); + db.assertColumnDefinition(TABLE, "organization_uuid", VARCHAR, 40, false); + db.assertColumnDefinition(TABLE, "alm_app_install_uuid", VARCHAR, 40, false); + db.assertColumnDefinition(TABLE, "alm_id", Types.VARCHAR, 40, false); + db.assertColumnDefinition(TABLE, "url", Types.VARCHAR, 2000, false); + db.assertColumnDefinition(TABLE, "user_uuid", Types.VARCHAR, 255, false); + db.assertColumnDefinition(TABLE, "created_at", Types.BIGINT, null, false); + + db.assertUniqueIndex(TABLE, "org_alm_bindings_org", "organization_uuid"); + db.assertUniqueIndex(TABLE, "org_alm_bindings_install", "alm_app_install_uuid"); + } + +} diff --git a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v75/DbVersion75Test.java b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v75/DbVersion75Test.java index c6d922ba3b0..0b940dbcb16 100644 --- a/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v75/DbVersion75Test.java +++ b/server/sonar-db-migration/src/test/java/org/sonar/server/platform/db/migration/version/v75/DbVersion75Test.java @@ -35,6 +35,6 @@ public class DbVersion75Test { @Test public void verify_migration_count() { - verifyMigrationCount(underTest, 1); + verifyMigrationCount(underTest, 2); } } diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationAlmBinding.java b/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationAlmBinding.java new file mode 100644 index 00000000000..7fccdc922b5 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationAlmBinding.java @@ -0,0 +1,31 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.organization; + +import org.sonar.api.server.ServerSide; +import org.sonar.db.DbSession; +import org.sonar.db.organization.OrganizationDto; + +@ServerSide +public interface OrganizationAlmBinding { + + void bindOrganization(DbSession dbSession, OrganizationDto organization, String installationId); +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationUpdater.java index 60bb5006439..6bb14d6fb72 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationUpdater.java +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationUpdater.java @@ -20,6 +20,7 @@ package org.sonar.server.organization; import java.util.Optional; +import java.util.function.Consumer; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import org.sonar.api.web.UserRole; @@ -72,7 +73,7 @@ public interface OrganizationUpdater { * @throws KeyConflictException if an organization with the specified key already exists * @throws IllegalArgumentException if any field of {@code newOrganization} is invalid according to {@link OrganizationValidation} */ - OrganizationDto create(DbSession dbSession, UserDto userCreator, NewOrganization newOrganization) throws KeyConflictException; + OrganizationDto create(DbSession dbSession, UserDto userCreator, NewOrganization newOrganization, Consumer<OrganizationDto> beforeCommit) throws KeyConflictException; /** * Create a new guarded organization which details are based on the login of the specified User. diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationUpdaterImpl.java b/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationUpdaterImpl.java index cc6b889a0c8..5765db7c88c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationUpdaterImpl.java +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/OrganizationUpdaterImpl.java @@ -94,7 +94,7 @@ public class OrganizationUpdaterImpl implements OrganizationUpdater { } @Override - public OrganizationDto create(DbSession dbSession, UserDto userCreator, NewOrganization newOrganization) throws KeyConflictException { + public OrganizationDto create(DbSession dbSession, UserDto userCreator, NewOrganization newOrganization, Consumer<OrganizationDto> beforeCommit) throws KeyConflictException { validate(newOrganization); String key = newOrganization.getKey(); if (organizationKeyIsUsed(dbSession, key)) { @@ -103,16 +103,16 @@ public class OrganizationUpdaterImpl implements OrganizationUpdater { QualityGateDto builtInQualityGate = dbClient.qualityGateDao().selectBuiltIn(dbSession); OrganizationDto organization = insertOrganization(dbSession, newOrganization, builtInQualityGate); + beforeCommit.accept(organization); insertOrganizationMember(dbSession, organization, userCreator.getId()); dbClient.qualityGateDao().associate(dbSession, uuidFactory.create(), organization, builtInQualityGate); GroupDto ownerGroup = insertOwnersGroup(dbSession, organization); GroupDto defaultGroup = defaultGroupCreator.create(dbSession, organization.getUuid()); insertDefaultTemplateOnGroups(dbSession, organization, ownerGroup, defaultGroup); + addCurrentUserToGroup(dbSession, ownerGroup, userCreator.getId()); + addCurrentUserToGroup(dbSession, defaultGroup, userCreator.getId()); try (DbSession batchDbSession = dbClient.openSession(true)) { insertQualityProfiles(dbSession, batchDbSession, organization); - addCurrentUserToGroup(dbSession, ownerGroup, userCreator.getId()); - addCurrentUserToGroup(dbSession, defaultGroup, userCreator.getId()); - batchDbSession.commit(); // Elasticsearch is updated when DB session is committed diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/CreateAction.java index 136866aff69..992add61372 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/CreateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/CreateAction.java @@ -31,6 +31,7 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.user.UserDto; +import org.sonar.server.organization.OrganizationAlmBinding; import org.sonar.server.organization.OrganizationFlags; import org.sonar.server.organization.OrganizationUpdater; import org.sonar.server.organization.OrganizationValidation; @@ -38,6 +39,7 @@ import org.sonar.server.user.UserSession; import org.sonarqube.ws.Organizations.CreateWsResponse; import static com.google.common.base.Preconditions.checkArgument; +import static java.util.Objects.requireNonNull; import static org.sonar.server.organization.OrganizationUpdater.NewOrganization.newOrganizationBuilder; import static org.sonar.server.organization.OrganizationValidation.KEY_MAX_LENGTH; import static org.sonar.server.organization.OrganizationValidation.KEY_MIN_LENGTH; @@ -45,7 +47,9 @@ import static org.sonar.server.organization.ws.OrganizationsWsSupport.PARAM_KEY; import static org.sonar.server.ws.WsUtils.writeProtobuf; public class CreateAction implements OrganizationsWsAction { + private static final String ACTION = "create"; + private static final String PARAM_INSTALLATION_ID = "installationId"; private final Configuration config; private final UserSession userSession; @@ -54,9 +58,12 @@ public class CreateAction implements OrganizationsWsAction { private final OrganizationValidation organizationValidation; private final OrganizationUpdater organizationUpdater; private final OrganizationFlags organizationFlags; + @CheckForNull + private final OrganizationAlmBinding organizationAlmBinding; public CreateAction(Configuration config, UserSession userSession, DbClient dbClient, OrganizationsWsSupport wsSupport, - OrganizationValidation organizationValidation, OrganizationUpdater organizationUpdater, OrganizationFlags organizationFlags) { + OrganizationValidation organizationValidation, OrganizationUpdater organizationUpdater, OrganizationFlags organizationFlags, + @Nullable OrganizationAlmBinding organizationAlmBinding) { this.config = config; this.userSession = userSession; this.dbClient = dbClient; @@ -64,6 +71,12 @@ public class CreateAction implements OrganizationsWsAction { this.organizationValidation = organizationValidation; this.organizationUpdater = organizationUpdater; this.organizationFlags = organizationFlags; + this.organizationAlmBinding = organizationAlmBinding; + } + + public CreateAction(Configuration config, UserSession userSession, DbClient dbClient, OrganizationsWsSupport wsSupport, + OrganizationValidation organizationValidation, OrganizationUpdater organizationUpdater, OrganizationFlags organizationFlags) { + this(config, userSession, dbClient, wsSupport, organizationValidation, organizationUpdater, organizationFlags, null); } @Override @@ -77,8 +90,7 @@ public class CreateAction implements OrganizationsWsAction { .setSince("6.2") .setChangelog( new Change("7.4", "Maximal number of character of name and key is 300 characters"), - new Change("7.2", "Minimal number of character of name and key is one character") - ) + new Change("7.2", "Minimal number of character of name and key is one character")) .setHandler(this); action.createParam(PARAM_KEY) @@ -90,6 +102,11 @@ public class CreateAction implements OrganizationsWsAction { "When not specified, the key is computed from the name. <br />" + "All chars must be lower-case letters (a to z), digits or dash (but dash can neither be trailing nor heading)") .setExampleValue("foo-company"); + action.createParam(PARAM_INSTALLATION_ID) + .setRequired(false) + .setInternal(true) + .setDescription("Installation ID of the GitHub/Bitbucket application") + .setExampleValue("387133"); wsSupport.addOrganizationDetailsParams(action, true); } @@ -111,7 +128,7 @@ public class CreateAction implements OrganizationsWsAction { try (DbSession dbSession = dbClient.openSession(false)) { organizationFlags.checkEnabled(dbSession); - UserDto currentUser = dbClient.userDao().selectActiveUserByLogin(dbSession, userSession.getLogin()); + UserDto currentUser = requireNonNull(dbClient.userDao().selectActiveUserByLogin(dbSession, requireNonNull(userSession.getLogin()))); OrganizationDto organization = organizationUpdater.create( dbSession, currentUser, @@ -121,7 +138,8 @@ public class CreateAction implements OrganizationsWsAction { .setDescription(description) .setUrl(url) .setAvatarUrl(avatar) - .build()); + .build(), + o -> bindOrganization(request, dbSession, o)); writeResponse(request, response, organization); } catch (OrganizationUpdater.KeyConflictException e) { @@ -130,6 +148,17 @@ public class CreateAction implements OrganizationsWsAction { } } + private void bindOrganization(Request request, DbSession dbSession, OrganizationDto organization) { + if (organizationAlmBinding == null) { + return; + } + String installationId = request.param(PARAM_INSTALLATION_ID); + if (installationId == null) { + return; + } + organizationAlmBinding.bindOrganization(dbSession, organization, installationId); + } + @CheckForNull private String getAndCheckKey(Request request) { String rqstKey = request.param(PARAM_KEY); diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java index 7b1685f8385..ad2a2d80fce 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/DeleteAction.java @@ -120,6 +120,7 @@ public class DeleteAction implements OrganizationsWsAction { deleteGroups(dbSession, organization); deleteQualityProfiles(dbSession, organization); deleteQualityGates(dbSession, organization); + deleteOrganizationAlmBinding(dbSession, organization); deleteOrganization(dbSession, organization); billingValidations.onDelete(new BillingValidations.Organization(organization.getKey(), organization.getUuid())); @@ -148,16 +149,12 @@ public class DeleteAction implements OrganizationsWsAction { private void deletePermissions(DbSession dbSession, OrganizationDto organization) { dbClient.permissionTemplateDao().deleteByOrganization(dbSession, organization.getUuid()); - dbSession.commit(); dbClient.userPermissionDao().deleteByOrganization(dbSession, organization.getUuid()); - dbSession.commit(); dbClient.groupPermissionDao().deleteByOrganization(dbSession, organization.getUuid()); - dbSession.commit(); } private void deleteGroups(DbSession dbSession, OrganizationDto organization) { dbClient.groupDao().deleteByOrganization(dbSession, organization.getUuid()); - dbSession.commit(); } private void deleteQualityProfiles(DbSession dbSession, OrganizationDto organization) { @@ -174,6 +171,10 @@ public class DeleteAction implements OrganizationsWsAction { dbClient.qualityGateDao().deleteOrgQualityGatesByOrganization(dbSession, organization); } + private void deleteOrganizationAlmBinding(DbSession dbSession, OrganizationDto organization){ + dbClient.organizationAlmBindingDao().deleteByOrganization(dbSession, organization); + } + private void deleteOrganization(DbSession dbSession, OrganizationDto organization) { Collection<String> uuids = dbClient.organizationMemberDao().selectUserUuidsByOrganizationUuid(dbSession, organization.getUuid()); dbClient.organizationMemberDao().deleteByOrganizationUuid(dbSession, organization.getUuid()); diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java index 25f59dd801d..9be33f51eed 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/OrganizationsWsSupport.java @@ -42,7 +42,6 @@ public class OrganizationsWsSupport { static final String PARAM_DESCRIPTION = "description"; static final String PARAM_URL = "url"; static final String PARAM_AVATAR_URL = "avatar"; - static final String PARAM_LOGIN = "login"; private final OrganizationValidation organizationValidation; @@ -109,10 +108,7 @@ public class OrganizationsWsSupport { } Organization.Builder toOrganization(OrganizationDto dto) { - return toOrganization(Organization.newBuilder(), dto); - } - - Organization.Builder toOrganization(Organization.Builder builder, OrganizationDto dto) { + Organization.Builder builder = Organization.newBuilder(); builder .setName(dto.getName()) .setKey(dto.getKey()) diff --git a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/SearchAction.java b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/SearchAction.java index d9d9f6835b3..625a16687d7 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/organization/ws/SearchAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/organization/ws/SearchAction.java @@ -20,15 +20,19 @@ package org.sonar.server.organization.ws; import java.util.List; +import java.util.Map; import java.util.Set; import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import org.sonar.api.server.ws.Change; import org.sonar.api.server.ws.Request; import org.sonar.api.server.ws.Response; import org.sonar.api.server.ws.WebService; import org.sonar.api.server.ws.WebService.Param; +import org.sonar.core.util.stream.MoreCollectors; import org.sonar.db.DbClient; import org.sonar.db.DbSession; +import org.sonar.db.alm.OrganizationAlmBindingDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.organization.OrganizationQuery; import org.sonar.server.user.UserSession; @@ -36,6 +40,7 @@ import org.sonarqube.ws.Organizations; import org.sonarqube.ws.Organizations.Organization; import static java.util.Collections.emptySet; +import static org.sonar.core.util.Protobuf.setNullable; import static org.sonar.core.util.stream.MoreCollectors.toSet; import static org.sonar.db.Pagination.forPage; import static org.sonar.db.organization.OrganizationQuery.newOrganizationQueryBuilder; @@ -52,12 +57,10 @@ public class SearchAction implements OrganizationsWsAction { private final DbClient dbClient; private final UserSession userSession; - private final OrganizationsWsSupport wsSupport; - public SearchAction(DbClient dbClient, UserSession userSession, OrganizationsWsSupport wsSupport) { + public SearchAction(DbClient dbClient, UserSession userSession) { this.dbClient = dbClient; this.userSession = userSession; - this.wsSupport = wsSupport; } @Override @@ -90,7 +93,7 @@ public class SearchAction implements OrganizationsWsAction { @Override public void handle(Request request, Response response) throws Exception { boolean isMember = request.mandatoryParamAsBoolean(PARAM_MEMBER); - if (isMember){ + if (isMember) { userSession.checkLoggedIn(); } @@ -100,7 +103,9 @@ public class SearchAction implements OrganizationsWsAction { Paging paging = buildWsPaging(request, total); List<OrganizationDto> organizations = dbClient.organizationDao().selectByQuery(dbSession, dbQuery, forPage(paging.getPageIndex()).andSize(paging.getPageSize())); Set<String> adminOrganizationUuids = searchOrganizationWithAdminPermission(dbSession); - writeResponse(request, response, organizations, adminOrganizationUuids, paging); + Map<String, OrganizationAlmBindingDto> organizationAlmBindingByOrgUuid = dbClient.organizationAlmBindingDao().selectByOrganizations(dbSession, organizations) + .stream().collect(MoreCollectors.uniqueIndex(OrganizationAlmBindingDto::getOrganizationUuid)); + writeResponse(request, response, organizations, adminOrganizationUuids, organizationAlmBindingByOrgUuid, paging); } } @@ -117,20 +122,38 @@ public class SearchAction implements OrganizationsWsAction { : dbClient.organizationDao().selectByPermission(dbSession, userId, ADMINISTER.getKey()).stream().map(OrganizationDto::getUuid).collect(toSet()); } - private void writeResponse(Request httpRequest, Response httpResponse, List<OrganizationDto> organizations, Set<String> adminOrganizationUuids, Paging paging) { + private static void writeResponse(Request httpRequest, Response httpResponse, List<OrganizationDto> organizations, Set<String> adminOrganizationUuids, + Map<String, OrganizationAlmBindingDto> organizationAlmBindingByOrgUuid, + Paging paging) { Organizations.SearchWsResponse.Builder response = Organizations.SearchWsResponse.newBuilder(); response.setPaging(paging); Organization.Builder wsOrganization = Organization.newBuilder(); organizations .forEach(o -> { - boolean isAdmin = adminOrganizationUuids.contains(o.getUuid()); wsOrganization.clear(); + boolean isAdmin = adminOrganizationUuids.contains(o.getUuid()); wsOrganization.setIsAdmin(isAdmin); - response.addOrganizations(wsSupport.toOrganization(wsOrganization, o)); + response.addOrganizations(toOrganization(wsOrganization, o, organizationAlmBindingByOrgUuid.get(o.getUuid()))); }); writeProtobuf(response.build(), httpRequest, httpResponse); } + private static Organization.Builder toOrganization(Organization.Builder builder, OrganizationDto organization, @Nullable OrganizationAlmBindingDto organizationAlmBinding) { + builder + .setName(organization.getName()) + .setKey(organization.getKey()) + .setGuarded(organization.isGuarded()); + setNullable(organization.getDescription(), builder::setDescription); + setNullable(organization.getUrl(), builder::setUrl); + setNullable(organization.getAvatarUrl(), builder::setAvatar); + if (organizationAlmBinding != null) { + builder.setAlm(Organization.Alm.newBuilder() + .setKey(organizationAlmBinding.getAlm().getId()) + .setUrl(organizationAlmBinding.getUrl())); + } + return builder; + } + private static Paging buildWsPaging(Request request, int total) { return Paging.newBuilder() .setPageIndex(request.mandatoryParamAsInt(Param.PAGE)) diff --git a/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java b/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java index 4fb3bf2f420..eba36038998 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/ui/ws/ComponentAction.java @@ -192,8 +192,10 @@ public class ComponentAction implements NavigationWsAction { .map(ALM::getId) .map(String::valueOf) .orElseThrow(() -> new IllegalStateException("Alm binding id DB has no ALM id")); - json.prop("almId", almId) - .prop("almRepoUrl", b.getUrl()); + json.name("alm").beginObject() + .prop("key", almId) + .prop("url", b.getUrl()) + .endObject(); }); } diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/OrganizationUpdaterImplTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/OrganizationUpdaterImplTest.java index f389e9bc947..8d1b7019024 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/organization/OrganizationUpdaterImplTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/organization/OrganizationUpdaterImplTest.java @@ -22,6 +22,7 @@ package org.sonar.server.organization; import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.function.Consumer; import org.apache.commons.lang.RandomStringUtils; import org.junit.Rule; import org.junit.Test; @@ -90,6 +91,9 @@ public class OrganizationUpdaterImplTest { private System2 system2 = new TestSystem2().setNow(A_DATE); + private static Consumer<OrganizationDto> EMPTY_ORGANIZATION_CONSUMER = o -> { + }; + @Rule public DbTester db = DbTester.create(system2); @Rule @@ -122,7 +126,7 @@ public class OrganizationUpdaterImplTest { UserDto user = db.users().insertUser(); db.qualityGates().insertBuiltInQualityGate(); - underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION); + underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION, EMPTY_ORGANIZATION_CONSUMER); OrganizationDto organization = dbClient.organizationDao().selectByKey(dbSession, FULL_POPULATED_NEW_ORGANIZATION.getKey()).get(); assertThat(organization.getUuid()).isNotEmpty(); @@ -143,7 +147,7 @@ public class OrganizationUpdaterImplTest { builtInQProfileRepositoryRule.initialize(); db.qualityGates().insertBuiltInQualityGate(); - underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION); + underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION, EMPTY_ORGANIZATION_CONSUMER); verifyGroupOwners(user, FULL_POPULATED_NEW_ORGANIZATION.getKey(), FULL_POPULATED_NEW_ORGANIZATION.getName()); } @@ -154,7 +158,7 @@ public class OrganizationUpdaterImplTest { builtInQProfileRepositoryRule.initialize(); db.qualityGates().insertBuiltInQualityGate(); - underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION); + underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION, EMPTY_ORGANIZATION_CONSUMER); verifyMembersGroup(user, FULL_POPULATED_NEW_ORGANIZATION.getKey()); } @@ -168,7 +172,7 @@ public class OrganizationUpdaterImplTest { underTest.create(dbSession, user, newOrganizationBuilder() .setKey("key") .setName("name") - .build()); + .build(), EMPTY_ORGANIZATION_CONSUMER); OrganizationDto organization = dbClient.organizationDao().selectByKey(dbSession, "key").get(); assertThat(organization.getKey()).isEqualTo("key"); @@ -185,7 +189,7 @@ public class OrganizationUpdaterImplTest { UserDto user = db.users().insertUser(); db.qualityGates().insertBuiltInQualityGate(); - underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION); + underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION, EMPTY_ORGANIZATION_CONSUMER); OrganizationDto organization = dbClient.organizationDao().selectByKey(dbSession, FULL_POPULATED_NEW_ORGANIZATION.getKey()).get(); GroupDto ownersGroup = dbClient.groupDao().selectByName(dbSession, organization.getUuid(), "Owners").get(); @@ -210,7 +214,7 @@ public class OrganizationUpdaterImplTest { builtInQProfileRepositoryRule.initialize(); db.qualityGates().insertBuiltInQualityGate(); - OrganizationDto result = underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION); + OrganizationDto result = underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION, EMPTY_ORGANIZATION_CONSUMER); assertThat(dbClient.organizationMemberDao().select(dbSession, result.getUuid(), user.getId())).isPresent(); assertThat(userIndex.search(UserQuery.builder().setOrganizationUuid(result.getUuid()).setTextQuery(user.getLogin()).build(), new SearchOptions()).getTotal()).isEqualTo(1L); @@ -226,7 +230,7 @@ public class OrganizationUpdaterImplTest { UserDto user = db.users().insertUser(); db.qualityGates().insertBuiltInQualityGate(); - underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION); + underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION, EMPTY_ORGANIZATION_CONSUMER); OrganizationDto organization = dbClient.organizationDao().selectByKey(dbSession, FULL_POPULATED_NEW_ORGANIZATION.getKey()).get(); List<QProfileDto> profiles = dbClient.qualityProfileDao().selectOrderedByOrganizationUuid(dbSession, organization); @@ -252,20 +256,35 @@ public class OrganizationUpdaterImplTest { builtInQProfileRepositoryRule.initialize(); UserDto user = db.users().insertUser(); - underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION); + underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION, o -> { + }); OrganizationDto organization = dbClient.organizationDao().selectByKey(dbSession, FULL_POPULATED_NEW_ORGANIZATION.getKey()).get(); assertThat(dbClient.qualityGateDao().selectDefault(dbSession, organization).getUuid()).isEqualTo(builtInQualityGate.getUuid()); } @Test + public void create_calls_consumer() throws OrganizationUpdater.KeyConflictException { + UserDto user = db.users().insertUser(); + builtInQProfileRepositoryRule.initialize(); + db.qualityGates().insertBuiltInQualityGate(); + Boolean[] isConsumerCalled = new Boolean[]{false}; + + underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION, o -> { + isConsumerCalled[0] = true; + }); + + assertThat(isConsumerCalled[0]).isEqualTo(true); + } + + @Test public void create_throws_NPE_if_NewOrganization_arg_is_null() throws OrganizationUpdater.KeyConflictException { UserDto user = db.users().insertUser(); expectedException.expect(NullPointerException.class); expectedException.expectMessage("newOrganization can't be null"); - underTest.create(dbSession, user, null); + underTest.create(dbSession, user, null, EMPTY_ORGANIZATION_CONSUMER); } @Test @@ -307,7 +326,7 @@ public class OrganizationUpdaterImplTest { private void createThrowsExceptionThrownByOrganizationValidation(UserDto user) throws OrganizationUpdater.KeyConflictException { try { - underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION); + underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION, EMPTY_ORGANIZATION_CONSUMER); fail(exceptionThrownByOrganizationValidation + " should have been thrown"); } catch (IllegalArgumentException e) { assertThat(e).isSameAs(exceptionThrownByOrganizationValidation); @@ -322,7 +341,7 @@ public class OrganizationUpdaterImplTest { expectedException.expect(OrganizationUpdater.KeyConflictException.class); expectedException.expectMessage("Organization key '" + FULL_POPULATED_NEW_ORGANIZATION.getKey() + "' is already used"); - underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION); + underTest.create(dbSession, user, FULL_POPULATED_NEW_ORGANIZATION, EMPTY_ORGANIZATION_CONSUMER); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java index 362b1be4bdc..3fd6feef739 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/CreateActionTest.java @@ -50,6 +50,7 @@ import org.sonar.db.user.UserMembershipQuery; import org.sonar.server.es.EsTester; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.UnauthorizedException; +import org.sonar.server.organization.OrganizationAlmBinding; import org.sonar.server.organization.OrganizationUpdater; import org.sonar.server.organization.OrganizationUpdaterImpl; import org.sonar.server.organization.OrganizationValidation; @@ -72,7 +73,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.elasticsearch.index.query.QueryBuilders.boolQuery; import static org.elasticsearch.index.query.QueryBuilders.termQuery; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import static org.sonar.core.config.CorePropertyDefinitions.ORGANIZATIONS_ANYONE_CAN_CREATE; import static org.sonar.server.organization.ws.OrganizationsWsSupport.PARAM_NAME; import static org.sonar.server.organization.ws.OrganizationsWsTestSupport.STRING_257_CHARS_LONG; @@ -107,11 +112,12 @@ public class CreateActionTest { userIndexer, mock(BuiltInQProfileRepository.class), new DefaultGroupCreatorImpl(dbClient), permissionService); private TestOrganizationFlags organizationFlags = TestOrganizationFlags.standalone().setEnabled(true); + private OrganizationAlmBinding organizationAlmBinding = mock(OrganizationAlmBinding.class); private WsActionTester wsTester = new WsActionTester( new CreateAction(settings.asConfig(), userSession, dbClient, new OrganizationsWsSupport(organizationValidation), organizationValidation, - organizationUpdater, organizationFlags)); + organizationUpdater, organizationFlags, organizationAlmBinding)); @Test public void create_organization() { @@ -252,6 +258,47 @@ public class CreateActionTest { } @Test + public void bind_organization_when_installation_id_is_set() { + createUserAndLogInAsSystemAdministrator(); + db.qualityGates().insertBuiltInQualityGate(); + + wsTester.newRequest() + .setParam(PARAM_NAME, "foo") + .setParam("installationId", "ABCD") + .execute(); + + verify(organizationAlmBinding).bindOrganization(any(DbSession.class), any(OrganizationDto.class), eq("ABCD")); + } + + @Test + public void does_not_bind_organization_when_organizationAlmBinding_is_null() { + wsTester = new WsActionTester( + new CreateAction(settings.asConfig(), userSession, dbClient, new OrganizationsWsSupport(organizationValidation), + organizationValidation, organizationUpdater, organizationFlags, null)); + createUserAndLogInAsSystemAdministrator(); + db.qualityGates().insertBuiltInQualityGate(); + + wsTester.newRequest() + .setParam(PARAM_NAME, "foo") + .setParam("installationId", "ABCD") + .execute(); + + verifyZeroInteractions(organizationAlmBinding); + } + + @Test + public void does_not_bind_organization_when_installation_id_is_not_set() { + createUserAndLogInAsSystemAdministrator(); + db.qualityGates().insertBuiltInQualityGate(); + + wsTester.newRequest() + .setParam(PARAM_NAME, "foo") + .execute(); + + verifyZeroInteractions(organizationAlmBinding); + } + + @Test public void request_succeeds_if_user_is_system_administrator_and_logged_in_users_cannot_create_organizations() { createUserAndLogInAsSystemAdministrator(); db.qualityGates().insertBuiltInQualityGate(); @@ -520,7 +567,7 @@ public class CreateActionTest { assertThat(action.isInternal()).isTrue(); assertThat(action.since()).isEqualTo("6.2"); assertThat(action.handler()).isNotNull(); - assertThat(action.params()).hasSize(5); + assertThat(action.params()).hasSize(6); assertThat(action.responseExample()).isEqualTo(getClass().getResource("create-example.json")); assertThat(action.param("name")) @@ -547,6 +594,9 @@ public class CreateActionTest { .matches(param -> !param.isRequired()) .matches(param -> "https://www.foo.com/foo.png".equals(param.exampleValue())) .matches(param -> param.description() != null); + assertThat(action.param("installationId")) + .matches(param -> !param.isRequired()) + .matches(param -> param.isInternal()); } @Test diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java index 3d709dc4c99..3acddd3f63c 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/DeleteActionTest.java @@ -37,6 +37,8 @@ import org.sonar.core.util.UuidFactory; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; +import org.sonar.db.alm.ALM; +import org.sonar.db.alm.AlmAppInstallDto; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; import org.sonar.db.component.ResourceTypesRule; @@ -76,6 +78,8 @@ import org.sonar.server.ws.WsActionTester; import static com.google.common.collect.ImmutableList.of; import static java.util.Arrays.asList; import static java.util.Collections.emptySet; +import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic; +import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.fail; import static org.mockito.ArgumentMatchers.any; @@ -542,6 +546,21 @@ public class DeleteActionTest { verify(billingValidationsProxy).onDelete(any(BillingValidations.Organization.class)); } + @Test + public void delete_organization_alm_binding() { + OrganizationDto organization = db.organizations().insert(); + String installationId = randomAlphanumeric(10); + db.getDbClient().almAppInstallDao().insertOrUpdate(db.getSession(), ALM.GITHUB, randomAlphabetic(13), false, installationId); + AlmAppInstallDto almAppInstall = db.getDbClient().almAppInstallDao().selectByInstallationId(db.getSession(), ALM.GITHUB, installationId).get(); + db.getDbClient().organizationAlmBindingDao().insert(db.getSession(), organization, almAppInstall, randomAlphabetic(10), db.users().insertUser().getUuid()); + db.commit(); + logInAsAdministrator(organization); + + sendRequest(organization); + + assertThat(db.getDbClient().organizationAlmBindingDao().selectByOrganization(db.getSession(), organization)).isNotPresent(); + } + @DataProvider public static Object[][] indexOfFailingProjectDeletion() { return new Object[][] { diff --git a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchActionTest.java index 1f3db59c0d8..7d54fcb892a 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/organization/ws/SearchActionTest.java @@ -22,6 +22,7 @@ package org.sonar.server.organization.ws; import com.google.common.base.Joiner; import java.util.Arrays; import java.util.List; +import java.util.Map; import java.util.Random; import javax.annotation.Nullable; import org.junit.Rule; @@ -31,11 +32,12 @@ import org.sonar.api.server.ws.WebService; import org.sonar.api.utils.System2; import org.sonar.core.util.Uuids; import org.sonar.db.DbTester; +import org.sonar.db.alm.AlmAppInstallDto; +import org.sonar.db.alm.OrganizationAlmBindingDto; import org.sonar.db.organization.OrganizationDto; import org.sonar.db.user.GroupDto; import org.sonar.db.user.UserDto; import org.sonar.server.exceptions.UnauthorizedException; -import org.sonar.server.organization.OrganizationValidationImpl; import org.sonar.server.tester.UserSessionRule; import org.sonar.server.ws.TestRequest; import org.sonar.server.ws.WsActionTester; @@ -45,6 +47,8 @@ import org.sonarqube.ws.Organizations.Organization; import org.sonarqube.ws.Organizations.SearchWsResponse; import static java.lang.String.valueOf; +import static java.util.function.Function.identity; +import static java.util.stream.Collectors.toMap; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.tuple; import static org.mockito.Mockito.mock; @@ -68,7 +72,7 @@ public class SearchActionTest { @Rule public ExpectedException expectedException = ExpectedException.none(); - private SearchAction underTest = new SearchAction(db.getDbClient(), userSession, new OrganizationsWsSupport(new OrganizationValidationImpl())); + private SearchAction underTest = new SearchAction(db.getDbClient(), userSession); private WsActionTester ws = new WsActionTester(underTest); @Test @@ -213,6 +217,21 @@ public class SearchActionTest { } @Test + public void return_alm_info() { + OrganizationDto organization = db.organizations().insert(); + AlmAppInstallDto almAppInstall = db.alm().insertAlmAppInstall(); + OrganizationAlmBindingDto organizationAlmBinding = db.alm().insertOrganizationAlmBinding(organization, almAppInstall); + OrganizationDto organizationNotBoundToAlm = db.organizations().insert(); + + SearchWsResponse result = call(ws.newRequest()); + + Map<String, Organization> orgByKey = result.getOrganizationsList().stream().collect(toMap(Organization::getKey, identity())); + assertThat(orgByKey.get(organization.getKey()).getAlm().getKey()).isEqualTo(organizationAlmBinding.getAlm().getId()); + assertThat(orgByKey.get(organization.getKey()).getAlm().getUrl()).isEqualTo(organizationAlmBinding.getUrl()); + assertThat(orgByKey.get(organizationNotBoundToAlm.getKey()).hasAlm()).isEqualTo(false); + } + + @Test public void request_on_empty_db_returns_an_empty_organization_list() { assertThat(executeRequestAndReturnList(null, null)).isEmpty(); assertThat(executeRequestAndReturnList(null, 1)).isEmpty(); diff --git a/server/sonar-server/src/test/java/org/sonar/server/ui/ws/ComponentActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/ui/ws/ComponentActionTest.java index 9edc7b47337..19e539c36df 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/ui/ws/ComponentActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/ui/ws/ComponentActionTest.java @@ -606,7 +606,19 @@ public class ComponentActionTest { userSession.addProjectPermission(UserRole.USER, project); init(); - executeAndVerify(project.getDbKey(), "return_alm_infos_on_project.json"); + String json = execute(project.getKey()); + + assertJson(json).isSimilarTo("{\n" + + " \"organization\": \"my-org\",\n" + + " \"key\": \"polop\",\n" + + " \"id\": \"abcd\",\n" + + " \"name\": \"Polop\",\n" + + " \"description\": \"test project\",\n" + + " \"alm\": {\n" + + " \"key\": \"bitbucketcloud\",\n" + + " \"url\": \"http://bitbucket.org/foo/bar\"\n" + + " }\n" + + "}\n"); } @Test @@ -618,7 +630,18 @@ public class ComponentActionTest { userSession.addProjectPermission(UserRole.USER, project); init(); - executeAndVerify(module.getDbKey(), "return_alm_infos_on_module.json"); + String json = execute(module.getKey()); + + assertJson(json).isSimilarTo("{\n" + + " \"organization\": \"my-org\",\n" + + " \"key\": \"palap\",\n" + + " \"id\": \"bcde\",\n" + + " \"name\": \"Palap\",\n" + + " \"alm\": {\n" + + " \"key\": \"bitbucketcloud\",\n" + + " \"url\": \"http://bitbucket.org/foo/bar\"\n" + + " }\n" + + "}\n"); } @Test @@ -630,11 +653,24 @@ public class ComponentActionTest { userSession.addProjectPermission(UserRole.USER, project); init(); - verify(ws.newRequest() - .setParam("componentKey", project.getDbKey()) + String json = ws.newRequest() + .setParam("componentKey", project.getKey()) .setParam("branch", branch.getBranch()) .execute() - .getInput(), "return_alm_infos_on_branch.json"); + .getInput(); + + assertJson(json).isSimilarTo("{\n" + + " \"organization\": \"my-org\",\n" + + " \"key\": \"polop\",\n" + + " \"id\": \"xyz\",\n" + + " \"branch\": \"feature1\"," + + " \"name\": \"Polop\",\n" + + " \"description\": \"test project\",\n" + + " \"alm\": {\n" + + " \"key\": \"bitbucketcloud\",\n" + + " \"url\": \"http://bitbucket.org/foo/bar\"\n" + + " }\n" + + "}\n"); } private ComponentDto insertOrganizationAndProject() { diff --git a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentActionTest/return_alm_infos_on_branch.json b/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentActionTest/return_alm_infos_on_branch.json deleted file mode 100644 index 968b1094a5c..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentActionTest/return_alm_infos_on_branch.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "organization": "my-org", - "key": "polop", - "id": "xyz", - "branch": "feature1", - "name": "Polop", - "description": "test project", - "almId": "bitbucketcloud", - "almRepoUrl": "http://bitbucket.org/foo/bar" -} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentActionTest/return_alm_infos_on_module.json b/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentActionTest/return_alm_infos_on_module.json deleted file mode 100644 index 0078c9f13c4..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentActionTest/return_alm_infos_on_module.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "organization": "my-org", - "key": "palap", - "id": "bcde", - "name": "Palap", - "almId": "bitbucketcloud", - "almRepoUrl": "http://bitbucket.org/foo/bar" -} diff --git a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentActionTest/return_alm_infos_on_project.json b/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentActionTest/return_alm_infos_on_project.json deleted file mode 100644 index 6ac04350a84..00000000000 --- a/server/sonar-server/src/test/resources/org/sonar/server/ui/ws/ComponentActionTest/return_alm_infos_on_project.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "organization": "my-org", - "key": "polop", - "id": "abcd", - "name": "Polop", - "description": "test project", - "almId": "bitbucketcloud", - "almRepoUrl": "http://bitbucket.org/foo/bar" -} diff --git a/sonar-ws/src/main/protobuf/ws-organizations.proto b/sonar-ws/src/main/protobuf/ws-organizations.proto index 172bf31d85c..1d729266ddc 100644 --- a/sonar-ws/src/main/protobuf/ws-organizations.proto +++ b/sonar-ws/src/main/protobuf/ws-organizations.proto @@ -61,6 +61,12 @@ message Organization { optional string avatar = 5; optional bool guarded = 6; optional bool isAdmin = 7; + optional Alm alm = 8; + + message Alm { + optional string key = 1; + optional string url = 2; + } } message User { |