private static final Set<String> PROJECT_OR_MODULE_QUALIFIERS = ImmutableSet.of(Qualifiers.PROJECT, Qualifiers.MODULE);
- public void updateKey(DbSession dbSession, String projectUuid, String newKey) {
+ public void updateKey(DbSession dbSession, String projectOrModuleUuid, String newKey) {
ComponentKeyUpdaterMapper mapper = dbSession.getMapper(ComponentKeyUpdaterMapper.class);
if (mapper.countResourceByKey(newKey) > 0) {
throw new IllegalArgumentException("Impossible to update key: a component with key \"" + newKey + "\" already exists.");
}
// must SELECT first everything
- ResourceDto project = mapper.selectProject(projectUuid);
+ ResourceDto project = mapper.selectProject(projectOrModuleUuid);
String projectOldKey = project.getKey();
- List<ResourceDto> resources = mapper.selectProjectResources(projectUuid);
+ List<ResourceDto> resources = mapper.selectProjectResources(projectOrModuleUuid);
resources.add(project);
// and then proceed with the batch UPDATE at once
runBatchUpdateForAllResources(resources, projectOldKey, newKey, mapper);
-
- dbSession.commit();
}
public static void checkIsProjectOrModule(ComponentDto component) {
<select id="selectProject" parameterType="String" resultMap="resourceResultMap">
select * from projects
- where uuid=#{uuid,jdbcType=VARCHAR}
+ where uuid = #{uuid,jdbcType=VARCHAR}
</select>
<select id="selectProjectResources" parameterType="String" resultMap="resourceResultMap">
select * from projects
where
- root_uuid=#{rootUuid,jdbcType=VARCHAR}
- and scope!='PRJ'
- and enabled=${_true}
+ root_uuid = #{rootUuid,jdbcType=VARCHAR}
+ and scope != 'PRJ'
+ and enabled = ${_true}
</select>
<select id="selectDescendantProjects" parameterType="String" resultMap="resourceResultMap">
p.enabled=${_true}
and p.copy_component_uuid is null
<if test="projectUuid != null">
- and p.project_uuid=#{projectUuid,jdbcType=VARCHAR}
+ and p.project_uuid = #{projectUuid,jdbcType=VARCHAR}
</if>
</select>
db.prepareDbUnit(getClass(), "shared.xml");
underTest.updateKey(dbSession, "B", "struts:core");
+ dbSession.commit();
db.assertDbUnit(getClass(), "shouldUpdateKey-result.xml", "projects");
}
db.components().insertComponent(newFileDto(project, inactiveDirectory).setKey("my_project:inactive_directory/file").setEnabled(false));
underTest.updateKey(dbSession, "A", "your_project");
- db.commit();
+ dbSession.commit();
List<ComponentDto> result = dbClient.componentDao().selectAllComponentsFromProjectKey(dbSession, "your_project");
assertThat(result).hasSize(5).extracting(ComponentDto::getKey)
throw new IllegalArgumentException("Only projects can be deleted");
}
dbClient.purgeDao().deleteRootComponent(dbSession, project.uuid());
- projectIndexers.commitAndIndex(dbSession, singletonList(project.uuid()), ProjectIndexer.Cause.PROJECT_DELETION);
+ projectIndexers.commitAndIndex(dbSession, singletonList(project), ProjectIndexer.Cause.PROJECT_DELETION);
}
private static boolean hasNotProjectScope(ComponentDto project) {
}
// TODO should be moved to UpdateKeyAction
- public void updateKey(DbSession dbSession, ComponentDto component, String newKey) {
- userSession.checkComponentPermission(UserRole.ADMIN, component);
- checkIsProjectOrModule(component);
+ public void updateKey(DbSession dbSession, ComponentDto projectOrModule, String newKey) {
+ userSession.checkComponentPermission(UserRole.ADMIN, projectOrModule);
+ checkIsProjectOrModule(projectOrModule);
checkProjectOrModuleKeyFormat(newKey);
- dbClient.componentKeyUpdaterDao().updateKey(dbSession, component.uuid(), newKey);
- projectIndexers.commitAndIndex(dbSession, singletonList(component.uuid()), ProjectIndexer.Cause.PROJECT_KEY_UPDATE);
+ dbClient.componentKeyUpdaterDao().updateKey(dbSession, projectOrModule.uuid(), newKey);
+ projectIndexers.commitAndIndex(dbSession, singletonList(projectOrModule), ProjectIndexer.Cause.PROJECT_KEY_UPDATE);
}
// TODO should be moved to BulkUpdateKeyAction
- public void bulkUpdateKey(DbSession dbSession, String projectUuid, String stringToReplace, String replacementString) {
- dbClient.componentKeyUpdaterDao().bulkUpdateKey(dbSession, projectUuid, stringToReplace, replacementString);
- projectIndexers.commitAndIndex(dbSession, singletonList(projectUuid), ProjectIndexer.Cause.PROJECT_KEY_UPDATE);
+ public void bulkUpdateKey(DbSession dbSession, ComponentDto projectOrModule, String stringToReplace, String replacementString) {
+ dbClient.componentKeyUpdaterDao().bulkUpdateKey(dbSession, projectOrModule.uuid(), stringToReplace, replacementString);
+ projectIndexers.commitAndIndex(dbSession, singletonList(projectOrModule), ProjectIndexer.Cause.PROJECT_KEY_UPDATE);
}
private static void checkProjectOrModuleKeyFormat(String key) {
ComponentDto componentDto = createRootComponent(dbSession, newComponent);
removeDuplicatedProjects(dbSession, componentDto.getKey());
handlePermissionTemplate(dbSession, componentDto, newComponent.getOrganizationUuid(), userId);
- projectIndexers.commitAndIndex(dbSession, singletonList(componentDto.uuid()), Cause.PROJECT_CREATION);
+ projectIndexers.commitAndIndex(dbSession, singletonList(componentDto), Cause.PROJECT_CREATION);
return componentDto;
}
package org.sonar.server.es;
import java.util.Collection;
+import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbSession;
+import org.sonar.db.component.ComponentDto;
public interface ProjectIndexers {
/**
* Commits the DB transaction and indexes the specified projects, if needed (according to
* "cause" parameter).
+ * IMPORTANT - UUIDs must relate to projects only. Modules, directories and files are forbidden
+ * and will lead to lack of indexing.
*/
- void commitAndIndex(DbSession dbSession, Collection<String> projectUuid, ProjectIndexer.Cause cause);
+ void commitAndIndexByProjectUuids(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause);
+
+ default void commitAndIndex(DbSession dbSession, Collection<ComponentDto> projectOrModules, ProjectIndexer.Cause cause) {
+ Collection<String> projectUuids = projectOrModules.stream()
+ .map(ComponentDto::projectUuid)
+ .collect(MoreCollectors.toSet(projectOrModules.size()));
+ commitAndIndexByProjectUuids(dbSession, projectUuids, cause);
+ }
}
}
@Override
- public void commitAndIndex(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause) {
+ public void commitAndIndexByProjectUuids(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause) {
Map<ProjectIndexer, Collection<EsQueueDto>> itemsByIndexer = new IdentityHashMap<>();
indexers.forEach(i -> itemsByIndexer.put(i, i.prepareForRecovery(dbSession, projectUuids, cause)));
dbSession.commit();
import org.sonar.api.server.ServerSide;
import org.sonar.core.component.ComponentKeys;
import org.sonar.core.permission.ProjectPermissions;
-import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.component.ComponentDto;
for (ComponentDto project : projects) {
copyPermissions(dbSession, template, project, null);
}
- projectIndexers.commitAndIndex(dbSession, projects.stream().map(ComponentDto::uuid).collect(MoreCollectors.toList()), ProjectIndexer.Cause.PERMISSION_CHANGE);
+ projectIndexers.commitAndIndex(dbSession, projects, ProjectIndexer.Cause.PERMISSION_CHANGE);
}
/**
private final GroupPermissionChanger groupPermissionChanger;
public PermissionUpdater(DbClient dbClient, ProjectIndexers projectIndexers,
- UserPermissionChanger userPermissionChanger, GroupPermissionChanger groupPermissionChanger) {
+ UserPermissionChanger userPermissionChanger, GroupPermissionChanger groupPermissionChanger) {
this.dbClient = dbClient;
this.projectIndexers = projectIndexers;
this.userPermissionChanger = userPermissionChanger;
dbClient.resourceDao().updateAuthorizationDate(projectId, dbSession);
}
- projectIndexers.commitAndIndex(dbSession, projectOrViewUuids, ProjectIndexer.Cause.PERMISSION_CHANGE);
+ projectIndexers.commitAndIndexByProjectUuids(dbSession, projectOrViewUuids, ProjectIndexer.Cause.PERMISSION_CHANGE);
}
private boolean doApply(DbSession dbSession, PermissionChange change) {
}
private void bulkUpdateKey(DbSession dbSession, BulkUpdateKeyWsRequest request, ComponentDto projectOrModule) {
- componentService.bulkUpdateKey(dbSession, projectOrModule.uuid(), request.getFrom(), request.getTo());
- dbSession.commit();
+ componentService.bulkUpdateKey(dbSession, projectOrModule, request.getFrom(), request.getTo());
}
private static BulkUpdateKeyWsResponse buildResponse(Map<String, String> newKeysByOldKeys, Map<String, Boolean> newKeysWithDuplicateMap) {
try (DbSession dbSession = dbClient.openSession(false)) {
ComponentDto projectOrModule = componentFinder.getByUuidOrKey(dbSession, request.getId(), request.getKey(), ParamNames.PROJECT_ID_AND_FROM);
componentService.updateKey(dbSession, projectOrModule, request.getNewKey());
- dbSession.commit();
}
}
} else {
updatePermissionsToPublic(dbSession, component);
}
- projectIndexers.commitAndIndex(dbSession, singletonList(component.uuid()), ProjectIndexer.Cause.PERMISSION_CHANGE);
+ projectIndexers.commitAndIndex(dbSession, singletonList(component), ProjectIndexer.Cause.PERMISSION_CHANGE);
}
}
}
project.setTags(tags);
dbClient.componentDao().updateTags(dbSession, project);
- projectIndexers.commitAndIndex(dbSession, singletonList(project.uuid()), PROJECT_TAGS_UPDATE);
+ projectIndexers.commitAndIndex(dbSession, singletonList(project), PROJECT_TAGS_UPDATE);
}
response.noContent();
ComponentDto file = componentDb.insertComponent(newFileDto(module, null).setKey("my_project:root:module:src/File.xoo"));
ComponentDto inactiveFile = componentDb.insertComponent(newFileDto(module, null).setKey("my_project:root:module:src/InactiveFile.xoo").setEnabled(false));
- underTest.bulkUpdateKey(dbSession, project.uuid(), "my_", "your_");
+ underTest.bulkUpdateKey(dbSession, project, "my_", "your_");
assertComponentKeyUpdated(project.key(), "your_project");
assertComponentKeyUpdated(module.key(), "your_project:root:module");
public void update_module_key() {
ComponentDto project = insertSampleRootProject();
ComponentDto module = ComponentTesting.newModuleDto(project).setKey("sample:root:module");
- dbClient.componentDao().insert(dbSession, module);
+ db.components().insertComponent(module);
ComponentDto file = ComponentTesting.newFileDto(module, null).setKey("sample:root:module:src/File.xoo");
- dbClient.componentDao().insert(dbSession, file);
- dbSession.commit();
+ db.components().insertComponent(file);
logInAsProjectAdministrator(project);
underTest.updateKey(dbSession, module, "sample:root2:module");
assertComponentKeyHasBeenUpdated(module.key(), "sample:root2:module");
assertComponentKeyHasBeenUpdated(file.key(), "sample:root2:module:src/File.xoo");
- org.assertj.core.api.Assertions.assertThat(projectIndexers.hasBeenCalled(module.uuid(), ProjectIndexer.Cause.PROJECT_KEY_UPDATE)).isTrue();
+ // do not index the module but the project
+ org.assertj.core.api.Assertions.assertThat(projectIndexers.hasBeenCalled(project.uuid(), ProjectIndexer.Cause.PROJECT_KEY_UPDATE)).isTrue();
}
@Test
ComponentDto file = componentDb.insertComponent(newFileDto(module, null).setKey("my_project:root:module:src/File.xoo"));
ComponentDto inactiveFile = componentDb.insertComponent(newFileDto(module, null).setKey("my_project:root:module:src/InactiveFile.xoo").setEnabled(false));
- underTest.bulkUpdateKey(dbSession, project.uuid(), "my_", "your_");
+ underTest.bulkUpdateKey(dbSession, project, "my_", "your_");
assertComponentKeyUpdated(project.key(), "your_project");
assertComponentKeyUpdated(module.key(), "your_project:root:module");
ComponentDto project = db.components().insertPrivateProject();
ComponentDto file = db.components().insertComponent(newFileDto(project));
indexProject(project, PROJECT_CREATION);
+ assertThatIndexHasSize(2);
db.getDbClient().componentDao().delete(db.getSession(), project.getId());
db.getDbClient().componentDao().delete(db.getSession(), file.getId());
*/
package org.sonar.server.es;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
-import java.util.Set;
import org.junit.Test;
import org.sonar.db.DbSession;
-import org.sonar.db.es.EsQueueDto;
+import org.sonar.db.component.ComponentDto;
+import org.sonar.db.component.ComponentTesting;
+import org.sonar.db.organization.OrganizationDto;
+import org.sonar.db.organization.OrganizationTesting;
+import org.sonar.server.es.ProjectIndexer.Cause;
-import static java.util.Arrays.asList;
import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
public class ProjectIndexersImplTest {
@Test
- public void commitAndIndex_calls_indexer_with_only_its_supported_items() {
- EsQueueDto item1a = EsQueueDto.create("fake/fake1", "P1");
- EsQueueDto item1b = EsQueueDto.create("fake/fake1", "P1");
- EsQueueDto item2 = EsQueueDto.create("fake/fake2", "P1");
- FakeIndexer indexer1 = new FakeIndexer(asList(item1a, item1b));
- FakeIndexer indexer2 = new FakeIndexer(singletonList(item2));
- DbSession dbSession = mock(DbSession.class);
+ public void commitAndIndex_indexes_project() {
+ OrganizationDto organization = OrganizationTesting.newOrganizationDto();
+ ComponentDto project = ComponentTesting.newPublicProjectDto(organization);
- ProjectIndexersImpl underTest = new ProjectIndexersImpl(indexer1, indexer2);
- underTest.commitAndIndex(dbSession, singletonList("P1"), ProjectIndexer.Cause.PROJECT_CREATION);
+ FakeIndexers underTest = new FakeIndexers();
+ underTest.commitAndIndex(mock(DbSession.class), singletonList(project), Cause.PROJECT_CREATION);
- assertThat(indexer1.calledItems).containsExactlyInAnyOrder(item1a, item1b);
- assertThat(indexer2.calledItems).containsExactlyInAnyOrder(item2);
+ assertThat(underTest.calls).containsExactly(project.uuid());
}
- private static class FakeIndexer implements ProjectIndexer {
-
- private final List<EsQueueDto> items;
- private Collection<EsQueueDto> calledItems;
-
- private FakeIndexer(List<EsQueueDto> items) {
- this.items = items;
- }
-
- @Override
- public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
- throw new UnsupportedOperationException();
- }
+ @Test
+ public void commitAndIndex_of_module_indexes_the_project() {
+ OrganizationDto organization = OrganizationTesting.newOrganizationDto();
+ ComponentDto project = ComponentTesting.newPublicProjectDto(organization);
+ ComponentDto module = ComponentTesting.newModuleDto(project);
- @Override
- public Set<IndexType> getIndexTypes() {
- throw new UnsupportedOperationException();
- }
+ FakeIndexers underTest = new FakeIndexers();
+ underTest.commitAndIndex(mock(DbSession.class), singletonList(module), Cause.PROJECT_CREATION);
- @Override
- public Collection<EsQueueDto> prepareForRecovery(DbSession dbSession, Collection<String> projectUuids, Cause cause) {
- return items;
- }
+ assertThat(underTest.calls).containsExactly(project.uuid());
+ }
- @Override
- public IndexingResult index(DbSession dbSession, Collection<EsQueueDto> items) {
- this.calledItems = items;
- return new IndexingResult();
- }
+ private static class FakeIndexers implements ProjectIndexers {
+ private final List<String> calls = new ArrayList<>();
@Override
- public void indexOnAnalysis(String projectUuid) {
- throw new UnsupportedOperationException();
+ public void commitAndIndexByProjectUuids(DbSession dbSession, Collection<String> projectUuids, Cause cause) {
+ calls.addAll(projectUuids);
}
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+ package org.sonar.server.es;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
+import org.junit.Test;
+import org.sonar.db.DbSession;
+import org.sonar.db.es.EsQueueDto;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+public class ProjectIndexersTest {
+
+ @Test
+ public void commitAndIndexByProjectUuids_calls_indexer_with_only_its_supported_items() {
+ EsQueueDto item1a = EsQueueDto.create("fake/fake1", "P1");
+ EsQueueDto item1b = EsQueueDto.create("fake/fake1", "P1");
+ EsQueueDto item2 = EsQueueDto.create("fake/fake2", "P1");
+ FakeIndexer indexer1 = new FakeIndexer(asList(item1a, item1b));
+ FakeIndexer indexer2 = new FakeIndexer(singletonList(item2));
+ DbSession dbSession = mock(DbSession.class);
+
+ ProjectIndexersImpl underTest = new ProjectIndexersImpl(indexer1, indexer2);
+ underTest.commitAndIndexByProjectUuids(dbSession, singletonList("P1"), ProjectIndexer.Cause.PROJECT_CREATION);
+
+ assertThat(indexer1.calledItems).containsExactlyInAnyOrder(item1a, item1b);
+ assertThat(indexer2.calledItems).containsExactlyInAnyOrder(item2);
+ }
+
+ @Test
+ public void commitAndIndex_restricts_indexing_to_projects() {
+ // TODO
+ }
+
+ private static class FakeIndexer implements ProjectIndexer {
+
+ private final List<EsQueueDto> items;
+ private Collection<EsQueueDto> calledItems;
+
+ private FakeIndexer(List<EsQueueDto> items) {
+ this.items = items;
+ }
+
+ @Override
+ public void indexOnStartup(Set<IndexType> uninitializedIndexTypes) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Set<IndexType> getIndexTypes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Collection<EsQueueDto> prepareForRecovery(DbSession dbSession, Collection<String> projectUuids, Cause cause) {
+ return items;
+ }
+
+ @Override
+ public IndexingResult index(DbSession dbSession, Collection<EsQueueDto> items) {
+ this.calledItems = items;
+ return new IndexingResult();
+ }
+
+ @Override
+ public void indexOnAnalysis(String projectUuid) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
private final ListMultimap<String, ProjectIndexer.Cause> calls = ArrayListMultimap.create();
@Override
- public void commitAndIndex(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause) {
+ public void commitAndIndexByProjectUuids(DbSession dbSession, Collection<String> projectUuids, ProjectIndexer.Cause cause) {
dbSession.commit();
projectUuids.forEach(projectUuid -> calls.put(projectUuid, cause));
tuple(project.key(), "your_project", false),
tuple(module.key(), "your_project:root:module", false));
- verify(componentService).bulkUpdateKey(any(DbSession.class), eq(project.uuid()), eq(FROM), eq(TO));
+ verify(componentService).bulkUpdateKey(any(DbSession.class), eq(project), eq(FROM), eq(TO));
}
@Test
callByKey(provisionedProject.key(), provisionedProject.getKey(), newKey);
- verify(componentService).bulkUpdateKey(any(DbSession.class), eq(provisionedProject.uuid()), eq(provisionedProject.getKey()), eq(newKey));
+ verify(componentService).bulkUpdateKey(any(DbSession.class), eq(provisionedProject), eq(provisionedProject.getKey()), eq(newKey));
}
@Test
import org.sonarqube.tests.organization.PersonalOrganizationTest;
import org.sonarqube.tests.organization.RootUserOnOrganizationTest;
import org.sonarqube.tests.projectAdministration.ProjectDeletionTest;
+import org.sonarqube.tests.projectAdministration.ProjectKeyUpdateTest;
import org.sonarqube.tests.projectAdministration.ProjectProvisioningTest;
import org.sonarqube.tests.projectSearch.LeakProjectsPageTest;
import org.sonarqube.tests.projectSearch.SearchProjectsTest;
SearchProjectsTest.class,
RulesWsTest.class,
ProjectDeletionTest.class,
- ProjectProvisioningTest.class
+ ProjectProvisioningTest.class,
+ ProjectKeyUpdateTest.class
})
public class Category6Suite {
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
package org.sonarqube.tests;
import java.net.InetAddress;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonarqube.tests.projectAdministration;
-
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarScanner;
-import org.sonarqube.tests.Category1Suite;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.sonarqube.ws.client.PostRequest;
-import org.sonarqube.ws.client.WsClient;
-import org.sonarqube.pageobjects.Navigation;
-import org.sonarqube.pageobjects.ProjectKeyPage;
-
-import static com.codeborne.selenide.Condition.visible;
-import static com.codeborne.selenide.Selenide.$;
-import static com.codeborne.selenide.WebDriverRunner.url;
-import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.newAdminWsClient;
-import static util.ItUtils.projectDir;
-
-public class ProjectKeyPageTest {
-
- @ClassRule
- public static Orchestrator ORCHESTRATOR = Category1Suite.ORCHESTRATOR;
-
- private static WsClient wsClient;
-
- @BeforeClass
- public static void setUp() {
- wsClient = newAdminWsClient(ORCHESTRATOR);
- }
-
- @Before
- public void cleanUp() {
- ORCHESTRATOR.resetData();
- }
-
- private Navigation nav = Navigation.create(ORCHESTRATOR);
-
- @Test
- public void change_key_when_no_modules() {
- createProject("sample");
-
- ProjectKeyPage page = openPage("sample");
- page.assertSimpleUpdate().trySimpleUpdate("another");
-
- assertThat(url()).endsWith("/project/key?id=another");
- }
-
- @Test
- public void fail_to_change_key_when_no_modules() {
- createProject("sample");
- createProject("another");
-
- ProjectKeyPage page = openPage("sample");
- page.assertSimpleUpdate().trySimpleUpdate("another");
-
- $(".alert.alert-danger").shouldBe(visible);
- assertThat(url()).endsWith("/project/key?id=sample");
- }
-
- @Test
- public void change_key_of_multi_modules_project() {
- analyzeProject("shared/xoo-multi-modules-sample", "sample");
-
- ProjectKeyPage page = openPage("sample");
- page.openFineGrainedUpdate().tryFineGrainedUpdate("sample", "another");
-
- assertThat(url()).endsWith("/project/key?id=another");
- }
-
- @Test
- public void fail_to_change_key_of_multi_modules_project() {
- analyzeProject("shared/xoo-multi-modules-sample", "sample");
- createProject("another");
-
- ProjectKeyPage page = openPage("sample");
- page.openFineGrainedUpdate().tryFineGrainedUpdate("sample", "another");
-
- $(".alert.alert-danger").shouldBe(visible);
- assertThat(url()).endsWith("/project/key?id=sample");
- }
-
- @Test
- public void change_key_of_module_of_multi_modules_project() {
- analyzeProject("shared/xoo-multi-modules-sample", "sample");
-
- ProjectKeyPage page = openPage("sample");
- page.openFineGrainedUpdate().tryFineGrainedUpdate("sample:module_a:module_a1", "another");
-
- $("#update-key-confirmation-form").shouldNotBe(visible);
-
- nav.openProjectKey("another");
- assertThat(url()).endsWith("/project/key?id=another");
- }
-
- @Test
- public void fail_to_change_key_of_module_of_multi_modules_project() {
- analyzeProject("shared/xoo-multi-modules-sample", "sample");
- createProject("another");
-
- ProjectKeyPage page = openPage("sample");
- page.openFineGrainedUpdate().tryFineGrainedUpdate("sample:module_a:module_a1", "another");
-
- $(".alert.alert-danger").shouldBe(visible);
- }
-
- @Test
- public void bulk_change() {
- analyzeProject("shared/xoo-multi-modules-sample", "sample");
-
- ProjectKeyPage page = openPage("sample");
- page.assertBulkChange().simulateBulkChange("sample", "another");
-
- $("#bulk-update-results").shouldBe(visible);
- page.assertBulkChangeSimulationResult("sample", "another")
- .assertBulkChangeSimulationResult("sample:module_a:module_a1", "another:module_a:module_a1");
-
- page.confirmBulkUpdate().assertSuccessfulBulkUpdate();
- }
-
- @Test
- public void fail_to_bulk_change_because_no_changed_key() {
- analyzeProject("shared/xoo-multi-modules-sample", "sample");
-
- ProjectKeyPage page = openPage("sample");
- page.assertBulkChange().simulateBulkChange("random", "another");
-
- $("#bulk-update-nothing").shouldBe(visible);
- $("#bulk-update-results").shouldNotBe(visible);
- }
-
- @Test
- public void fail_to_bulk_change_because_of_duplications() {
- analyzeProject("shared/xoo-multi-modules-sample", "sample");
-
- ProjectKeyPage page = openPage("sample");
- page.assertBulkChange().simulateBulkChange("module_a1", "module_a2");
-
- $("#bulk-update-duplicate").shouldBe(visible);
- $("#bulk-update-results").shouldBe(visible);
-
- page.assertBulkChangeSimulationResult("sample:module_a:module_a1", "sample:module_a:module_a2")
- .assertDuplicated("sample:module_a:module_a1");
- }
-
- private ProjectKeyPage openPage(String projectKey) {
- nav.logIn().submitCredentials("admin", "admin");
- return nav.openProjectKey(projectKey);
- }
-
- private static void createProject(String projectKey) {
- wsClient.wsConnector().call(new PostRequest("api/projects/create")
- .setParam("key", projectKey)
- .setParam("name", projectKey));
- }
-
- private static void analyzeProject(String path, String projectKey) {
- ORCHESTRATOR.executeBuild(SonarScanner.create(projectDir(path))
- .setProjectKey(projectKey));
- }
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.tests.projectAdministration;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import org.sonarqube.tests.Category1Suite;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.pageobjects.Navigation;
+import org.sonarqube.pageobjects.ProjectKeyPage;
+
+import static com.codeborne.selenide.Condition.visible;
+import static com.codeborne.selenide.Selenide.$;
+import static com.codeborne.selenide.WebDriverRunner.url;
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.ItUtils.newAdminWsClient;
+import static util.ItUtils.projectDir;
+
+public class ProjectKeyUpdatePageTest {
+
+ @ClassRule
+ public static Orchestrator ORCHESTRATOR = Category1Suite.ORCHESTRATOR;
+
+ private static WsClient wsClient;
+
+ @BeforeClass
+ public static void setUp() {
+ wsClient = newAdminWsClient(ORCHESTRATOR);
+ }
+
+ @Before
+ public void cleanUp() {
+ ORCHESTRATOR.resetData();
+ }
+
+ private Navigation nav = Navigation.create(ORCHESTRATOR);
+
+ @Test
+ public void change_key_when_no_modules() {
+ createProject("sample");
+
+ ProjectKeyPage page = openPage("sample");
+ page.assertSimpleUpdate().trySimpleUpdate("another");
+
+ assertThat(url()).endsWith("/project/key?id=another");
+ }
+
+ @Test
+ public void fail_to_change_key_when_no_modules() {
+ createProject("sample");
+ createProject("another");
+
+ ProjectKeyPage page = openPage("sample");
+ page.assertSimpleUpdate().trySimpleUpdate("another");
+
+ $(".alert.alert-danger").shouldBe(visible);
+ assertThat(url()).endsWith("/project/key?id=sample");
+ }
+
+ @Test
+ public void change_key_of_multi_modules_project() {
+ analyzeProject("shared/xoo-multi-modules-sample", "sample");
+
+ ProjectKeyPage page = openPage("sample");
+ page.openFineGrainedUpdate().tryFineGrainedUpdate("sample", "another");
+
+ assertThat(url()).endsWith("/project/key?id=another");
+ }
+
+ @Test
+ public void fail_to_change_key_of_multi_modules_project() {
+ analyzeProject("shared/xoo-multi-modules-sample", "sample");
+ createProject("another");
+
+ ProjectKeyPage page = openPage("sample");
+ page.openFineGrainedUpdate().tryFineGrainedUpdate("sample", "another");
+
+ $(".alert.alert-danger").shouldBe(visible);
+ assertThat(url()).endsWith("/project/key?id=sample");
+ }
+
+ @Test
+ public void change_key_of_module_of_multi_modules_project() {
+ analyzeProject("shared/xoo-multi-modules-sample", "sample");
+
+ ProjectKeyPage page = openPage("sample");
+ page.openFineGrainedUpdate().tryFineGrainedUpdate("sample:module_a:module_a1", "another");
+
+ $("#update-key-confirmation-form").shouldNotBe(visible);
+
+ nav.openProjectKey("another");
+ assertThat(url()).endsWith("/project/key?id=another");
+ }
+
+ @Test
+ public void fail_to_change_key_of_module_of_multi_modules_project() {
+ analyzeProject("shared/xoo-multi-modules-sample", "sample");
+ createProject("another");
+
+ ProjectKeyPage page = openPage("sample");
+ page.openFineGrainedUpdate().tryFineGrainedUpdate("sample:module_a:module_a1", "another");
+
+ $(".alert.alert-danger").shouldBe(visible);
+ }
+
+ @Test
+ public void bulk_change() {
+ analyzeProject("shared/xoo-multi-modules-sample", "sample");
+
+ ProjectKeyPage page = openPage("sample");
+ page.assertBulkChange().simulateBulkChange("sample", "another");
+
+ $("#bulk-update-results").shouldBe(visible);
+ page.assertBulkChangeSimulationResult("sample", "another")
+ .assertBulkChangeSimulationResult("sample:module_a:module_a1", "another:module_a:module_a1");
+
+ page.confirmBulkUpdate().assertSuccessfulBulkUpdate();
+ }
+
+ @Test
+ public void fail_to_bulk_change_because_no_changed_key() {
+ analyzeProject("shared/xoo-multi-modules-sample", "sample");
+
+ ProjectKeyPage page = openPage("sample");
+ page.assertBulkChange().simulateBulkChange("random", "another");
+
+ $("#bulk-update-nothing").shouldBe(visible);
+ $("#bulk-update-results").shouldNotBe(visible);
+ }
+
+ @Test
+ public void fail_to_bulk_change_because_of_duplications() {
+ analyzeProject("shared/xoo-multi-modules-sample", "sample");
+
+ ProjectKeyPage page = openPage("sample");
+ page.assertBulkChange().simulateBulkChange("module_a1", "module_a2");
+
+ $("#bulk-update-duplicate").shouldBe(visible);
+ $("#bulk-update-results").shouldBe(visible);
+
+ page.assertBulkChangeSimulationResult("sample:module_a:module_a1", "sample:module_a:module_a2")
+ .assertDuplicated("sample:module_a:module_a1");
+ }
+
+ private ProjectKeyPage openPage(String projectKey) {
+ nav.logIn().submitCredentials("admin", "admin");
+ return nav.openProjectKey(projectKey);
+ }
+
+ private static void createProject(String projectKey) {
+ wsClient.wsConnector().call(new PostRequest("api/projects/create")
+ .setParam("key", projectKey)
+ .setParam("name", projectKey));
+ }
+
+ private static void analyzeProject(String path, String projectKey) {
+ ORCHESTRATOR.executeBuild(SonarScanner.create(projectDir(path))
+ .setProjectKey(projectKey));
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.tests.projectAdministration;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import javax.annotation.CheckForNull;
+import org.junit.After;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.DisableOnDebug;
+import org.junit.rules.TestRule;
+import org.junit.rules.Timeout;
+import org.sonarqube.tests.Category6Suite;
+import org.sonarqube.tests.Tester;
+import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.WsComponents;
+import org.sonarqube.ws.WsProjects;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsResponse;
+import org.sonarqube.ws.client.component.SearchProjectsRequest;
+import org.sonarqube.ws.client.project.CreateRequest;
+import org.sonarqube.ws.client.project.SearchWsRequest;
+import org.sonarqube.ws.client.project.UpdateKeyWsRequest;
+import util.ItUtils;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.ItUtils.projectDir;
+
+public class ProjectKeyUpdateTest {
+
+ @ClassRule
+ public static final Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
+
+ @Rule
+ public TestRule safeguard = new DisableOnDebug(Timeout.seconds(300));
+ @Rule
+ public Tester tester = new Tester(orchestrator)
+ .setElasticsearchHttpPort(Category6Suite.SEARCH_HTTP_PORT);
+
+ @After
+ public void tearDown() throws Exception {
+ unlockWritesOnProjectIndices();
+ }
+
+ @Test
+ public void update_key_of_provisioned_project() {
+ Organizations.Organization organization = tester.organizations().generate();
+ WsProjects.CreateWsResponse.Project project = createProject(organization, "one", "Foo");
+
+ updateKey(project, "two");
+
+ assertThat(isInProjectsSearch(organization, "one")).isFalse();
+ assertThat(isInProjectsSearch(organization, "two")).isTrue();
+ assertThat(isInComponentSearch(organization, "one")).isFalse();
+ assertThat(isInComponentSearch(organization, "two")).isTrue();
+ assertThat(keyInComponentSearchProjects("Foo")).isEqualTo("two");
+ assertThat(keysInComponentSuggestions("Foo")).containsExactly("two");
+ }
+
+ @Test
+ public void recover_indexing_errors_when_updating_key_of_provisioned_project() throws Exception {
+ Organizations.Organization organization = tester.organizations().generate();
+ WsProjects.CreateWsResponse.Project project = createProject(organization, "one", "Foo");
+
+ lockWritesOnProjectIndices();
+
+ updateKey(project, "two");
+
+ assertThat(isInProjectsSearch(organization, "one")).isFalse();
+
+ // WS gets the list of projects from ES then reloads projects from db.
+ // That's why keys in WS responses are correct.
+ assertThat(isInProjectsSearch(organization, "one")).isFalse();
+ assertThat(isInProjectsSearch(organization, "two")).isTrue();
+ assertThat(keyInComponentSearchProjects("Foo")).isEqualTo("two");
+ assertThat(keysInComponentSuggestions("Foo")).containsExactly("two");
+
+ // however searching by key is inconsistent
+ assertThat(keyInComponentSearchProjects("one")).isEqualTo("two");
+ assertThat(keysInComponentSuggestions("one")).containsExactly("two");
+ assertThat(keyInComponentSearchProjects("two")).isNull();
+ assertThat(keysInComponentSuggestions("two")).isEmpty();
+
+ unlockWritesOnProjectIndices();
+
+ boolean recovered = false;
+ while (!recovered) {
+ // recovery daemon runs every second, see Category6Suite
+ Thread.sleep(1_000L);
+ recovered = keyInComponentSearchProjects("one") == null &&
+ keysInComponentSuggestions("one").isEmpty() &&
+ "two".equals(keyInComponentSearchProjects("two")) &&
+ keysInComponentSuggestions("two").contains("two");
+ }
+ }
+
+ @Test
+ public void update_key_of_module() {
+ Organizations.Organization organization = tester.organizations().generate();
+ orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-multi-modules-sample"),
+ "sonar.organization", organization.getKey(),
+ "sonar.login", "admin", "sonar.password", "admin"));
+
+ String initialKey = "com.sonarsource.it.samples:multi-modules-sample:module_a";
+ String newKey = "com.sonarsource.it.samples:multi-modules-sample:module_c";
+
+ updateKey(initialKey, newKey);
+
+ assertThat(isInComponentSearch(organization, initialKey)).isFalse();
+ assertThat(isInComponentSearch(organization, newKey)).isTrue();
+ // suggestions engine ignores one-character words, so we can't search for "Module A"
+ assertThat(keysInComponentSuggestions("Module"))
+ .contains(newKey)
+ .doesNotContain(initialKey);
+ assertThat(keysInComponentSuggestions(newKey))
+ .contains(newKey)
+ .doesNotContain(initialKey);
+ assertThat(keysInComponentSuggestions(initialKey)).isEmpty();
+
+ }
+
+ @Test
+ public void recover_indexing_errors_when_updating_key_of_module() throws Exception {
+ Organizations.Organization organization = tester.organizations().generate();
+ orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-multi-modules-sample"),
+ "sonar.organization", organization.getKey(),
+ "sonar.login", "admin", "sonar.password", "admin"));
+
+ String initialKey = "com.sonarsource.it.samples:multi-modules-sample:module_a";
+ String newKey = "com.sonarsource.it.samples:multi-modules-sample:module_c";
+
+ lockWritesOnProjectIndices();
+ updateKey(initialKey, newKey);
+
+ // api/components/search loads keys from db, so results are consistent
+ assertThat(isInComponentSearch(organization, initialKey)).isFalse();
+ assertThat(isInComponentSearch(organization, newKey)).isTrue();
+
+ // key in result of suggestion engine is loaded from db, so results are ok when searching for unchanged name
+ assertThat(keysInComponentSuggestions("Module"))
+ .contains(newKey)
+ .doesNotContain(initialKey);
+
+ // but searching for new key does not work
+ assertThat(keysInComponentSuggestions(newKey)).isEmpty();
+ assertThat(keysInComponentSuggestions(initialKey))
+ .isNotEmpty()
+ .contains(newKey /* the returned key is loaded from db, so it's correct */);
+
+ unlockWritesOnProjectIndices();
+
+ boolean recovered = false;
+ while (!recovered) {
+ // recovery daemon runs every second, see Category6Suite
+ Thread.sleep(1_000L);
+ recovered = keysInComponentSuggestions(newKey).contains(newKey) && keysInComponentSuggestions(initialKey).isEmpty();
+ }
+
+ }
+
+ private void lockWritesOnProjectIndices() throws Exception {
+ tester.elasticsearch().lockWrites("components");
+ tester.elasticsearch().lockWrites("projectmeasures");
+ }
+
+
+ private void unlockWritesOnProjectIndices() throws Exception {
+ tester.elasticsearch().unlockWrites("components");
+ tester.elasticsearch().unlockWrites("projectmeasures");
+ }
+
+ private void updateKey(WsProjects.CreateWsResponse.Project project, String newKey) {
+ tester.wsClient().projects().updateKey(UpdateKeyWsRequest.builder().setKey(project.getKey()).setNewKey(newKey).build());
+ }
+
+ private void updateKey(String initialKey, String newKey) {
+ tester.wsClient().projects().updateKey(UpdateKeyWsRequest.builder().setKey(initialKey).setNewKey(newKey).build());
+ }
+
+ private WsProjects.CreateWsResponse.Project createProject(Organizations.Organization organization, String key, String name) {
+ CreateRequest createRequest = CreateRequest.builder().setKey(key).setName(name).setOrganization(organization.getKey()).build();
+ return tester.wsClient().projects().create(createRequest).getProject();
+ }
+
+ /**
+ * Projects administration page - uses database
+ */
+ private boolean isInProjectsSearch(Organizations.Organization organization, String key) {
+ WsProjects.SearchWsResponse response = tester.wsClient().projects().search(
+ SearchWsRequest.builder().setOrganization(organization.getKey()).setQuery(key).setQualifiers(singletonList("TRK")).build());
+ return response.getComponentsCount() > 0;
+ }
+
+ private boolean isInComponentSearch(Organizations.Organization organization, String key) {
+ org.sonarqube.ws.client.component.SearchWsRequest request = new org.sonarqube.ws.client.component.SearchWsRequest()
+ .setQualifiers(asList("TRK", "BRC"))
+ .setQuery(key)
+ .setOrganization(organization.getKey());
+ return tester.wsClient().components().search(request).getComponentsCount() == 1L;
+ }
+
+ /**
+ * Projects page - api/components/search_projects - uses ES + DB
+ */
+ @CheckForNull
+ private String keyInComponentSearchProjects(String name) {
+ WsComponents.SearchProjectsWsResponse response = tester.wsClient().components().searchProjects(
+ SearchProjectsRequest.builder().setFilter("query=\"" + name + "\"").build());
+ if (response.getComponentsCount() > 0) {
+ return response.getComponents(0).getKey();
+ }
+ return null;
+ }
+
+ /**
+ * Top-right search engine - api/components/suggestions - uses ES + DB
+ */
+ private List<String> keysInComponentSuggestions(String name) {
+ GetRequest request = new GetRequest("api/components/suggestions").setParam("s", name);
+ WsResponse response = tester.wsClient().wsConnector().call(request);
+ Map<String, Object> json = ItUtils.jsonToMap(response.content());
+ Collection<Map<String, Object>> results = (Collection<Map<String, Object>>) json.get("results");
+ return results.stream()
+ .filter(map -> "TRK".equals(map.get("q")) || "BRC".equals(map.get("q")))
+ .flatMap(map -> ((Collection<Map<String, Object>>) map.get("items")).stream())
+ .map(map -> (String) map.get("key"))
+ .collect(Collectors.toList());
+ }
+}