From 4fc7e70ddb24c863a2686b954676790adf071aa7 Mon Sep 17 00:00:00 2001 From: Julien Lancelot Date: Mon, 23 Jan 2017 14:54:39 +0100 Subject: [PATCH] SONAR-7299 Move code related to project creation to ComponentUpdater --- .../server/component/ComponentService.java | 96 +---- .../server/component/ComponentUpdater.java | 151 ++++++++ .../DefaultRubyComponentService.java | 57 +-- .../sonar/server/component/NewComponent.java | 3 +- .../computation/queue/ReportSubmitter.java | 23 +- .../platformlevel/PlatformLevel4.java | 4 +- .../sonar/server/project/ws/CreateAction.java | 33 +- .../component/ComponentServiceTest.java | 247 +------------ .../ComponentServiceUpdateKeyTest.java | 11 +- .../component/ComponentUpdaterTest.java | 333 ++++++++++++++++++ .../DefaultRubyComponentServiceTest.java | 35 +- .../component/ws/BulkUpdateKeyActionTest.java | 2 +- .../queue/ReportSubmitterTest.java | 16 +- .../server/project/ws/CreateActionTest.java | 8 +- 14 files changed, 563 insertions(+), 456 deletions(-) create mode 100644 server/sonar-server/src/main/java/org/sonar/server/component/ComponentUpdater.java create mode 100644 server/sonar-server/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java index f525fb70326e..6a01a8414732 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentService.java @@ -23,24 +23,16 @@ import com.google.common.collect.Collections2; import com.google.common.collect.Sets; import java.util.Collection; -import java.util.Date; import java.util.List; -import java.util.Locale; import java.util.Set; import javax.annotation.Nullable; import org.sonar.api.ce.ComputeEngineSide; -import org.sonar.api.i18n.I18n; -import org.sonar.api.resources.Scopes; import org.sonar.api.server.ServerSide; -import org.sonar.api.utils.System2; import org.sonar.api.web.UserRole; -import org.sonar.core.component.ComponentKeys; -import org.sonar.core.util.Uuids; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; import org.sonar.server.component.index.ComponentIndexer; -import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.measure.index.ProjectMeasuresIndexer; import org.sonar.server.user.UserSession; @@ -55,22 +47,19 @@ @ComputeEngineSide public class ComponentService { private final DbClient dbClient; - private final I18n i18n; private final UserSession userSession; - private final System2 system2; private final ProjectMeasuresIndexer projectMeasuresIndexer; private final ComponentIndexer componentIndexer; - public ComponentService(DbClient dbClient, I18n i18n, UserSession userSession, System2 system2, ProjectMeasuresIndexer projectMeasuresIndexer, + public ComponentService(DbClient dbClient, UserSession userSession, ProjectMeasuresIndexer projectMeasuresIndexer, ComponentIndexer componentIndexer) { this.dbClient = dbClient; - this.i18n = i18n; this.userSession = userSession; - this.system2 = system2; this.projectMeasuresIndexer = projectMeasuresIndexer; this.componentIndexer = componentIndexer; } + // TODO should be moved to ComponentUpdater public void updateKey(DbSession dbSession, ComponentDto component, String newKey) { userSession.checkComponentUuidPermission(UserRole.ADMIN, component.projectUuid()); checkIsProjectOrModule(component); @@ -80,76 +69,25 @@ public void updateKey(DbSession dbSession, ComponentDto component, String newKey index(component.uuid()); } + // TODO should be moved to ComponentUpdater public void bulkUpdateKey(DbSession dbSession, String projectUuid, String stringToReplace, String replacementString) { dbClient.componentKeyUpdaterDao().bulkUpdateKey(dbSession, projectUuid, stringToReplace, replacementString); dbSession.commit(); index(projectUuid); } - // Used by SQ and Governance - public ComponentDto create(DbSession session, NewComponent newComponent) { - checkKeyFormat(newComponent.qualifier(), newComponent.key()); - ComponentDto rootComponent = createRootComponent(session, newComponent); - removeDuplicatedProjects(session, rootComponent.getKey()); - index(rootComponent.uuid()); - return rootComponent; - } - private void index(String projectUuid) { projectMeasuresIndexer.index(projectUuid); componentIndexer.indexByProjectUuid(projectUuid); } - /** - * No permission check must be done when inserting a new developer as it's done on Compute Engine side. - * No check must be done on the key - * No need to remove duplicated components as it's not possible to create the same developer twice in the same time. - */ - public ComponentDto createDeveloper(DbSession session, NewComponent newComponent) { - return createRootComponent(session, newComponent); - } - - private ComponentDto createRootComponent(DbSession session, NewComponent newComponent) { - checkBranchFormat(newComponent.qualifier(), newComponent.branch()); - String keyWithBranch = ComponentKeys.createKey(newComponent.key(), newComponent.branch()); - if (dbClient.componentDao().selectByKey(session, keyWithBranch).isPresent()) { - throw new BadRequestException(formatMessage("Could not create %s, key already exists: %s", newComponent.qualifier(), keyWithBranch)); - } - - String uuid = Uuids.create(); - ComponentDto component = new ComponentDto() - .setOrganizationUuid(newComponent.getOrganizationUuid()) - .setUuid(uuid) - .setUuidPath(ComponentDto.UUID_PATH_OF_ROOT) - .setRootUuid(uuid) - .setModuleUuid(null) - .setModuleUuidPath(ComponentDto.UUID_PATH_SEPARATOR + uuid + ComponentDto.UUID_PATH_SEPARATOR) - .setProjectUuid(uuid) - .setKey(keyWithBranch) - .setDeprecatedKey(keyWithBranch) - .setName(newComponent.name()) - .setLongName(newComponent.name()) - .setScope(Scopes.PROJECT) - .setQualifier(newComponent.qualifier()) - .setCreatedAt(new Date(system2.now())); - dbClient.componentDao().insert(session, component); - dbClient.componentIndexDao().indexResource(session, component.uuid()); - session.commit(); - return component; - } - - /** - * On MySQL, as PROJECTS.KEE is not unique, if the same project is provisioned multiple times, then it will be duplicated in the database. - * So, after creating a project, we commit, and we search in the db if their are some duplications and we remove them. - * - * SONAR-6332 - */ - private void removeDuplicatedProjects(DbSession session, String projectKey) { - List duplicated = dbClient.componentDao().selectComponentsHavingSameKeyOrderedById(session, projectKey); - for (int i = 1; i < duplicated.size(); i++) { - dbClient.componentDao().delete(session, duplicated.get(i).getId()); + public Collection componentUuids(@Nullable Collection componentKeys) { + DbSession session = dbClient.openSession(false); + try { + return componentUuids(session, componentKeys, false); + } finally { + dbClient.closeSession(session); } - session.commit(); } public Collection componentUuids(DbSession session, @Nullable Collection componentKeys, boolean ignoreMissingComponents) { @@ -196,24 +134,8 @@ public Collection getByUuids(DbSession session, @Nullable Collecti return directoryPaths; } - private void checkKeyFormat(String qualifier, String kee) { - checkRequest(isValidModuleKey(kee), formatMessage("Malformed key for %s: %s. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", - qualifier, kee)); - } - private static void checkProjectOrModuleKeyFormat(String key) { checkRequest(isValidModuleKey(key), "Malformed key for '%s'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", key); } - private void checkBranchFormat(String qualifier, @Nullable String branch) { - if (branch != null && !ComponentKeys.isValidBranch(branch)) { - throw new BadRequestException(formatMessage("Malformed branch for %s: %s. Allowed characters are alphanumeric, '-', '_', '.' and '/', with at least one non-digit.", - qualifier, branch)); - } - } - - private String formatMessage(String message, String qualifier, String key) { - return String.format(message, i18n.message(Locale.getDefault(), "qualifier." + qualifier, "Project"), key); - } - } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/ComponentUpdater.java b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentUpdater.java new file mode 100644 index 000000000000..9c4d3293054a --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/component/ComponentUpdater.java @@ -0,0 +1,151 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.component; + +import java.util.Date; +import java.util.List; +import java.util.Locale; +import javax.annotation.Nullable; +import org.sonar.api.i18n.I18n; +import org.sonar.api.resources.Scopes; +import org.sonar.api.utils.System2; +import org.sonar.core.component.ComponentKeys; +import org.sonar.core.util.Uuids; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.component.ComponentDto; +import org.sonar.server.component.index.ComponentIndexer; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.favorite.FavoriteUpdater; +import org.sonar.server.measure.index.ProjectMeasuresIndexer; +import org.sonar.server.permission.PermissionTemplateService; + +import static org.sonar.api.resources.Qualifiers.PROJECT; +import static org.sonar.core.component.ComponentKeys.isValidModuleKey; +import static org.sonar.server.ws.WsUtils.checkRequest; + +public class ComponentUpdater { + + private final DbClient dbClient; + private final I18n i18n; + private final System2 system2; + private final PermissionTemplateService permissionTemplateService; + private final FavoriteUpdater favoriteUpdater; + private final ProjectMeasuresIndexer projectMeasuresIndexer; + private final ComponentIndexer componentIndexer; + + public ComponentUpdater(DbClient dbClient, I18n i18n, System2 system2, + PermissionTemplateService permissionTemplateService, FavoriteUpdater favoriteUpdater, + ProjectMeasuresIndexer projectMeasuresIndexer, ComponentIndexer componentIndexer) { + this.dbClient = dbClient; + this.i18n = i18n; + this.system2 = system2; + this.projectMeasuresIndexer = projectMeasuresIndexer; + this.componentIndexer = componentIndexer; + this.permissionTemplateService = permissionTemplateService; + this.favoriteUpdater = favoriteUpdater; + } + + /** + * - Create component + * - Apply default permission template + * - Add component to favorite if the component has the 'Project Creators' permission + * - Index component if es indexes + */ + public ComponentDto create(DbSession dbSession, NewComponent newComponent, @Nullable Long userId) { + checkKeyFormat(newComponent.qualifier(), newComponent.key()); + ComponentDto componentDto = createRootComponent(dbSession, newComponent); + removeDuplicatedProjects(dbSession, componentDto.getKey()); + handlePermissionTemplate(dbSession, componentDto, newComponent.getOrganizationUuid(), userId); + dbSession.commit(); + index(componentDto.uuid()); + return componentDto; + } + + private ComponentDto createRootComponent(DbSession session, NewComponent newComponent) { + checkBranchFormat(newComponent.qualifier(), newComponent.branch()); + String keyWithBranch = ComponentKeys.createKey(newComponent.key(), newComponent.branch()); + if (dbClient.componentDao().selectByKey(session, keyWithBranch).isPresent()) { + throw new BadRequestException(formatMessage("Could not create %s, key already exists: %s", newComponent.qualifier(), keyWithBranch)); + } + + String uuid = Uuids.create(); + ComponentDto component = new ComponentDto() + .setOrganizationUuid(newComponent.getOrganizationUuid()) + .setUuid(uuid) + .setUuidPath(ComponentDto.UUID_PATH_OF_ROOT) + .setRootUuid(uuid) + .setModuleUuid(null) + .setModuleUuidPath(ComponentDto.UUID_PATH_SEPARATOR + uuid + ComponentDto.UUID_PATH_SEPARATOR) + .setProjectUuid(uuid) + .setKey(keyWithBranch) + .setDeprecatedKey(keyWithBranch) + .setName(newComponent.name()) + .setLongName(newComponent.name()) + .setScope(Scopes.PROJECT) + .setQualifier(newComponent.qualifier()) + .setCreatedAt(new Date(system2.now())); + dbClient.componentDao().insert(session, component); + dbClient.componentIndexDao().indexResource(session, component.uuid()); + return component; + } + + /** + * On MySQL, as PROJECTS.KEE is not unique, if the same project is provisioned multiple times, then it will be duplicated in the database. + * So, after creating a project, we commit, and we search in the db if their are some duplications and we remove them. + * + * SONAR-6332 + */ + private void removeDuplicatedProjects(DbSession session, String projectKey) { + List duplicated = dbClient.componentDao().selectComponentsHavingSameKeyOrderedById(session, projectKey); + for (int i = 1; i < duplicated.size(); i++) { + dbClient.componentDao().delete(session, duplicated.get(i).getId()); + } + } + + private void handlePermissionTemplate(DbSession dbSession, ComponentDto componentDto, String organizationUuid, @Nullable Long userId) { + permissionTemplateService.applyDefault(dbSession, organizationUuid, componentDto, userId); + if (componentDto.qualifier().equals(PROJECT) && permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(dbSession, organizationUuid, componentDto)) { + favoriteUpdater.add(dbSession, componentDto, userId); + } + } + + private void checkKeyFormat(String qualifier, String kee) { + checkRequest(isValidModuleKey(kee), formatMessage("Malformed key for %s: %s. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", + qualifier, kee)); + } + + private void checkBranchFormat(String qualifier, @Nullable String branch) { + if (branch != null && !ComponentKeys.isValidBranch(branch)) { + throw new BadRequestException(formatMessage("Malformed branch for %s: %s. Allowed characters are alphanumeric, '-', '_', '.' and '/', with at least one non-digit.", + qualifier, branch)); + } + } + + private String formatMessage(String message, String qualifier, String key) { + return String.format(message, i18n.message(Locale.getDefault(), "qualifier." + qualifier, "Project"), key); + } + + private void index(String projectUuid) { + projectMeasuresIndexer.index(projectUuid); + componentIndexer.indexByProjectUuid(projectUuid); + } +} diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/DefaultRubyComponentService.java b/server/sonar-server/src/main/java/org/sonar/server/component/DefaultRubyComponentService.java index f1cbd928f1b6..d95404ec1157 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/DefaultRubyComponentService.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/DefaultRubyComponentService.java @@ -19,15 +19,10 @@ */ package org.sonar.server.component; -import javax.annotation.CheckForNull; -import javax.annotation.Nullable; -import org.sonar.api.resources.Qualifiers; import org.sonar.db.DbClient; import org.sonar.db.DbSession; -import org.sonar.db.component.ComponentDto; -import org.sonar.server.favorite.FavoriteUpdater; import org.sonar.server.organization.DefaultOrganizationProvider; -import org.sonar.server.permission.PermissionTemplateService; +import org.sonar.server.user.UserSession; import static org.sonar.server.component.NewComponent.newComponentBuilder; @@ -36,50 +31,36 @@ */ public class DefaultRubyComponentService { + private final UserSession userSession; private final DbClient dbClient; - private final ComponentService componentService; - private final PermissionTemplateService permissionTemplateService; - private final FavoriteUpdater favoriteUpdater; + private final ComponentUpdater componentUpdater; private final DefaultOrganizationProvider defaultOrganizationProvider; - public DefaultRubyComponentService(DbClient dbClient, ComponentService componentService, - PermissionTemplateService permissionTemplateService, FavoriteUpdater favoriteUpdater, + public DefaultRubyComponentService(UserSession userSession, DbClient dbClient, ComponentUpdater componentUpdater, DefaultOrganizationProvider defaultOrganizationProvider) { + this.userSession = userSession; this.dbClient = dbClient; - this.componentService = componentService; - this.permissionTemplateService = permissionTemplateService; - this.favoriteUpdater = favoriteUpdater; + this.componentUpdater = componentUpdater; this.defaultOrganizationProvider = defaultOrganizationProvider; } // Used in GOV - @CheckForNull + /** + * @deprecated Use {@link ComponentUpdater#create(DbSession, NewComponent, Long)} instead + */ + @Deprecated public Long createComponent(String key, String name, String qualifier) { try (DbSession dbSession = dbClient.openSession(false)) { - return createComponent(dbSession, key, null, name, qualifier); + return componentUpdater.create( + dbSession, + newComponentBuilder() + .setOrganizationUuid(defaultOrganizationProvider.get().getUuid()) + .setKey(key) + .setName(name) + .setQualifier(qualifier) + .build(), + userSession.isLoggedIn() ? userSession.getUserId().longValue() : null).getId(); } } - public long createComponent(DbSession dbSession, String key, @Nullable String branch, String name, @Nullable String qualifier) { - ComponentDto provisionedComponent = componentService.create( - dbSession, - newComponentBuilder() - .setOrganizationUuid(defaultOrganizationProvider.get().getUuid()) - .setKey(key) - .setName(name) - .setQualifier(qualifier) - .setBranch(branch) - .build()); - String organizationUuid = defaultOrganizationProvider.get().getUuid(); - permissionTemplateService.applyDefaultPermissionTemplate(dbSession, organizationUuid, provisionedComponent.getKey()); - if (Qualifiers.PROJECT.equals(provisionedComponent.qualifier()) - && permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(dbSession, organizationUuid, provisionedComponent)) { - // TODO Set userId as null as this method will be removed in a next commit - favoriteUpdater.add(dbSession, provisionedComponent, null); - dbSession.commit(); - } - - return provisionedComponent.getId(); - } - } diff --git a/server/sonar-server/src/main/java/org/sonar/server/component/NewComponent.java b/server/sonar-server/src/main/java/org/sonar/server/component/NewComponent.java index d9e514413961..29da48b50c2d 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/component/NewComponent.java +++ b/server/sonar-server/src/main/java/org/sonar/server/component/NewComponent.java @@ -20,6 +20,7 @@ package org.sonar.server.component; import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import javax.annotation.concurrent.Immutable; import org.sonar.api.resources.Qualifiers; @@ -90,7 +91,7 @@ public Builder setKey(String key) { return this; } - public Builder setBranch(String branch) { + public Builder setBranch(@Nullable String branch) { this.branch = branch; return this; } diff --git a/server/sonar-server/src/main/java/org/sonar/server/computation/queue/ReportSubmitter.java b/server/sonar-server/src/main/java/org/sonar/server/computation/queue/ReportSubmitter.java index 42186f3f891d..fe3170cfc739 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/computation/queue/ReportSubmitter.java +++ b/server/sonar-server/src/main/java/org/sonar/server/computation/queue/ReportSubmitter.java @@ -34,10 +34,9 @@ import org.sonar.db.ce.CeTaskTypes; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; -import org.sonar.server.component.ComponentService; +import org.sonar.server.component.ComponentUpdater; import org.sonar.server.component.NewComponent; import org.sonar.server.exceptions.NotFoundException; -import org.sonar.server.favorite.FavoriteUpdater; import org.sonar.server.permission.PermissionTemplateService; import org.sonar.server.user.UserSession; @@ -53,19 +52,17 @@ public class ReportSubmitter { private final CeQueue queue; private final UserSession userSession; - private final ComponentService componentService; + private final ComponentUpdater componentUpdater; private final PermissionTemplateService permissionTemplateService; private final DbClient dbClient; - private final FavoriteUpdater favoriteUpdater; - public ReportSubmitter(CeQueue queue, UserSession userSession, ComponentService componentService, - PermissionTemplateService permissionTemplateService, DbClient dbClient, FavoriteUpdater favoriteUpdater) { + public ReportSubmitter(CeQueue queue, UserSession userSession, ComponentUpdater componentUpdater, + PermissionTemplateService permissionTemplateService, DbClient dbClient) { this.queue = queue; this.userSession = userSession; - this.componentService = componentService; + this.componentUpdater = componentUpdater; this.permissionTemplateService = permissionTemplateService; this.dbClient = dbClient; - this.favoriteUpdater = favoriteUpdater; } /** @@ -115,15 +112,7 @@ private ComponentDto createProject(DbSession dbSession, String organizationUuid, .setBranch(projectBranch) .setQualifier(Qualifiers.PROJECT) .build(); - ComponentDto project = componentService.create(dbSession, newProject); - if (permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(dbSession, organizationUuid, project)) { - favoriteUpdater.add(dbSession, project, projectCreatorUserId); - dbSession.commit(); - } - - permissionTemplateService.applyDefault(dbSession, organizationUuid, project, projectCreatorUserId); - - return project; + return componentUpdater.create(dbSession, newProject, projectCreatorUserId); } private CeTask submitReport(DbSession dbSession, InputStream reportInput, ComponentDto project) { diff --git a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java index 48008257cbb6..a5dc9695fa16 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java +++ b/server/sonar-server/src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel4.java @@ -38,7 +38,7 @@ import org.sonar.server.component.ComponentCleanerService; import org.sonar.server.component.ComponentFinder; import org.sonar.server.component.ComponentService; -import org.sonar.server.component.DefaultRubyComponentService; +import org.sonar.server.component.ComponentUpdater; import org.sonar.server.component.index.ComponentIndex; import org.sonar.server.component.index.ComponentIndexDefinition; import org.sonar.server.component.index.ComponentIndexer; @@ -359,8 +359,8 @@ protected void configureLevel() { ProjectsWsModule.class, ProjectsEsModule.class, ComponentsWsModule.class, - DefaultRubyComponentService.class, ComponentService.class, + ComponentUpdater.class, ComponentFinder.class, NewAlerts.class, NewAlerts.newMetadata(), diff --git a/server/sonar-server/src/main/java/org/sonar/server/project/ws/CreateAction.java b/server/sonar-server/src/main/java/org/sonar/server/project/ws/CreateAction.java index b661f8d11f87..b3eaa57b7f7c 100644 --- a/server/sonar-server/src/main/java/org/sonar/server/project/ws/CreateAction.java +++ b/server/sonar-server/src/main/java/org/sonar/server/project/ws/CreateAction.java @@ -25,10 +25,8 @@ import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.component.ComponentDto; -import org.sonar.server.component.ComponentService; -import org.sonar.server.favorite.FavoriteUpdater; +import org.sonar.server.component.ComponentUpdater; import org.sonar.server.organization.DefaultOrganizationProvider; -import org.sonar.server.permission.PermissionTemplateService; import org.sonar.server.user.UserSession; import org.sonarqube.ws.WsProjects.CreateWsResponse; import org.sonarqube.ws.client.project.CreateRequest; @@ -49,18 +47,13 @@ public class CreateAction implements ProjectsWsAction { private final DbClient dbClient; private final UserSession userSession; - private final ComponentService componentService; + private final ComponentUpdater componentUpdater; private final DefaultOrganizationProvider defaultOrganizationProvider; - private final PermissionTemplateService permissionTemplateService; - private final FavoriteUpdater favoriteUpdater; - public CreateAction(DbClient dbClient, UserSession userSession, ComponentService componentService, PermissionTemplateService permissionTemplateService, - FavoriteUpdater favoriteUpdater, DefaultOrganizationProvider defaultOrganizationProvider) { + public CreateAction(DbClient dbClient, UserSession userSession, ComponentUpdater componentUpdater, DefaultOrganizationProvider defaultOrganizationProvider) { this.dbClient = dbClient; this.userSession = userSession; - this.componentService = componentService; - this.permissionTemplateService = permissionTemplateService; - this.favoriteUpdater = favoriteUpdater; + this.componentUpdater = componentUpdater; this.defaultOrganizationProvider = defaultOrganizationProvider; } @@ -99,29 +92,19 @@ public void handle(Request request, Response response) throws Exception { } private CreateWsResponse doHandle(CreateRequest request) { - String organizationUuid = defaultOrganizationProvider.get().getUuid(); try (DbSession dbSession = dbClient.openSession(false)) { - ComponentDto componentDto = componentService.create(dbSession, newComponentBuilder() - .setOrganizationUuid(organizationUuid) + ComponentDto componentDto = componentUpdater.create(dbSession, newComponentBuilder() + .setOrganizationUuid(defaultOrganizationProvider.get().getUuid()) .setKey(request.getKey()) .setName(request.getName()) .setBranch(request.getBranch()) .setQualifier(PROJECT) - .build()); - handlePermissionTemplate(dbSession, componentDto, organizationUuid); + .build(), + userSession.isLoggedIn() ? userSession.getUserId().longValue() : null); return toCreateResponse(componentDto); } } - private void handlePermissionTemplate(DbSession dbSession, ComponentDto componentDto, String organizationUuid) { - Long userId = userSession.isLoggedIn() ? userSession.getUserId().longValue() : null; - permissionTemplateService.applyDefault(dbSession, organizationUuid, componentDto, userId); - if (permissionTemplateService.hasDefaultTemplateWithPermissionOnProjectCreator(dbSession, organizationUuid, componentDto)) { - favoriteUpdater.add(dbSession, componentDto, userId); - dbSession.commit(); - } - } - private static CreateRequest toCreateRequest(Request request) { return CreateRequest.builder() .setKey(request.mandatoryParam(PARAM_PROJECT)) diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java index 6722c0350a72..7a587713b0da 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceTest.java @@ -19,54 +19,38 @@ */ package org.sonar.server.component; -import com.google.common.base.Optional; import java.util.Arrays; import org.junit.Before; +import org.assertj.core.api.Fail; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.api.config.MapSettings; -import org.sonar.api.resources.Qualifiers; import org.sonar.api.utils.System2; import org.sonar.db.DbClient; import org.sonar.db.DbSession; import org.sonar.db.DbTester; -import org.sonar.db.component.ComponentDao; import org.sonar.db.component.ComponentDbTester; import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; -import org.sonar.db.component.ResourceIndexDao; -import org.sonar.db.organization.OrganizationDto; import org.sonar.server.component.index.ComponentIndexDefinition; import org.sonar.server.component.index.ComponentIndexer; import org.sonar.server.es.EsTester; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.i18n.I18nRule; +import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.measure.index.ProjectMeasuresIndexDefinition; import org.sonar.server.measure.index.ProjectMeasuresIndexer; import org.sonar.server.tester.UserSessionRule; -import static com.google.common.collect.Lists.newArrayList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.guava.api.Assertions.assertThat; -import static org.mockito.Matchers.any; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; -import static org.sonar.core.permission.GlobalPermissions.PROVISIONING; import static org.sonar.db.component.ComponentTesting.newFileDto; import static org.sonar.db.component.ComponentTesting.newModuleDto; import static org.sonar.db.component.ComponentTesting.newProjectDto; -import static org.sonar.server.component.NewComponent.newComponentBuilder; -import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.INDEX_PROJECT_MEASURES; -import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.TYPE_PROJECT_MEASURE; public class ComponentServiceTest { - private System2 system2 = System2.INSTANCE; - @Rule public UserSessionRule userSession = UserSessionRule.standalone(); @Rule @@ -80,225 +64,10 @@ public class ComponentServiceTest { private ComponentDbTester componentDb = new ComponentDbTester(dbTester); private DbClient dbClient = dbTester.getDbClient(); private DbSession dbSession = dbTester.getSession(); - private I18nRule i18n = new I18nRule(); - private ProjectMeasuresIndexer projectMeasuresIndexer = new ProjectMeasuresIndexer(system2, dbClient, es.client()); + private ProjectMeasuresIndexer projectMeasuresIndexer = new ProjectMeasuresIndexer(System2.INSTANCE, dbClient, es.client()); private ComponentIndexer componentIndexer = new ComponentIndexer(dbClient, es.client()); - private OrganizationDto organization; - - private ComponentService underTest; - - @Before - public void setUp() { - i18n.put("qualifier.TRK", "Project"); - - underTest = new ComponentService(dbClient, i18n, userSession, system2, projectMeasuresIndexer, componentIndexer); - organization = dbTester.organizations().insert(); - } - - @Test - public void create_project() { - userSession.login("john").setGlobalPermissions(PROVISIONING); - - String key = underTest.create( - dbSession, - newComponentBuilder() - .setOrganizationUuid(organization.getUuid()) - .setKey("struts") - .setName("Struts project") - .build()) - .getKey(); - - ComponentDto project = dbTester.getDbClient().componentDao().selectOrFailByKey(dbSession, key); - assertThat(project.getOrganizationUuid()).isEqualTo(organization.getUuid()); - assertThat(project.key()).isEqualTo("struts"); - assertThat(project.deprecatedKey()).isEqualTo("struts"); - assertThat(project.uuid()).isNotNull(); - assertThat(project.projectUuid()).isEqualTo(project.uuid()); - assertThat(project.moduleUuid()).isNull(); - assertThat(project.moduleUuidPath()).isEqualTo("." + project.uuid() + "."); - assertThat(project.name()).isEqualTo("Struts project"); - assertThat(project.longName()).isEqualTo("Struts project"); - assertThat(project.scope()).isEqualTo("PRJ"); - assertThat(project.qualifier()).isEqualTo("TRK"); - assertThat(project.getCreatedAt()).isNotNull(); - - assertProjectIsInIndex(project.uuid()); - } - - @Test - public void create_new_project_with_branch() { - userSession.login("john").setGlobalPermissions(PROVISIONING); - - String key = underTest.create( - dbSession, - newComponentBuilder() - .setOrganizationUuid(organization.getUuid()) - .setKey("struts") - .setName("Struts project") - .setBranch("origin/branch") - .build()) - .getKey(); - - ComponentDto project = dbTester.getDbClient().componentDao().selectOrFailByKey(dbSession, key); - assertThat(project.getOrganizationUuid()).isEqualTo(organization.getUuid()); - assertThat(project.key()).isEqualTo("struts:origin/branch"); - assertThat(project.deprecatedKey()).isEqualTo("struts:origin/branch"); - } - - @Test - public void create_view() { - userSession.login("john").setGlobalPermissions(PROVISIONING); - - String key = underTest.create( - dbSession, - newComponentBuilder() - .setOrganizationUuid(organization.getUuid()) - .setKey("all-project") - .setName("All Projects") - .setQualifier(Qualifiers.VIEW) - .build()) - .getKey(); - - ComponentDto project = dbTester.getDbClient().componentDao().selectOrFailByKey(dbSession, key); - assertThat(project.getOrganizationUuid()).isEqualTo(organization.getUuid()); - assertThat(project.key()).isEqualTo("all-project"); - assertThat(project.deprecatedKey()).isEqualTo("all-project"); - assertThat(project.uuid()).isNotNull(); - assertThat(project.projectUuid()).isEqualTo(project.uuid()); - assertThat(project.moduleUuid()).isNull(); - assertThat(project.moduleUuidPath()).isEqualTo("." + project.uuid() + "."); - assertThat(project.name()).isEqualTo("All Projects"); - assertThat(project.longName()).isEqualTo("All Projects"); - assertThat(project.scope()).isEqualTo("PRJ"); - assertThat(project.qualifier()).isEqualTo("VW"); - assertThat(project.getCreatedAt()).isNotNull(); - - assertIndexIsEmpty(); - } - - @Test - public void create_developer() throws Exception { - // No permission should be required to create a developer - userSession.anonymous(); - - String key = underTest.createDeveloper( - dbSession, - newComponentBuilder() - .setOrganizationUuid(organization.getUuid()) - .setKey("DEV:jon.name@mail.com") - .setName("John") - .setQualifier("DEV") - .build()) - .getKey(); - dbTester.getSession().commit(); - - ComponentDto dev = dbTester.getDbClient().componentDao().selectOrFailByKey(dbSession, key); - assertThat(dev.getOrganizationUuid()).isEqualTo(organization.getUuid()); - assertThat(dev.key()).isEqualTo("DEV:jon.name@mail.com"); - assertThat(dev.deprecatedKey()).isEqualTo("DEV:jon.name@mail.com"); - assertThat(dev.uuid()).isNotNull(); - assertThat(dev.projectUuid()).isEqualTo(dev.uuid()); - assertThat(dev.moduleUuid()).isNull(); - assertThat(dev.moduleUuidPath()).isEqualTo("." + dev.uuid() + "."); - assertThat(dev.name()).isEqualTo("John"); - assertThat(dev.longName()).isEqualTo("John"); - assertThat(dev.scope()).isEqualTo("PRJ"); - assertThat(dev.qualifier()).isEqualTo("DEV"); - assertThat(dev.getCreatedAt()).isNotNull(); - - assertIndexIsEmpty(); - } - @Test - public void fail_to_create_new_component_on_invalid_key() { - userSession.login("john").setGlobalPermissions(PROVISIONING); - expectedException.expect(BadRequestException.class); - expectedException.expectMessage("Malformed key for Project: struts?parent. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit."); - - underTest.create( - dbSession, - newComponentBuilder() - .setOrganizationUuid(organization.getUuid()) - .setKey("struts?parent") - .setName("Struts project") - .build()); - } - - @Test - public void fail_to_create_new_component_on_invalid_branch() { - expectedException.expect(BadRequestException.class); - expectedException.expectMessage("Malformed branch for Project: origin?branch. Allowed characters are alphanumeric, '-', '_', '.' and '/', with at least one non-digit."); - - userSession.login("john").setGlobalPermissions(PROVISIONING); - - underTest.create( - dbSession, - newComponentBuilder() - .setOrganizationUuid(organization.getUuid()) - .setKey("struts") - .setName("Struts project") - .setBranch("origin?branch") - .build()); - } - - @Test - public void fail_to_create_new_component_if_key_already_exists() { - expectedException.expect(BadRequestException.class); - expectedException.expectMessage("Could not create Project, key already exists: struts"); - - userSession.login("john").setGlobalPermissions(PROVISIONING); - ComponentDto project = ComponentTesting.newProjectDto(dbTester.organizations().insert()).setKey("struts"); - dbClient.componentDao().insert(dbSession, project); - dbSession.commit(); - - underTest.create( - dbSession, - newComponentBuilder() - .setOrganizationUuid(organization.getUuid()) - .setKey("struts") - .setName("Struts project") - .build()); - } - - @Test - public void remove_duplicated_components_when_creating_project() throws Exception { - String projectKey = "PROJECT_KEY"; - - userSession.login("john").setGlobalPermissions(PROVISIONING); - - DbSession session = mock(DbSession.class); - - ComponentDao componentDao = mock(ComponentDao.class); - when(componentDao.selectByKey(session, projectKey)).thenReturn(Optional.absent()); - - DbClient dbClient = mock(DbClient.class); - when(dbClient.openSession(false)).thenReturn(session); - when(dbClient.componentDao()).thenReturn(componentDao); - when(dbClient.componentIndexDao()).thenReturn(mock(ResourceIndexDao.class)); - - doAnswer(invocation -> { - ((ComponentDto) invocation.getArguments()[1]).setId(1L); - return null; - }).when(componentDao).insert(eq(session), any(ComponentDto.class)); - - OrganizationDto organizationDto = dbTester.organizations().insert(); - when(componentDao.selectComponentsHavingSameKeyOrderedById(session, projectKey)).thenReturn(newArrayList( - ComponentTesting.newProjectDto(organizationDto).setId(1L).setKey(projectKey), - ComponentTesting.newProjectDto(organizationDto).setId(2L).setKey(projectKey), - ComponentTesting.newProjectDto(organizationDto).setId(3L).setKey(projectKey))); - - underTest = new ComponentService(dbClient, i18n, userSession, System2.INSTANCE, projectMeasuresIndexer, componentIndexer); - underTest.create( - session, - newComponentBuilder() - .setOrganizationUuid(organization.getUuid()) - .setKey(projectKey) - .setName(projectKey) - .build()); - - verify(componentDao).delete(session, 2L); - verify(componentDao).delete(session, 3L); - } + private ComponentService underTest = new ComponentService(dbClient, userSession, projectMeasuresIndexer, componentIndexer); @Test public void should_fail_silently_on_components_not_found_if_told_so() { @@ -338,12 +107,4 @@ private ComponentDto insertSampleProject() { return componentDb.insertComponent(newProjectDto(dbTester.organizations().insert()).setKey("sample:root")); } - private void assertProjectIsInIndex(String uuid) { - assertThat(es.getIds(INDEX_PROJECT_MEASURES, TYPE_PROJECT_MEASURE)).containsOnly(uuid); - } - - private void assertIndexIsEmpty() { - assertThat(es.getIds(INDEX_PROJECT_MEASURES, TYPE_PROJECT_MEASURE)).isEmpty(); - } - } diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java index d7bbb9c0b07f..abe3040e5b73 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceUpdateKeyTest.java @@ -21,7 +21,6 @@ package org.sonar.server.component; import org.elasticsearch.action.search.SearchRequestBuilder; -import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -40,7 +39,6 @@ import org.sonar.server.es.EsTester; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; -import org.sonar.server.i18n.I18nRule; import org.sonar.server.measure.index.ProjectMeasuresIndexDefinition; import org.sonar.server.measure.index.ProjectMeasuresIndexer; import org.sonar.server.tester.UserSessionRule; @@ -76,16 +74,9 @@ public class ComponentServiceUpdateKeyTest { private DbClient dbClient = db.getDbClient(); private DbSession dbSession = db.getSession(); - private I18nRule i18n = new I18nRule(); - private ProjectMeasuresIndexer projectMeasuresIndexer = new ProjectMeasuresIndexer(system2, dbClient, es.client()); private ComponentIndexer componentIndexer = new ComponentIndexer(dbClient, es.client()); - private ComponentService underTest = new ComponentService(dbClient, i18n, userSession, system2, projectMeasuresIndexer, componentIndexer); - - @Before - public void setUp() { - i18n.put("qualifier.TRK", "Project"); - } + private ComponentService underTest = new ComponentService(dbClient, userSession, projectMeasuresIndexer, componentIndexer); @Test public void update_project_key() { diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java new file mode 100644 index 000000000000..16ea360fe50c --- /dev/null +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentUpdaterTest.java @@ -0,0 +1,333 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact 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.component; + +import com.google.common.base.Optional; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.sonar.api.config.MapSettings; +import org.sonar.api.config.Settings; +import org.sonar.api.utils.System2; +import org.sonar.db.DbClient; +import org.sonar.db.DbSession; +import org.sonar.db.DbTester; +import org.sonar.db.component.ComponentDao; +import org.sonar.db.component.ComponentDto; +import org.sonar.db.component.ComponentTesting; +import org.sonar.db.component.ResourceIndexDao; +import org.sonar.db.organization.OrganizationDto; +import org.sonar.db.permission.template.PermissionTemplateDto; +import org.sonar.db.user.UserDto; +import org.sonar.server.component.index.ComponentIndexDefinition; +import org.sonar.server.component.index.ComponentIndexer; +import org.sonar.server.es.EsTester; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.favorite.FavoriteUpdater; +import org.sonar.server.i18n.I18nRule; +import org.sonar.server.issue.index.IssueIndexDefinition; +import org.sonar.server.measure.index.ProjectMeasuresIndexDefinition; +import org.sonar.server.measure.index.ProjectMeasuresIndexer; +import org.sonar.server.permission.PermissionTemplateService; +import org.sonar.server.permission.index.PermissionIndexer; +import org.sonar.server.view.index.ViewIndexDefinition; + +import static com.google.common.collect.Lists.newArrayList; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import static org.sonar.api.resources.Qualifiers.VIEW; +import static org.sonar.api.web.UserRole.USER; +import static org.sonar.server.component.NewComponent.newComponentBuilder; +import static org.sonar.server.component.index.ComponentIndexDefinition.INDEX_COMPONENTS; +import static org.sonar.server.component.index.ComponentIndexDefinition.TYPE_COMPONENT; +import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.INDEX_PROJECT_MEASURES; +import static org.sonar.server.measure.index.ProjectMeasuresIndexDefinition.TYPE_PROJECT_MEASURE; + +public class ComponentUpdaterTest { + + private static final String DEFAULT_PROJECT_KEY = "project-key"; + private static final String DEFAULT_PROJECT_NAME = "project-name"; + + private System2 system2 = System2.INSTANCE; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Rule + public DbTester db = DbTester.create(system2); + + @Rule + public EsTester es = new EsTester( + new ComponentIndexDefinition(new MapSettings()), + new ProjectMeasuresIndexDefinition(new MapSettings()), + new IssueIndexDefinition(new MapSettings()), + new ViewIndexDefinition(new MapSettings())); + + @Rule + public I18nRule i18n = new I18nRule().put("qualifier.TRK", "Project"); + + private Settings settings = new MapSettings(); + + private PermissionTemplateDto permissionTemplateDto; + + ComponentUpdater underTest = new ComponentUpdater(db.getDbClient(), i18n, system2, + new PermissionTemplateService(db.getDbClient(), settings, new PermissionIndexer(db.getDbClient(), es.client()), null), + new FavoriteUpdater(db.getDbClient()), + new ProjectMeasuresIndexer(system2, db.getDbClient(), es.client()), + new ComponentIndexer(db.getDbClient(), es.client())); + + @Before + public void setUp() throws Exception { + permissionTemplateDto = db.permissionTemplates().insertTemplate(); + setTemplateAsDefault(permissionTemplateDto); + } + + @Test + public void create_project() throws Exception { + ComponentDto project = underTest.create(db.getSession(), + NewComponent.newComponentBuilder() + .setKey(DEFAULT_PROJECT_KEY) + .setName(DEFAULT_PROJECT_NAME) + .setOrganizationUuid(db.getDefaultOrganization().getUuid()) + .build(), + null); + + assertThat(project.getKey()).isEqualTo(DEFAULT_PROJECT_KEY); + assertThat(project.deprecatedKey()).isEqualTo(DEFAULT_PROJECT_KEY); + assertThat(project.name()).isEqualTo(DEFAULT_PROJECT_NAME); + assertThat(project.longName()).isEqualTo(DEFAULT_PROJECT_NAME); + assertThat(project.qualifier()).isEqualTo("TRK"); + assertThat(project.scope()).isEqualTo("PRJ"); + assertThat(project.getOrganizationUuid()).isEqualTo(db.getDefaultOrganization().getUuid()); + assertThat(project.uuid()).isNotNull(); + assertThat(project.projectUuid()).isEqualTo(project.uuid()); + assertThat(project.moduleUuid()).isNull(); + assertThat(project.moduleUuidPath()).isEqualTo("." + project.uuid() + "."); + assertThat(project.getCreatedAt()).isNotNull(); + assertThat(db.getDbClient().componentDao().selectOrFailByKey(db.getSession(), DEFAULT_PROJECT_KEY)).isNotNull(); + } + + @Test + public void create_project_with_branch() throws Exception { + ComponentDto project = underTest.create(db.getSession(), + NewComponent.newComponentBuilder() + .setKey(DEFAULT_PROJECT_KEY) + .setName(DEFAULT_PROJECT_NAME) + .setBranch("origin/master") + .setOrganizationUuid(db.getDefaultOrganization().getUuid()) + .build(), + null); + + assertThat(project.getKey()).isEqualTo("project-key:origin/master"); + } + + @Test + public void remove_duplicated_components_when_creating_project() throws Exception { + String projectKey = "PROJECT_KEY"; + + DbSession session = mock(DbSession.class); + + ComponentDao componentDao = mock(ComponentDao.class); + when(componentDao.selectByKey(session, projectKey)).thenReturn(Optional.absent()); + + DbClient dbClient = mock(DbClient.class); + when(dbClient.openSession(false)).thenReturn(session); + when(dbClient.componentDao()).thenReturn(componentDao); + when(dbClient.componentIndexDao()).thenReturn(mock(ResourceIndexDao.class)); + + doAnswer(invocation -> { + ((ComponentDto) invocation.getArguments()[1]).setId(1L); + return null; + }).when(componentDao).insert(eq(session), any(ComponentDto.class)); + + OrganizationDto organization = db.getDefaultOrganization(); + when(componentDao.selectComponentsHavingSameKeyOrderedById(session, projectKey)).thenReturn(newArrayList( + ComponentTesting.newProjectDto(organization).setId(1L).setKey(projectKey), + ComponentTesting.newProjectDto(organization).setId(2L).setKey(projectKey), + ComponentTesting.newProjectDto(organization).setId(3L).setKey(projectKey))); + + underTest = new ComponentUpdater(dbClient, i18n, System2.INSTANCE, mock(PermissionTemplateService.class), null, mock(ProjectMeasuresIndexer.class), + mock(ComponentIndexer.class)); + underTest.create( + session, + newComponentBuilder() + .setOrganizationUuid(organization.getUuid()) + .setKey(projectKey) + .setName(projectKey) + .build(), + null); + + verify(componentDao).delete(session, 2L); + verify(componentDao).delete(session, 3L); + } + + @Test + public void verify_permission_template_is_applied() throws Exception { + UserDto userDto = db.users().insertUser(); + db.permissionTemplates().addUserToTemplate(permissionTemplateDto.getId(), userDto.getId(), USER); + + ComponentDto project = underTest.create(db.getSession(), + NewComponent.newComponentBuilder() + .setKey(DEFAULT_PROJECT_KEY) + .setName(DEFAULT_PROJECT_NAME) + .setOrganizationUuid(db.getDefaultOrganization().getUuid()) + .build(), + null); + + assertThat(db.users().selectProjectPermissionsOfUser(userDto, project)).containsOnly(USER); + } + + @Test + public void add_project_to_favorite_when_user() throws Exception { + UserDto userDto = db.users().insertUser(); + db.permissionTemplates().addProjectCreatorToTemplate(permissionTemplateDto.getId(), USER); + + ComponentDto project = underTest.create(db.getSession(), + NewComponent.newComponentBuilder() + .setKey(DEFAULT_PROJECT_KEY) + .setName(DEFAULT_PROJECT_NAME) + .setOrganizationUuid(db.getDefaultOrganization().getUuid()) + .build(), + userDto.getId()); + + assertThat(db.favorites().hasFavorite(project, userDto.getId())).isTrue(); + } + + @Test + public void does_not_add_project_to_favorite_when_no_user() throws Exception { + db.permissionTemplates().addProjectCreatorToTemplate(permissionTemplateDto.getId(), USER); + + ComponentDto project = underTest.create(db.getSession(), + NewComponent.newComponentBuilder() + .setKey(DEFAULT_PROJECT_KEY) + .setName(DEFAULT_PROJECT_NAME) + .setOrganizationUuid(db.getDefaultOrganization().getUuid()) + .build(), + null); + + assertThat(db.favorites().hasNoFavorite(project)).isTrue(); + } + + @Test + public void does_not_add_project_to_favorite_when_project_has_no_permission_on_template() throws Exception { + UserDto userDto = db.users().insertUser(); + + ComponentDto project = underTest.create(db.getSession(), + NewComponent.newComponentBuilder() + .setKey(DEFAULT_PROJECT_KEY) + .setName(DEFAULT_PROJECT_NAME) + .setOrganizationUuid(db.getDefaultOrganization().getUuid()) + .build(), + null); + + assertThat(db.favorites().hasNoFavorite(project)).isTrue(); + } + + @Test + public void verify_project_exists_in_es_indexes() throws Exception { + ComponentDto project = underTest.create(db.getSession(), + NewComponent.newComponentBuilder() + .setKey(DEFAULT_PROJECT_KEY) + .setName(DEFAULT_PROJECT_NAME) + .setOrganizationUuid(db.getDefaultOrganization().getUuid()) + .build(), + null); + + assertThat(es.getIds(INDEX_COMPONENTS, TYPE_COMPONENT)).containsOnly(project.uuid()); + assertThat(es.getIds(INDEX_PROJECT_MEASURES, TYPE_PROJECT_MEASURE)).containsOnly(project.uuid()); + } + + @Test + public void fail_when_project_already_exists() throws Exception { + db.components().insertComponent(ComponentTesting.newProjectDto(db.getDefaultOrganization()).setKey(DEFAULT_PROJECT_KEY)); + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Could not create Project, key already exists: project-key"); + + underTest.create(db.getSession(), + NewComponent.newComponentBuilder() + .setKey(DEFAULT_PROJECT_KEY) + .setName(DEFAULT_PROJECT_NAME) + .setOrganizationUuid(db.getDefaultOrganization().getUuid()) + .build(), + null); + } + + @Test + public void fail_when_key_has_bad_format() throws Exception { + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Malformed key for Project: 1234"); + + underTest.create(db.getSession(), + NewComponent.newComponentBuilder() + .setKey("1234") + .setName(DEFAULT_PROJECT_NAME) + .setOrganizationUuid(db.getDefaultOrganization().getUuid()) + .build(), + null); + } + + @Test + public void fail_to_create_new_component_on_invalid_branch() { + expectedException.expect(BadRequestException.class); + expectedException.expectMessage("Malformed branch for Project: origin?branch. Allowed characters are alphanumeric, '-', '_', '.' and '/', with at least one non-digit."); + + underTest.create(db.getSession(), + NewComponent.newComponentBuilder() + .setKey(DEFAULT_PROJECT_KEY) + .setName(DEFAULT_PROJECT_NAME) + .setBranch("origin?branch") + .setOrganizationUuid(db.getDefaultOrganization().getUuid()) + .build(), + null); + } + + @Test + public void create_view() { + ComponentDto view = underTest.create(db.getSession(), + NewComponent.newComponentBuilder() + .setKey("view-key") + .setName("view-name") + .setQualifier(VIEW) + .setOrganizationUuid(db.getDefaultOrganization().getUuid()) + .build(), + null); + + assertThat(view.getKey()).isEqualTo("view-key"); + assertThat(view.name()).isEqualTo("view-name"); + assertThat(view.qualifier()).isEqualTo("VW"); + assertThat(es.getIds(INDEX_COMPONENTS, TYPE_COMPONENT)).containsOnly(view.uuid()); + // Indexes related to project measures, issues and views are not indexed + assertThat(es.getIds(INDEX_PROJECT_MEASURES, TYPE_PROJECT_MEASURE)).isEmpty(); + assertThat(es.getIds(IssueIndexDefinition.INDEX, IssueIndexDefinition.TYPE_AUTHORIZATION)).isEmpty(); + assertThat(es.getIds(ViewIndexDefinition.INDEX, ViewIndexDefinition.TYPE_VIEW)).isEmpty(); + } + + private void setTemplateAsDefault(PermissionTemplateDto permissionTemplateDto) { + settings.appendProperty("sonar.permission.template.default", permissionTemplateDto.getUuid()); + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/DefaultRubyComponentServiceTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/DefaultRubyComponentServiceTest.java index 9b2db1113f35..7602bc354fce 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/DefaultRubyComponentServiceTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/DefaultRubyComponentServiceTest.java @@ -19,6 +19,8 @@ */ package org.sonar.server.component; +import java.util.List; +import java.util.Map; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -32,15 +34,16 @@ import org.sonar.server.component.index.ComponentIndexDefinition; import org.sonar.server.component.index.ComponentIndexer; import org.sonar.server.es.EsTester; -import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.favorite.FavoriteUpdater; import org.sonar.server.i18n.I18nRule; +import org.sonar.server.issue.index.IssueIndexDefinition; import org.sonar.server.measure.index.ProjectMeasuresIndexDefinition; import org.sonar.server.measure.index.ProjectMeasuresIndexer; import org.sonar.server.organization.DefaultOrganizationProvider; import org.sonar.server.organization.TestDefaultOrganizationProvider; import org.sonar.server.permission.PermissionTemplateService; import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.view.index.ViewIndexDefinition; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; @@ -48,7 +51,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import static org.sonar.core.permission.GlobalPermissions.PROVISIONING; public class DefaultRubyComponentServiceTest { @@ -57,24 +59,26 @@ public class DefaultRubyComponentServiceTest { @Rule public DbTester db = DbTester.create(system2); @Rule - public UserSessionRule userSession = UserSessionRule.standalone(); + public EsTester es = new EsTester( + new ComponentIndexDefinition(new MapSettings()), + new ProjectMeasuresIndexDefinition(new MapSettings()), + new IssueIndexDefinition(new MapSettings()), + new ViewIndexDefinition(new MapSettings())); @Rule - public EsTester es = new EsTester(new ProjectMeasuresIndexDefinition(new MapSettings()), - new ComponentIndexDefinition(new MapSettings())); + public UserSessionRule userSession = UserSessionRule.standalone(); private I18nRule i18n = new I18nRule(); - private DbClient dbClient = db.getDbClient(); private DbSession dbSession = db.getSession(); - private ComponentService componentService = new ComponentService(dbClient, i18n, userSession, system2, - new ProjectMeasuresIndexer(system2, dbClient, es.client()), new ComponentIndexer(dbClient, es.client())); private PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class); private FavoriteUpdater favoriteUpdater = mock(FavoriteUpdater.class); + private ComponentUpdater componentUpdater = new ComponentUpdater(db.getDbClient(), i18n, system2, permissionTemplateService, favoriteUpdater, + new ProjectMeasuresIndexer(system2, db.getDbClient(), es.client()), + new ComponentIndexer(db.getDbClient(), es.client())); private DefaultOrganizationProvider defaultOrganizationProvider = TestDefaultOrganizationProvider.from(db); - private DefaultRubyComponentService underTest = new DefaultRubyComponentService(dbClient, componentService, - permissionTemplateService, favoriteUpdater, defaultOrganizationProvider); + private DefaultRubyComponentService underTest = new DefaultRubyComponentService(userSession, dbClient, componentUpdater, defaultOrganizationProvider); private String defaultOrganizationUuid; @@ -85,7 +89,7 @@ public void setUp() throws Exception { @Test public void create_component() { - userSession.login("john").setGlobalPermissions(PROVISIONING); + userSession.login("john").setUserId(100); String componentKey = "new-project"; String componentName = "New Project"; String qualifier = Qualifiers.PROJECT; @@ -98,14 +102,7 @@ public void create_component() { assertThat(project.name()).isEqualTo(componentName); assertThat(project.qualifier()).isEqualTo(qualifier); assertThat(project.getId()).isEqualTo(result); - verify(permissionTemplateService).applyDefaultPermissionTemplate(any(DbSession.class), eq(defaultOrganizationUuid), eq(componentKey)); - verify(favoriteUpdater).add(any(DbSession.class), eq(project), eq(null)); - } - - @Test(expected = BadRequestException.class) - public void should_throw_if_malformed_key1() { - userSession.login("john").setGlobalPermissions(PROVISIONING); - underTest.createComponent("1234", "New Project", Qualifiers.PROJECT); + verify(permissionTemplateService).applyDefault(any(DbSession.class), eq(defaultOrganizationUuid), any(ComponentDto.class), eq(100L)); } } diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ws/BulkUpdateKeyActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ws/BulkUpdateKeyActionTest.java index 0c468c869485..511a6afba2e5 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ws/BulkUpdateKeyActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ws/BulkUpdateKeyActionTest.java @@ -93,7 +93,7 @@ public class BulkUpdateKeyActionTest { private WsActionTester ws = new WsActionTester( new BulkUpdateKeyAction(dbClient, componentFinder, - new ComponentService(dbClient, null, null, null, new ProjectMeasuresIndexer(system2, dbClient, es.client()), new ComponentIndexer(dbClient, es.client())), + new ComponentService(dbClient, null, new ProjectMeasuresIndexer(system2, dbClient, es.client()), new ComponentIndexer(dbClient, es.client())), userSession)); @Before diff --git a/server/sonar-server/src/test/java/org/sonar/server/computation/queue/ReportSubmitterTest.java b/server/sonar-server/src/test/java/org/sonar/server/computation/queue/ReportSubmitterTest.java index 99b49a96a893..09ef9ebd833b 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/computation/queue/ReportSubmitterTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/computation/queue/ReportSubmitterTest.java @@ -37,7 +37,7 @@ import org.sonar.db.ce.CeTaskTypes; import org.sonar.db.component.ComponentDto; import org.sonar.db.organization.OrganizationDto; -import org.sonar.server.component.ComponentService; +import org.sonar.server.component.ComponentUpdater; import org.sonar.server.component.NewComponent; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.exceptions.NotFoundException; @@ -75,11 +75,11 @@ public class ReportSubmitterTest { private String defaultOrganizationKey; private String defaultOrganizationUuid; private CeQueue queue = mock(CeQueueImpl.class); - private ComponentService componentService = mock(ComponentService.class); + private ComponentUpdater componentUpdater = mock(ComponentUpdater.class); private PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class); private FavoriteUpdater favoriteUpdater = mock(FavoriteUpdater.class); - private ReportSubmitter underTest = new ReportSubmitter(queue, userSession, componentService, permissionTemplateService, db.getDbClient(), favoriteUpdater); + private ReportSubmitter underTest = new ReportSubmitter(queue, userSession, componentUpdater, permissionTemplateService, db.getDbClient()); @Before public void setUp() throws Exception { @@ -138,7 +138,7 @@ public void provision_project_if_does_not_exist() throws Exception { mockSuccessfulPrepareSubmitCall(); ComponentDto createdProject = new ComponentDto().setId(23L).setUuid(PROJECT_UUID).setKey(PROJECT_KEY); - when(componentService.create(any(DbSession.class), any(NewComponent.class))).thenReturn(createdProject); + when(componentUpdater.create(any(DbSession.class), any(NewComponent.class), eq(null))).thenReturn(createdProject); when(permissionTemplateService.wouldUserHavePermissionWithDefaultTemplate(any(DbSession.class), eq(organization.getUuid()), anyLong(), eq(SCAN_EXECUTION), anyString(), eq(PROJECT_KEY), eq(Qualifiers.PROJECT))) .thenReturn(true); @@ -147,8 +147,6 @@ public void provision_project_if_does_not_exist() throws Exception { underTest.submit(organization.getKey(), PROJECT_KEY, null, PROJECT_NAME, IOUtils.toInputStream("{binary}")); verifyReportIsPersisted(TASK_UUID); - verify(permissionTemplateService).applyDefault(any(DbSession.class), eq(organization.getUuid()), eq(createdProject), anyLong()); - verify(favoriteUpdater).add(any(DbSession.class), eq(createdProject), eq(null)); verify(queue).submit(argThat(new TypeSafeMatcher() { @Override protected boolean matchesSafely(CeTaskSubmit submit) { @@ -169,7 +167,7 @@ public void no_favorite_when_no_project_creator_permission_on_permission_templat mockSuccessfulPrepareSubmitCall(); ComponentDto createdProject = new ComponentDto().setId(23L).setUuid(PROJECT_UUID).setKey(PROJECT_KEY); - when(componentService.create(any(DbSession.class), any(NewComponent.class))).thenReturn(createdProject); + when(componentUpdater.create(any(DbSession.class), any(NewComponent.class), eq(null))).thenReturn(createdProject); when(permissionTemplateService.wouldUserHavePermissionWithDefaultTemplate(any(DbSession.class), eq(defaultOrganizationUuid), anyLong(), eq(SCAN_EXECUTION), anyString(), eq(PROJECT_KEY), eq(Qualifiers.PROJECT))) .thenReturn(true); @@ -185,7 +183,7 @@ public void submit_a_report_on_new_project_with_global_scan_permission() { userSession.setGlobalPermissions(SCAN_EXECUTION, PROVISIONING); mockSuccessfulPrepareSubmitCall(); - when(componentService.create(any(DbSession.class), any(NewComponent.class))).thenReturn(new ComponentDto().setId(23L).setUuid(PROJECT_UUID).setKey(PROJECT_KEY)); + when(componentUpdater.create(any(DbSession.class), any(NewComponent.class), eq(null))).thenReturn(new ComponentDto().setId(23L).setUuid(PROJECT_UUID).setKey(PROJECT_KEY)); when(permissionTemplateService.wouldUserHavePermissionWithDefaultTemplate(any(DbSession.class), eq(defaultOrganizationUuid), anyLong(), eq(SCAN_EXECUTION), anyString(), eq(PROJECT_KEY), eq(Qualifiers.PROJECT))) .thenReturn(true); @@ -233,7 +231,7 @@ public void fail_with_forbidden_exception_on_new_project_when_only_project_scan_ userSession.addProjectUuidPermissions(SCAN_EXECUTION, PROJECT_UUID); mockSuccessfulPrepareSubmitCall(); - when(componentService.create(any(DbSession.class), any(NewComponent.class))).thenReturn(new ComponentDto().setUuid(PROJECT_UUID).setKey(PROJECT_KEY)); + when(componentUpdater.create(any(DbSession.class), any(NewComponent.class), eq(null))).thenReturn(new ComponentDto().setUuid(PROJECT_UUID).setKey(PROJECT_KEY)); thrown.expect(ForbiddenException.class); underTest.submit(defaultOrganizationKey, PROJECT_KEY, null, PROJECT_NAME, IOUtils.toInputStream("{binary}")); diff --git a/server/sonar-server/src/test/java/org/sonar/server/project/ws/CreateActionTest.java b/server/sonar-server/src/test/java/org/sonar/server/project/ws/CreateActionTest.java index 8610fc8f404e..dbce259b8874 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/project/ws/CreateActionTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/project/ws/CreateActionTest.java @@ -36,7 +36,7 @@ import org.sonar.db.component.ComponentTesting; import org.sonar.db.permission.template.PermissionTemplateDto; import org.sonar.db.user.UserDto; -import org.sonar.server.component.ComponentService; +import org.sonar.server.component.ComponentUpdater; import org.sonar.server.component.index.ComponentIndexDefinition; import org.sonar.server.component.index.ComponentIndexer; import org.sonar.server.es.EsTester; @@ -101,11 +101,11 @@ public class CreateActionTest { private WsActionTester ws = new WsActionTester( new CreateAction( db.getDbClient(), userSession, - new ComponentService(db.getDbClient(), i18n, userSession, system2, + new ComponentUpdater(db.getDbClient(), i18n, system2, + new PermissionTemplateService(db.getDbClient(), settings, new PermissionIndexer(db.getDbClient(), es.client()), userSession), + new FavoriteUpdater(db.getDbClient()), new ProjectMeasuresIndexer(system2, db.getDbClient(), es.client()), new ComponentIndexer(db.getDbClient(), es.client())), - new PermissionTemplateService(db.getDbClient(), settings, new PermissionIndexer(db.getDbClient(), es.client()), userSession), - new FavoriteUpdater(db.getDbClient()), defaultOrganizationProvider)); @Before