package org.sonar.server.component;
+import com.google.common.base.Function;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Collections2;
+import org.apache.commons.collections.CollectionUtils;
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.exceptions.NotFoundException;
+ import org.sonar.server.permission.InternalPermissionService;
import org.sonar.server.user.UserSession;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
- import java.util.Collection;
- import java.util.List;
-import java.util.Date;
-import java.util.Locale;
--import java.util.Map;
++import java.util.*;
+
+import static com.google.common.collect.Lists.newArrayList;
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();
+ }
+ }
+
+ public Collection<String> componentUuids(@Nullable Collection<String> componentKeys) {
+ DbSession session = dbClient.openSession(false);
+ try {
+ return componentUuids(session, componentKeys, false);
+ } finally {
+ session.close();
+ }
+ }
+
+ public Collection<String> componentUuids(DbSession session, @Nullable Collection<String> componentKeys, boolean ignoreMissingComponents) {
+ Collection<String> componentUuids = newArrayList();
+ if (componentKeys != null && !componentKeys.isEmpty()) {
+ List<ComponentDto> components = dbClient.componentDao().getByKeys(session, componentKeys);
+
+ if (!ignoreMissingComponents && components.size() < componentKeys.size()) {
+ Collection<String> foundKeys = Collections2.transform(components, new Function<ComponentDto, String>() {
+ @Override
+ public String apply(ComponentDto component) {
+ return component.key();
+ }
+ });
+ throw new NotFoundException("The following component keys do not match any component:\n" +
+ Joiner.on('\n').join(CollectionUtils.subtract(componentKeys, foundKeys)));
+ }
+
+ for (ComponentDto component : components) {
+ componentUuids.add(component.uuid());
+ }
+ }
+ return componentUuids;
+ }
++
+ 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);
+ }
+
}
package org.sonar.server.component;
- import org.sonar.server.exceptions.NotFoundException;
-
+import org.fest.assertions.Fail;
- import com.google.common.collect.Lists;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
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.exceptions.NotFoundException;
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;
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");
+ }
+ }
+
+ @Test
+ public void should_return_project_uuids() throws Exception {
++ ComponentDto project = createProject("sample:root");
+ String moduleKey = "sample:root:module";
+ ComponentDto module = ComponentTesting.newModuleDto(project).setKey(moduleKey);
+ tester.get(ComponentDao.class).insert(session, module);
+ String fileKey = "sample:root:module:Foo.xoo";
+ ComponentDto file = ComponentTesting.newFileDto(module).setKey(fileKey);
+ tester.get(ComponentDao.class).insert(session, file);
+ session.commit();
+
+ assertThat(service.componentUuids(Arrays.asList(moduleKey, fileKey))).hasSize(2);
+ assertThat(service.componentUuids(null)).isEmpty();
+ assertThat(service.componentUuids(Arrays.<String>asList())).isEmpty();
+ }
+
+ @Test
+ public void should_fail_on_components_not_found() throws Exception {
+ String moduleKey = "sample:root:module";
+ String fileKey = "sample:root:module:Foo.xoo";
+
+ try {
+ service.componentUuids(Arrays.asList(moduleKey, fileKey));
+ Fail.fail("Should throw NotFoundException");
+ } catch(NotFoundException notFound) {
+ assertThat(notFound.getMessage()).contains(moduleKey).contains(fileKey);
+ }
+ }
+
+ @Test
+ public void should_fail_silently_on_components_not_found_if_told_so() throws Exception {
+ String moduleKey = "sample:root:module";
+ String fileKey = "sample:root:module:Foo.xoo";
+
+ assertThat(service.componentUuids(session, Arrays.asList(moduleKey, fileKey), true)).isEmpty();
+ }
++
+ 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();
+ }
+
}