@@ -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 class ComponentService implements ServerComponent { | |||
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 class ComponentService implements ServerComponent { | |||
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 class ComponentService implements ServerComponent { | |||
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 class ComponentService implements ServerComponent { | |||
} | |||
} | |||
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); | |||
} | |||
} |
@@ -24,33 +24,28 @@ import org.sonar.api.component.Component; | |||
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 class DefaultRubyComponentService implements RubyComponentService { | |||
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<String, Object> params) { | |||
ComponentQuery query = toQuery(params); | |||
List<Component> components = resourceDao.selectProjectsByQualifiers(query.qualifiers()); | |||
@@ -158,14 +133,4 @@ public class DefaultRubyComponentService implements RubyComponentService { | |||
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); | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -24,25 +24,26 @@ import org.junit.After; | |||
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 class ComponentServiceMediumTest { | |||
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 class ComponentServiceMediumTest { | |||
@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 class ComponentServiceMediumTest { | |||
@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 class ComponentServiceMediumTest { | |||
@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 class ComponentServiceMediumTest { | |||
@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 class ComponentServiceMediumTest { | |||
@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 class ComponentServiceMediumTest { | |||
@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(); | |||
} | |||
} |
@@ -26,13 +26,9 @@ import org.mockito.ArgumentCaptor; | |||
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 class DefaultRubyComponentServiceTest { | |||
} | |||
@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<ResourceDto> 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<NewComponent> 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 class DefaultRubyComponentServiceTest { | |||
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<String> qualifiers = newArrayList("TRK"); |
@@ -71,20 +71,16 @@ class Api::ProjectsController < Api::ApiController | |||
# POST /api/projects/create?key=<key>&name=<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)) } |
@@ -33,34 +33,23 @@ class ProvisioningController < ApplicationController | |||
) { |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 | |||
@@ -1,8 +1,7 @@ | |||
<form id="create-resource-form" method="post" action="<%= ApplicationController.root_context -%>/provisioning/create_or_update"> | |||
<input type="hidden" name="id" value="<%= @id -%>"/> | |||
<form id="create-resource-form" method="post" action="<%= ApplicationController.root_context -%>/provisioning/create"> | |||
<fieldset> | |||
<div class="modal-head"> | |||
<h2><%= message((@id.nil? || @id.empty?) ? 'qualifiers.new.TRK' : 'qualifiers.update.TRK') -%></h2> | |||
<h2><%= message('qualifiers.new.TRK') -%></h2> | |||
</div> | |||
<div class="modal-body"> | |||
<% if flash.now[:error] %> | |||
@@ -12,13 +11,17 @@ | |||
<label for="key"><%= h message('key') -%> <em class="mandatory">*</em></label> | |||
<input id="key" name="key" value="<%= h @key -%>" type="text" size="50" maxlength="400" autofocus="autofocus"/> | |||
</div> | |||
<div class="modal-field"> | |||
<label for="branch"><%= h message('branch') -%></label> | |||
<input id="branch" name="branch" value="<%= h @branch -%>" type="text" size="50" maxlength="400" autofocus="autofocus"/> | |||
</div> | |||
<div class="modal-field"> | |||
<label for="name"><%= h message('name') -%> <em class="mandatory">*</em></label> | |||
<input id="name" name="name" value="<%= h @name -%>" type="text" size="50" maxlength="256" value=""/> | |||
</div> | |||
</div> | |||
<div class="modal-foot"> | |||
<input type="submit" value="<%= h message((@id.nil? || @id.empty?) ? 'qualifiers.create.TRK' : 'qualifiers.update.TRK') -%>" id="save-submit"/> | |||
<input type="submit" value="<%= h message('qualifiers.create.TRK') -%>" id="save-submit"/> | |||
<a href="#" onclick="return closeModalWindow()" id="save-cancel"><%= h message('cancel') -%></a> | |||
</div> | |||
</fieldset> |
@@ -33,9 +33,6 @@ | |||
<td><%= h resource.name -%></td> | |||
<td><%= format_datetime(resource.created_at) -%></td> | |||
<td class="operations"> | |||
<%= 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'} -%> | |||
</td> |
@@ -105,8 +105,8 @@ public final class ComponentKeys { | |||
* </ul> | |||
* </li> | |||
* </ul> | |||
* @param keyCandidate | |||
* @return <code>true</code> if <code>keyCandidate</code> can be used for a project/module | |||
* @param branchCandidate | |||
* @return <code>true</code> if <code>branchCandidate</code> can be used for a project/module | |||
*/ | |||
public static boolean isValidBranch(String branchCandidate) { | |||
return branchCandidate.matches(VALID_BRANCH_REGEXP); |
@@ -132,24 +132,28 @@ public class ResourceIndexerDao { | |||
} | |||
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); |
@@ -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 | |||