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 b8c8f269394f..fe9eccbe6898 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 @@ -21,16 +21,27 @@ package org.sonar.server.component; import org.sonar.api.ServerComponent; +import org.sonar.api.i18n.I18n; +import org.sonar.api.resources.Scopes; +import org.sonar.api.utils.internal.Uuids; import org.sonar.api.web.UserRole; import org.sonar.core.component.ComponentDto; +import org.sonar.core.component.ComponentKeys; +import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.DbSession; import org.sonar.core.preview.PreviewCache; +import org.sonar.core.resource.ResourceIndexerDao; import org.sonar.core.resource.ResourceKeyUpdaterDao; import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.BadRequestException; +import org.sonar.server.permission.InternalPermissionService; import org.sonar.server.user.UserSession; import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import java.util.Date; +import java.util.Locale; import java.util.Map; public class ComponentService implements ServerComponent { @@ -39,17 +50,24 @@ public class ComponentService implements ServerComponent { private final ResourceKeyUpdaterDao resourceKeyUpdaterDao; private final PreviewCache previewCache; + private final I18n i18n; + private final ResourceIndexerDao resourceIndexerDao; + private final InternalPermissionService permissionService; - public ComponentService(DbClient dbClient, ResourceKeyUpdaterDao resourceKeyUpdaterDao, PreviewCache previewCache) { + public ComponentService(DbClient dbClient, ResourceKeyUpdaterDao resourceKeyUpdaterDao, PreviewCache previewCache, I18n i18n, ResourceIndexerDao resourceIndexerDao, + InternalPermissionService permissionService) { this.dbClient = dbClient; this.resourceKeyUpdaterDao = resourceKeyUpdaterDao; this.previewCache = previewCache; + this.i18n = i18n; + this.resourceIndexerDao = resourceIndexerDao; + this.permissionService = permissionService; } public ComponentDto getByKey(String key) { DbSession session = dbClient.openSession(false); try { - return dbClient.componentDao().getByKey(session, key); + return getByKey(session, key); } finally { session.close(); } @@ -59,7 +77,7 @@ public ComponentDto getByKey(String key) { public ComponentDto getNullableByKey(String key) { DbSession session = dbClient.openSession(false); try { - return dbClient.componentDao().getNullableByKey(session, key); + return getNullableByKey(session, key); } finally { session.close(); } @@ -89,7 +107,7 @@ public void updateKey(String projectOrModuleKey, String newKey) { DbSession session = dbClient.openSession(false); try { - ComponentDto projectOrModule = getByKey(projectOrModuleKey); + ComponentDto projectOrModule = getByKey(session, projectOrModuleKey); resourceKeyUpdaterDao.updateKey(projectOrModule.getId(), newKey); session.commit(); @@ -117,7 +135,7 @@ public void bulkUpdateKey(String projectKey, String stringToReplace, String repl DbSession session = dbClient.openSession(false); try { - ComponentDto project = getByKey(projectKey); + ComponentDto project = getByKey(session, projectKey); resourceKeyUpdaterDao.bulkUpdateKey(project.getId(), stringToReplace, replacementString); session.commit(); @@ -131,4 +149,67 @@ public void bulkUpdateKey(String projectKey, String stringToReplace, String repl } } + public String create(NewComponent newComponent) { + UserSession.get().checkGlobalPermission(GlobalPermissions.PROVISIONING); + + DbSession session = dbClient.openSession(false); + try { + checkKeyFormat(newComponent.qualifier(), newComponent.key()); + checkBranchFormat(newComponent.qualifier(), newComponent.branch()); + String keyWithBranch = ComponentKeys.createKey(newComponent.key(), newComponent.branch()); + + ComponentDto existingComponent = getNullableByKey(keyWithBranch); + if (existingComponent != null) { + throw new BadRequestException(formatMessage("Could not create %s, key already exists: %s", newComponent.qualifier(), keyWithBranch)); + } + + String uuid = Uuids.create(); + ComponentDto component = dbClient.componentDao().insert(session, + new ComponentDto() + .setUuid(uuid) + .setProjectUuid(uuid) + .setKey(keyWithBranch) + .setDeprecatedKey(keyWithBranch) + .setName(newComponent.name()) + .setLongName(newComponent.name()) + .setScope(Scopes.PROJECT) + .setQualifier(newComponent.qualifier()) + .setCreatedAt(new Date())); + resourceIndexerDao.indexResource(session, component.getId()); + session.commit(); + + permissionService.applyDefaultPermissionTemplate(component.key()); + return component.key(); + } finally { + session.close(); + } + } + + private void checkKeyFormat(String qualifier, String kee) { + if (!ComponentKeys.isValidModuleKey(kee)) { + throw new BadRequestException(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); + } + + @CheckForNull + private ComponentDto getNullableByKey(DbSession session, String key) { + return dbClient.componentDao().getNullableByKey(session, key); + } + + private ComponentDto getByKey(DbSession session, String key) { + return dbClient.componentDao().getByKey(session, key); + } + } 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 8184d2cae0c2..6ed564077856 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 @@ -24,33 +24,28 @@ import org.sonar.api.component.RubyComponentService; import org.sonar.api.i18n.I18n; import org.sonar.api.resources.Qualifiers; -import org.sonar.api.resources.Scopes; -import org.sonar.api.utils.internal.Uuids; import org.sonar.core.component.ComponentDto; -import org.sonar.core.component.ComponentKeys; import org.sonar.core.resource.ResourceDao; import org.sonar.core.resource.ResourceDto; -import org.sonar.core.resource.ResourceIndexerDao; import org.sonar.server.exceptions.BadRequestException; -import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.util.RubyUtils; import javax.annotation.CheckForNull; +import javax.annotation.Nullable; -import java.util.*; +import java.util.List; +import java.util.Map; public class DefaultRubyComponentService implements RubyComponentService { private final ResourceDao resourceDao; private final DefaultComponentFinder finder; - private final ResourceIndexerDao resourceIndexerDao; private final ComponentService componentService; private final I18n i18n; - public DefaultRubyComponentService(ResourceDao resourceDao, DefaultComponentFinder finder, ResourceIndexerDao resourceIndexerDao, ComponentService componentService, I18n i18n) { + public DefaultRubyComponentService(ResourceDao resourceDao, DefaultComponentFinder finder, ComponentService componentService, I18n i18n) { this.resourceDao = resourceDao; this.finder = finder; - this.resourceIndexerDao = resourceIndexerDao; this.componentService = componentService; this.i18n = i18n; } @@ -66,48 +61,28 @@ public Component findByUuid(String uuid) { return componentService.getNullableByUuid(uuid); } + /** + * Be careful when updating this method, it's used by the Views plugin + */ @CheckForNull - public Long createComponent(String kee, String name, String qualifier) { + public Long createComponent(String key, String name, String qualifier) { + return createComponent(key, null, name, qualifier); + } + + @CheckForNull + public Long createComponent(String key, @Nullable String branch, String name, @Nullable String qualifier) { // Sub view should not be created with provisioning. Will be fixed by http://jira.sonarsource.com/browse/VIEWS-296 if (!Qualifiers.SUBVIEW.equals(qualifier)) { - ComponentDto component = (ComponentDto) resourceDao.findByKey(kee); - if (component != null) { - throw new BadRequestException(formatMessage("Could not create %s, key already exists: %s", qualifier, kee)); - } - checkKeyFormat(qualifier, kee); - - String uuid = Uuids.create(); - resourceDao.insertOrUpdate( - new ResourceDto() - .setUuid(uuid) - .setProjectUuid(uuid) - .setKey(kee) - .setDeprecatedKey(kee) - .setName(name) - .setLongName(name) - .setScope(Scopes.PROJECT) - .setQualifier(qualifier) - .setCreatedAt(new Date())); - component = (ComponentDto) resourceDao.findByKey(kee); + String createdKey = componentService.create(NewComponent.create(key, name).setQualifier(qualifier).setBranch(branch)); + ComponentDto component = (ComponentDto) resourceDao.findByKey(createdKey); if (component == null) { - throw new BadRequestException(String.format("Component not created: %s", kee)); + throw new BadRequestException(String.format("Component not created: %s", createdKey)); } - resourceIndexerDao.indexResource(component.getId()); return component.getId(); } return null; } - public void updateComponent(Long id, String key, String name) { - ResourceDto resource = resourceDao.getResource(id); - if (resource == null) { - throw new NotFoundException(); - } - checkKeyFormat(resource.getQualifier(), key); - - resourceDao.insertOrUpdate(resource.setKey(key).setName(name)); - } - public DefaultComponentQueryResult find(Map params) { ComponentQuery query = toQuery(params); List components = resourceDao.selectProjectsByQualifiers(query.qualifiers()); @@ -158,14 +133,4 @@ static ComponentQuery toQuery(Map props) { return builder.build(); } - private void checkKeyFormat(String qualifier, String kee) { - if (!ComponentKeys.isValidModuleKey(kee)) { - throw new BadRequestException(formatMessage("Malformed key for %s: %s. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit.", - qualifier, kee)); - } - } - - 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/NewComponent.java b/server/sonar-server/src/main/java/org/sonar/server/component/NewComponent.java new file mode 100644 index 000000000000..8f9dc59acb10 --- /dev/null +++ b/server/sonar-server/src/main/java/org/sonar/server/component/NewComponent.java @@ -0,0 +1,73 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube 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. + * + * SonarQube 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.Preconditions; +import org.sonar.api.resources.Qualifiers; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; + +public class NewComponent { + + private String key; + private String branch; + private String qualifier; + private String name; + + public NewComponent(String key, String name) { + this.key = key; + this.name = name; + } + + public String key() { + return key; + } + + public String name() { + return name; + } + + @CheckForNull + public String branch() { + return branch; + } + + public NewComponent setBranch(@Nullable String branch) { + this.branch = branch; + return this; + } + + public String qualifier() { + return qualifier != null ? qualifier : Qualifiers.PROJECT; + } + + public NewComponent setQualifier(@Nullable String qualifier) { + this.qualifier = qualifier; + return this; + } + + public static NewComponent create(String key, String name) { + Preconditions.checkNotNull(key, "Key can't be null"); + Preconditions.checkNotNull(name, "Name can't be null"); + return new NewComponent(key, name); + } +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceMediumTest.java b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceMediumTest.java index adeb8d137097..8cb2dcab6fc3 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceMediumTest.java +++ b/server/sonar-server/src/test/java/org/sonar/server/component/ComponentServiceMediumTest.java @@ -24,25 +24,26 @@ import org.junit.Before; import org.junit.ClassRule; import org.junit.Test; +import org.sonar.api.resources.Qualifiers; import org.sonar.api.security.DefaultGroups; import org.sonar.api.web.UserRole; import org.sonar.core.component.ComponentDto; import org.sonar.core.permission.GlobalPermissions; import org.sonar.core.persistence.DbSession; -import org.sonar.core.rule.RuleDto; import org.sonar.server.component.db.ComponentDao; import org.sonar.server.db.DbClient; +import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.ForbiddenException; import org.sonar.server.permission.InternalPermissionService; import org.sonar.server.permission.PermissionChange; -import org.sonar.server.rule.RuleTesting; -import org.sonar.server.rule.db.RuleDao; +import org.sonar.server.platform.Platform; import org.sonar.server.tester.ServerTester; import org.sonar.server.user.MockUserSession; import java.util.Map; import static org.fest.assertions.Assertions.assertThat; +import static org.fest.assertions.Fail.fail; public class ComponentServiceMediumTest { @@ -52,8 +53,6 @@ public class ComponentServiceMediumTest { DbClient db; DbSession session; ComponentService service; - ComponentDto project; - RuleDto rule; @Before public void setUp() throws Exception { @@ -61,19 +60,6 @@ public void setUp() throws Exception { db = tester.get(DbClient.class); session = db.openSession(false); service = tester.get(ComponentService.class); - - project = ComponentTesting.newProjectDto().setKey("sample:root"); - tester.get(ComponentDao.class).insert(session, project); - session.commit(); - - // project can be seen by anyone - MockUserSession.set().setLogin("admin").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); - tester.get(InternalPermissionService.class).addPermission(new PermissionChange().setComponentKey(project.getKey()).setGroup(DefaultGroups.ANYONE).setPermission(UserRole.USER)); - - rule = RuleTesting.newXooX1(); - tester.get(RuleDao.class).insert(session, rule); - - session.commit(); } @After @@ -83,28 +69,33 @@ public void after() { @Test public void get_by_key() throws Exception { + ComponentDto project = createProject("sample:root"); assertThat(service.getByKey(project.getKey())).isNotNull(); } @Test public void get_nullable_by_key() throws Exception { + ComponentDto project = createProject("sample:root"); assertThat(service.getNullableByKey(project.getKey())).isNotNull(); assertThat(service.getNullableByKey("unknown")).isNull(); } @Test public void get_by_uuid() throws Exception { + ComponentDto project = createProject("sample:root"); assertThat(service.getByUuid(project.uuid())).isNotNull(); } @Test public void get_nullable_by_uuid() throws Exception { + ComponentDto project = createProject("sample:root"); assertThat(service.getNullableByUuid(project.uuid())).isNotNull(); assertThat(service.getNullableByUuid("unknown")).isNull(); } @Test public void update_project_key() throws Exception { + ComponentDto project = createProject("sample:root"); ComponentDto file = ComponentTesting.newFileDto(project).setKey("sample:root:src/File.xoo"); tester.get(ComponentDao.class).insert(session, file); @@ -128,6 +119,7 @@ public void update_project_key() throws Exception { @Test public void update_module_key() throws Exception { + ComponentDto project = createProject("sample:root"); ComponentDto module = ComponentTesting.newModuleDto(project).setKey("sample:root:module"); tester.get(ComponentDao.class).insert(session, module); @@ -176,12 +168,14 @@ public void update_provisioned_project_key() throws Exception { @Test(expected = ForbiddenException.class) public void fail_to_update_project_key_without_admin_permission() throws Exception { + ComponentDto project = createProject("sample:root"); MockUserSession.set().setLogin("john").addComponentPermission(UserRole.USER, project.key(), project.key()); service.updateKey(project.key(), "sample2:root"); } @Test public void check_module_keys_before_renaming() throws Exception { + ComponentDto project = createProject("sample:root"); ComponentDto module = ComponentTesting.newModuleDto(project).setKey("sample:root:module"); tester.get(ComponentDao.class).insert(session, module); @@ -200,6 +194,7 @@ public void check_module_keys_before_renaming() throws Exception { @Test public void check_module_keys_before_renaming_return_duplicate_key() throws Exception { + ComponentDto project = createProject("sample:root"); ComponentDto module = ComponentTesting.newModuleDto(project).setKey("sample:root:module"); tester.get(ComponentDao.class).insert(session, module); @@ -218,12 +213,14 @@ public void check_module_keys_before_renaming_return_duplicate_key() throws Exce @Test(expected = ForbiddenException.class) public void fail_to_check_module_keys_before_renaming_without_admin_permission() throws Exception { + ComponentDto project = createProject("sample:root"); MockUserSession.set().setLogin("john").addComponentPermission(UserRole.USER, project.key(), project.key()); service.checkModuleKeysBeforeRenaming(project.key(), "sample", "sample2"); } @Test public void bulk_update_project_key() throws Exception { + ComponentDto project = createProject("sample:root"); ComponentDto module = ComponentTesting.newModuleDto(project).setKey("sample:root:module"); tester.get(ComponentDao.class).insert(session, module); @@ -273,8 +270,120 @@ public void bulk_update_provisioned_project_key() throws Exception { @Test(expected = ForbiddenException.class) public void fail_to_bulk_update_project_key_without_admin_permission() throws Exception { + ComponentDto project = createProject("sample:root"); MockUserSession.set().setLogin("john").addProjectPermissions(UserRole.USER, project.key()); service.bulkUpdateKey("sample:root", "sample", "sample2"); } + @Test + public void create_project() throws Exception { + executeStartupTasksToCreateDefaultPermissionTemplate(); + MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.PROVISIONING); + + String key = service.create(NewComponent.create("struts", "Struts project")); + + ComponentDto project = service.getNullableByKey(key); + 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()).isNull(); + 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(); + } + + @Test + public void create_new_project_with_branch() throws Exception { + executeStartupTasksToCreateDefaultPermissionTemplate(); + MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.PROVISIONING); + + String key = service.create(NewComponent.create("struts", "Struts project").setBranch("origin/branch")); + + ComponentDto project = service.getNullableByKey(key); + assertThat(project.key()).isEqualTo("struts:origin/branch"); + assertThat(project.deprecatedKey()).isEqualTo("struts:origin/branch"); + } + + @Test + public void create_view() throws Exception { + executeStartupTasksToCreateDefaultPermissionTemplate(); + MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.PROVISIONING); + + String key = service.create(NewComponent.create("all-project", "All Projects").setQualifier(Qualifiers.VIEW)); + + ComponentDto project = service.getNullableByKey(key); + 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()).isNull(); + 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(); + } + + @Test + public void fail_to_create_new_component_on_invalid_key() throws Exception { + MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.PROVISIONING); + + try { + service.create(NewComponent.create("struts?parent", "Struts project")); + fail(); + } catch (Exception e){ + assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("Malformed key for Project: struts?parent. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit."); + } + } + + @Test + public void fail_to_create_new_component_on_invalid_branch() throws Exception { + MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.PROVISIONING); + + try { + service.create(NewComponent.create("struts", "Struts project").setBranch("origin?branch")); + fail(); + } catch (Exception e){ + assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("Malformed branch for Project: origin?branch. Allowed characters are alphanumeric, '-', '_', '.' and '/', with at least one non-digit."); + } + } + + @Test + public void fail_to_create_new_component_if_key_already_exists() throws Exception { + MockUserSession.set().setLogin("john").setGlobalPermissions(GlobalPermissions.PROVISIONING); + + ComponentDto project = ComponentTesting.newProjectDto().setKey("struts"); + tester.get(ComponentDao.class).insert(session, project); + session.commit(); + + try { + service.create(NewComponent.create("struts", "Struts project")); + fail(); + } catch (Exception e){ + assertThat(e).isInstanceOf(BadRequestException.class).hasMessage("Could not create Project, key already exists: struts"); + } + } + + private ComponentDto createProject(String key){ + ComponentDto project = ComponentTesting.newProjectDto().setKey("sample:root"); + tester.get(ComponentDao.class).insert(session, project); + session.commit(); + + // project can be seen by anyone + MockUserSession.set().setLogin("admin").setGlobalPermissions(GlobalPermissions.SYSTEM_ADMIN); + tester.get(InternalPermissionService.class).addPermission(new PermissionChange().setComponentKey(project.getKey()).setGroup(DefaultGroups.ANYONE).setPermission(UserRole.USER)); + MockUserSession.set(); + + return project; + } + + private void executeStartupTasksToCreateDefaultPermissionTemplate(){ + tester.get(Platform.class).executeStartupTasks(); + } + } 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 a04ea302b355..d41bc6beca94 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 @@ -26,13 +26,9 @@ import org.sonar.api.component.Component; import org.sonar.api.i18n.I18n; import org.sonar.api.resources.Qualifiers; -import org.sonar.api.resources.Scopes; import org.sonar.core.component.ComponentDto; import org.sonar.core.resource.ResourceDao; -import org.sonar.core.resource.ResourceDto; -import org.sonar.core.resource.ResourceIndexerDao; import org.sonar.server.exceptions.BadRequestException; -import org.sonar.server.exceptions.NotFoundException; import java.util.List; import java.util.Map; @@ -48,7 +44,6 @@ public class DefaultRubyComponentServiceTest { ResourceDao resourceDao; DefaultComponentFinder finder; - ResourceIndexerDao resourceIndexerDao; ComponentService componentService; I18n i18n; @@ -58,10 +53,9 @@ public class DefaultRubyComponentServiceTest { public void before() { resourceDao = mock(ResourceDao.class); finder = mock(DefaultComponentFinder.class); - resourceIndexerDao = mock(ResourceIndexerDao.class); componentService = mock(ComponentService.class); i18n = mock(I18n.class); - service = new DefaultRubyComponentService(resourceDao, finder, resourceIndexerDao, componentService, i18n); + service = new DefaultRubyComponentService(resourceDao, finder, componentService, i18n); } @Test @@ -81,49 +75,35 @@ public void find_by_uuid() { } @Test - public void create_component_and_index_it() { + public void create_component() { String componentKey = "new-project"; String componentName = "New Project"; String qualifier = Qualifiers.PROJECT; - long componentId = Long.MAX_VALUE; - ComponentDto component = mock(ComponentDto.class); - when(component.getId()).thenReturn(componentId); - when(resourceDao.findByKey(componentKey)).thenReturn(null).thenReturn(component); + when(resourceDao.findByKey(componentKey)).thenReturn(ComponentTesting.newProjectDto()); + when(componentService.create(any(NewComponent.class))).thenReturn(componentKey); service.createComponent(componentKey, componentName, qualifier); - ArgumentCaptor resourceCaptor = ArgumentCaptor.forClass(ResourceDto.class); - verify(resourceDao).insertOrUpdate(resourceCaptor.capture()); - ResourceDto created = resourceCaptor.getValue(); - assertThat(created.getUuid()).isNotNull(); - assertThat(created.getProjectUuid()).isEqualTo(created.getUuid()); - assertThat(created.getKey()).isEqualTo(componentKey); - assertThat(created.getName()).isEqualTo(componentName); - assertThat(created.getLongName()).isEqualTo(componentName); - assertThat(created.getScope()).isEqualTo(Scopes.PROJECT); - assertThat(created.getQualifier()).isEqualTo(qualifier); - verify(resourceDao, times(2)).findByKey(componentKey); - verify(resourceIndexerDao).indexResource(componentId); + ArgumentCaptor newComponentArgumentCaptor = ArgumentCaptor.forClass(NewComponent.class); + verify(componentService).create(newComponentArgumentCaptor.capture()); + NewComponent newComponent = newComponentArgumentCaptor.getValue(); + assertThat(newComponent.key()).isEqualTo(componentKey); + assertThat(newComponent.name()).isEqualTo(componentName); + assertThat(newComponent.branch()).isNull(); + assertThat(newComponent.qualifier()).isEqualTo(Qualifiers.PROJECT); } @Test public void not_create_component_on_sub_views() { - String componentKey = "new-project"; - String componentName = "New Project"; - String qualifier = Qualifiers.SUBVIEW; - long componentId = Long.MAX_VALUE; - ComponentDto component = mock(ComponentDto.class); - when(component.getId()).thenReturn(componentId); - when(resourceDao.findByKey(componentKey)).thenReturn(null).thenReturn(component); + when(resourceDao.findByKey(anyString())).thenReturn(ComponentTesting.newProjectDto()); - service.createComponent(componentKey, componentName, qualifier); + service.createComponent("new-project", "New Project", Qualifiers.SUBVIEW); - verify(resourceDao, never()).insertOrUpdate(any(ResourceDto.class)); - verifyZeroInteractions(resourceIndexerDao); + verify(componentService, never()).create(any(NewComponent.class)); } @Test(expected = BadRequestException.class) - public void should_thow_if_create_fails() { + public void should_throw_exception_if_create_fails() { String componentKey = "new-project"; String componentName = "New Project"; String qualifier = Qualifiers.PROJECT; @@ -132,53 +112,11 @@ public void should_thow_if_create_fails() { service.createComponent(componentKey, componentName, qualifier); } - @Test(expected = BadRequestException.class) - public void should_throw_if_component_already_exists() { - String componentKey = "new-project"; - String componentName = "New Project"; - String qualifier = Qualifiers.PROJECT; - when(resourceDao.findByKey(componentKey)).thenReturn(mock(ComponentDto.class)); - - service.createComponent(componentKey, componentName, qualifier); - } - @Test(expected = BadRequestException.class) public void should_throw_if_malformed_key1() { service.createComponent("1234", "New Project", Qualifiers.PROJECT); } - @Test(expected = NotFoundException.class) - public void should_throw_if_updating_unknown_component() { - final long componentId = 1234l; - when(resourceDao.getResource(componentId)).thenReturn(null); - service.updateComponent(componentId, "key", "name"); - } - - @Test - public void should_update_component() { - final long componentId = 1234l; - final String newKey = "newKey"; - final String newName = "newName"; - ResourceDto resource = mock(ResourceDto.class); - when(resourceDao.getResource(componentId)).thenReturn(resource); - when(resource.setKey(newKey)).thenReturn(resource); - when(resource.setName(newName)).thenReturn(resource); - service.updateComponent(componentId, newKey, newName); - verify(resource).setKey(newKey); - verify(resource).setName(newName); - verify(resourceDao).insertOrUpdate(resource); - } - - @Test(expected=BadRequestException.class) - public void should_throw_if_malformed_key_in_update() { - final long componentId = 1234l; - final String newKey = "new/key"; - final String newName = "newName"; - ResourceDto resource = mock(ResourceDto.class); - when(resourceDao.getResource(componentId)).thenReturn(resource); - service.updateComponent(componentId, newKey, newName); - } - @Test public void should_find() { List qualifiers = newArrayList("TRK"); diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/projects_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/projects_controller.rb index 9e26b80df1f0..cfd97e37cb5a 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/projects_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/api/projects_controller.rb @@ -71,20 +71,16 @@ def destroy # POST /api/projects/create?key=&name= # # -- Example - # curl -v -u admin:admin -X POST 'http://localhost:9000/api/projects/create?key=project1&name=Project%20One' + # curl -v -u admin:admin -X POST 'http://localhost:9000/api/projects/create?key=project1&name=Project%20One&branch=origin/master' # # since 4.0 # def create verify_post_request require_parameters :key, :name - access_denied unless has_role?("provisioning") - key = params[:key] - name = params[:name] - Internal.component_api.createComponent(key, name, 'TRK') - Internal.permissions.applyDefaultPermissionTemplate(key) - result = Project.by_key(key) + id = Internal.component_api.createComponent(params[:key], params[:branch], params[:name], nil) + result = Project.find(id.to_i) respond_to do |format| format.json { render :json => jsonp(to_json_hash(result)) } diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/provisioning_controller.rb b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/provisioning_controller.rb index 4eb611603794..1b91cc25eca6 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/provisioning_controller.rb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/controllers/provisioning_controller.rb @@ -33,34 +33,23 @@ def index ) { |p| p.key } end - def create_or_update + def create verify_post_request - access_denied unless has_role?("provisioning") @id = params[:id] @key = params[:key] @name = params[:name] + @branch = params[:branch] begin bad_request('provisioning.missing.key') if @key.blank? bad_request('provisioning.missing.name') if @name.blank? - if @id.nil? or @id.empty? - Internal.component_api.createComponent(@key, @name, 'TRK') - begin - Internal.permissions.applyDefaultPermissionTemplate(@key) - rescue - # Programmatic transaction rollback - Java::OrgSonarServerUi::JRubyFacade.getInstance().deleteResourceTree(@key) - raise - end - else - Internal.component_api.updateComponent(@id.to_i, @key, @name) - end + Internal.component_api.createComponent(@key, @branch, @name, nil) redirect_to :action => 'index' rescue Exception => e flash.now[:error]= Api::Utils.message(e.message) - render :partial => 'create_form', :id => @id, :key => @key, :name => @name, :status => 400 + render :partial => 'create_form', :key => @key, :branch => @branch, :name => @name, :status => 400 end end diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/provisioning/_create_form.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/provisioning/_create_form.html.erb index b96dbce2506e..5b2bd449e409 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/provisioning/_create_form.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/provisioning/_create_form.html.erb @@ -1,8 +1,7 @@ -
- +
+
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/provisioning/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/provisioning/index.html.erb index 39ae046435fb..633629c0a24e 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/provisioning/index.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/provisioning/index.html.erb @@ -33,9 +33,6 @@ <%= h resource.name -%> <%= format_datetime(resource.created_at) -%> - <%= link_to message('edit'), {:action => :create_form, :id => resource.id, :key => resource.key, :name => resource.name}, - {:id => "edit-#{resource.key.parameterize}", :class => 'open-modal link-action'} -%> -   <%= link_to message('delete'), {:action => :delete_form, :id => resource.id}, {:id => "delete-#{resource.key.parameterize}", :class => 'open-modal link-action link-red'} -%> diff --git a/sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java b/sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java index 615fe1304b43..9fbc4267b59e 100644 --- a/sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java +++ b/sonar-core/src/main/java/org/sonar/core/component/ComponentKeys.java @@ -105,8 +105,8 @@ public static boolean isValidModuleKey(String keyCandidate) { * * * - * @param keyCandidate - * @return true if keyCandidate can be used for a project/module + * @param branchCandidate + * @return true if branchCandidate can be used for a project/module */ public static boolean isValidBranch(String branchCandidate) { return branchCandidate.matches(VALID_BRANCH_REGEXP); diff --git a/sonar-core/src/main/java/org/sonar/core/resource/ResourceIndexerDao.java b/sonar-core/src/main/java/org/sonar/core/resource/ResourceIndexerDao.java index c2f648720bba..0befc3ad53ff 100644 --- a/sonar-core/src/main/java/org/sonar/core/resource/ResourceIndexerDao.java +++ b/sonar-core/src/main/java/org/sonar/core/resource/ResourceIndexerDao.java @@ -132,24 +132,28 @@ void doIndex(ResourceDto resource, ResourceIndexerMapper mapper) { } public boolean indexResource(long id) { - boolean indexed = false; - SqlSession session = mybatis.openSession(false); + DbSession session = mybatis.openSession(false); try { - ResourceIndexerMapper mapper = session.getMapper(ResourceIndexerMapper.class); - ResourceDto resource = mapper.selectResourceToIndex(id); - if (resource != null) { - Long rootId = resource.getRootId(); - if (rootId == null) { - rootId = resource.getId(); - } - indexed = indexResource(resource.getId(), resource.getName(), resource.getQualifier(), rootId, session, mapper); - } - return indexed; + return indexResource(session, id); } finally { MyBatis.closeQuietly(session); } } + public boolean indexResource(DbSession session, long id) { + boolean indexed = false; + ResourceIndexerMapper mapper = session.getMapper(ResourceIndexerMapper.class); + ResourceDto resource = mapper.selectResourceToIndex(id); + if (resource != null) { + Long rootId = resource.getRootId(); + if (rootId == null) { + rootId = resource.getId(); + } + indexed = indexResource(resource.getId(), resource.getName(), resource.getQualifier(), rootId, session, mapper); + } + return indexed; + } + public boolean indexResource(int id, String name, String qualifier, int rootId) { boolean indexed = false; SqlSession session = mybatis.openSession(false); diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index ade5dec40c98..98c8bbb951a6 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -25,6 +25,7 @@ backup=Backup backup_verb=Back up blocker=Blocker bold=Bold +branch=Branch build_date=Build date build_time=Build time calendar=Calendar @@ -379,7 +380,6 @@ qualifiers.create.TRK=Create Project qualifiers.create.VW=Create View qualifiers.create.DEV=Create Developer -qualifiers.update.TRK=Update Project qualifiers.update.VW=Update View qualifiers.update.DEV=Update Developer