public static final String REPORT = "REPORT";
public static final String PROJECT_EXPORT = "PROJECT_EXPORT";
+ public static final String GITHUB_PROJECT_PERMISSIONS_PROVISIONING = "GITHUB_PROJECT_PERMISSIONS_PROVISIONING";
+
private CeTaskTypes() {
// only statics
}
.orElse(false);
}
+ @Override
+ public void queuePermissionSyncTask(String submitterUuid, String componentUuid, String projectUuid) {
+ findManagedProjectService()
+ .ifPresent(managedProjectService -> managedProjectService.queuePermissionSyncTask(submitterUuid, componentUuid, projectUuid));
+ }
+
private Optional<ManagedProjectService> findManagedProjectService() {
return findManagedInstanceService()
.filter(ManagedProjectService.class::isInstance)
boolean isProjectManaged(DbSession dbSession, String projectUuid);
+ void queuePermissionSyncTask(String submitterUuid, String componentUuid, String projectUuid);
}
*/
package org.sonar.server.project;
+import com.google.common.annotations.VisibleForTesting;
import java.util.Optional;
import org.sonar.api.ce.ComputeEngineSide;
import org.sonar.api.server.ServerSide;
import static java.util.Optional.ofNullable;
import static org.sonar.api.utils.Preconditions.checkState;
import static org.sonar.api.web.UserRole.PUBLIC_PERMISSIONS;
+import static org.sonar.db.ce.CeTaskTypes.GITHUB_PROJECT_PERMISSIONS_PROVISIONING;
@ServerSide
@ComputeEngineSide
}
}
}
- private void checkNoPendingTasks(DbSession dbSession, EntityDto entityDto) {
- //This check likely can be removed when we remove the column 'private' from components table
- checkState(noPendingTask(dbSession, entityDto.getKey()), "Component visibility can't be changed as long as it has background task(s) pending or in progress");
+
+ @VisibleForTesting
+ void checkNoPendingTasks(DbSession dbSession, EntityDto entityDto) {
+ //This check likely can be removed when we remove the column 'private' from components table in SONAR-20126.
+ checkState(countPendingTask(dbSession, entityDto.getKey()) == 0, "Component visibility can't be changed as long as it has background task(s) pending or in progress");
}
- private boolean noPendingTask(DbSession dbSession, String entityKey) {
+ private long countPendingTask(DbSession dbSession, String entityKey) {
EntityDto entityDto = dbClient.entityDao().selectByKey(dbSession, entityKey).orElseThrow(() -> new IllegalStateException("Can't find entity " + entityKey));
- return dbClient.ceQueueDao().selectByEntityUuid(dbSession, entityDto.getUuid()).isEmpty();
+ return dbClient.ceQueueDao().selectByEntityUuid(dbSession, entityDto.getUuid())
+ .stream()
+ .filter(task -> !task.getTaskType().equals(GITHUB_PROJECT_PERMISSIONS_PROVISIONING))
+ .count();
}
private void setPrivateForRootComponentUuid(DbSession dbSession, EntityDto entity, boolean newIsPrivate) {
import static java.util.Collections.emptySet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.assertj.core.api.Assertions.assertThatNoException;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.withSettings;
@Test
public void getProviderName_whenNotManaged_shouldThrow() {
- DelegatingManagedServices managedInstanceService = NO_MANAGED_SERVICES;
-
- assertThatThrownBy(managedInstanceService::getProviderName)
+ assertThatThrownBy(NO_MANAGED_SERVICES::getProviderName)
.isInstanceOf(IllegalStateException.class)
.hasMessage("This instance is not managed.");
}
@Test
public void isInstanceExternallyManaged_whenNoManagedInstanceService_returnsFalse() {
- DelegatingManagedServices managedInstanceService = NO_MANAGED_SERVICES;
- assertThat(managedInstanceService.isInstanceExternallyManaged()).isFalse();
+ assertThat(NO_MANAGED_SERVICES.isInstanceExternallyManaged()).isFalse();
}
@Test
@Test
public void getUserUuidToManaged_whenNoDelegates_setAllUsersAsNonManaged() {
Set<String> userUuids = Set.of("a", "b");
- DelegatingManagedServices managedInstanceService = NO_MANAGED_SERVICES;
- Map<String, Boolean> userUuidToManaged = managedInstanceService.getUserUuidToManaged(dbSession, userUuids);
+ Map<String, Boolean> userUuidToManaged = NO_MANAGED_SERVICES.getUserUuidToManaged(dbSession, userUuids);
assertThat(userUuidToManaged).containsExactlyInAnyOrderEntriesOf(Map.of("a", false, "b", false));
}
@Test
public void isProjectManaged_whenManagedNoInstanceServices_returnsFalse() {
- DelegatingManagedServices managedInstanceService = NO_MANAGED_SERVICES;
+ assertThat(NO_MANAGED_SERVICES.isProjectManaged(dbSession, "whatever")).isFalse();
+ }
+
+ @Test
+ public void queuePermissionSyncTask_whenManagedNoInstanceServices_doesNotFail() {
+ assertThatNoException().isThrownBy(() -> NO_MANAGED_SERVICES.queuePermissionSyncTask("userUuid", "componentUuid", "projectUuid"));
+ }
- assertThat(managedInstanceService.isProjectManaged(dbSession, "whatever")).isFalse();
+ @Test
+ public void queuePermissionSyncTask_whenManagedInstanceServices_shouldDelegatesToRightService() {
+ NeverManagedInstanceService neverManagedInstanceService = spy(new NeverManagedInstanceService());
+ AlwaysManagedInstanceService alwaysManagedInstanceService = spy(new AlwaysManagedInstanceService());
+ Set<ManagedInstanceService> delegates = Set.of(neverManagedInstanceService, alwaysManagedInstanceService);
+ DelegatingManagedServices managedInstanceService = new DelegatingManagedServices(delegates);
+
+ managedInstanceService.queuePermissionSyncTask("userUuid", "componentUuid", "projectUuid");
+ verify(neverManagedInstanceService, never()).queuePermissionSyncTask(anyString(), anyString(), anyString());
+ verify(alwaysManagedInstanceService).queuePermissionSyncTask("userUuid", "componentUuid", "projectUuid");
}
private static class NeverManagedInstanceService implements ManagedInstanceService, ManagedProjectService {
public boolean isProjectManaged(DbSession dbSession, String projectUuid) {
return false;
}
+
+ @Override
+ public void queuePermissionSyncTask(String submitterUuid, String componentUuid, String projectUuid) {
+
+ }
}
private static class AlwaysManagedInstanceService implements ManagedInstanceService, ManagedProjectService {
public boolean isProjectManaged(DbSession dbSession, String projectUuid) {
return true;
}
+
+ @Override
+ public void queuePermissionSyncTask(String submitterUuid, String componentUuid, String projectUuid) {
+
+ }
}
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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.project;
+
+import java.util.List;
+import java.util.Optional;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Answers;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnitRunner;
+import org.sonar.db.DbClient;
+import org.sonar.db.DbSession;
+import org.sonar.db.ce.CeQueueDto;
+import org.sonar.db.entity.EntityDto;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.sonar.db.ce.CeTaskTypes.GITHUB_PROJECT_PERMISSIONS_PROVISIONING;
+
+@RunWith(MockitoJUnitRunner.class)
+public class VisibilityServiceTest {
+
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS)
+ private DbClient dbClient;
+
+ @Mock
+ private DbSession dbSession;
+
+ @InjectMocks
+ private VisibilityService visibilityService;
+
+ @Test
+ public void checkNoPendingTasks_whenEntityNotFound_throwsIae() {
+ EntityDto entityDto = mockEntityDto();
+ when(dbClient.entityDao().selectByKey(dbSession, entityDto.getKey())).thenReturn(Optional.empty());
+
+ assertThatIllegalStateException()
+ .isThrownBy(() -> visibilityService.checkNoPendingTasks(dbSession, entityDto))
+ .withMessage("Can't find entity entityKey");
+ }
+
+ @Test
+ public void checkNoPendingTasks_whenEntityFoundAndNoTaskInQueue_doesNotThrow() {
+ EntityDto entityDto = mockEntityDto();
+ when(dbClient.entityDao().selectByKey(dbSession, entityDto.getKey())).thenReturn(Optional.of(entityDto));
+
+ visibilityService.checkNoPendingTasks(dbSession, entityDto);
+ }
+
+ @Test
+ public void checkNoPendingTasks_whenOneGithubSyncTaskInQueue_doesNotThrow() {
+ EntityDto entityDto = mockEntityDto();
+ when(dbClient.entityDao().selectByKey(dbSession, entityDto.getKey())).thenReturn(Optional.of(entityDto));
+
+ mockCeQueueDto(GITHUB_PROJECT_PERMISSIONS_PROVISIONING, entityDto.getKey());
+
+ visibilityService.checkNoPendingTasks(dbSession, entityDto);
+ }
+
+ @Test
+ public void checkNoPendingTasks_whenAnyOtherTaskInQueue_throws() {
+ EntityDto entityDto = mockEntityDto();
+ when(dbClient.entityDao().selectByKey(dbSession, entityDto.getKey())).thenReturn(Optional.of(entityDto));
+
+ mockCeQueueDto("ANYTHING", entityDto.getUuid());
+
+ assertThatIllegalStateException()
+ .isThrownBy(() -> visibilityService.checkNoPendingTasks(dbSession, entityDto))
+ .withMessage("Component visibility can't be changed as long as it has background task(s) pending or in progress");
+ }
+
+ private void mockCeQueueDto(String taskType, String entityDto) {
+ CeQueueDto ceQueueDto = mock(CeQueueDto.class);
+ when(ceQueueDto.getTaskType()).thenReturn(taskType);
+ when(dbClient.ceQueueDao().selectByEntityUuid(dbSession, entityDto)).thenReturn(List.of(ceQueueDto));
+ }
+
+ private static EntityDto mockEntityDto() {
+ EntityDto entityDto = mock(EntityDto.class);
+ when(entityDto.getKey()).thenReturn("entityKey");
+ when(entityDto.getUuid()).thenReturn("entityUuid");
+ return entityDto;
+ }
+}
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.exceptions.UnauthorizedException;
import org.sonar.server.favorite.FavoriteUpdater;
+import org.sonar.server.management.ManagedProjectService;
import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver;
import org.sonar.server.permission.GroupPermissionChanger;
import org.sonar.server.permission.PermissionService;
private final GitHubSettings gitHubSettings = mock(GitHubSettings.class);
private NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider);
- private final WsActionTester ws = new WsActionTester(new ImportGithubProjectAction(db.getDbClient(), userSession,
+ private final ManagedProjectService managedProjectService = mock(ManagedProjectService.class);
+ private final WsActionTester ws = new WsActionTester(new ImportGithubProjectAction(db.getDbClient(), managedProjectService, userSession,
projectDefaultVisibility, appClient, componentUpdater, importHelper, projectKeyGenerator, newCodeDefinitionResolver,
defaultBranchNameResolver, gitHubSettings));
assertThat(mainBranch).isPresent();
assertThat(mainBranch.get().getKey()).isEqualTo("default-branch");
+ verify(managedProjectService).queuePermissionSyncTask(userSession.getUuid(), mainBranch.get().getUuid() , projectDto.get().getUuid());
}
@Test
*/
package org.sonar.server.almintegration.ws.github;
+import java.util.Objects;
import java.util.Optional;
import javax.inject.Inject;
import org.sonar.alm.client.github.GithubApplicationClient;
import org.sonar.server.component.ComponentUpdater;
import org.sonar.server.component.NewComponent;
import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.management.ManagedProjectService;
import org.sonar.server.newcodeperiod.NewCodeDefinitionResolver;
import org.sonar.server.project.DefaultBranchNameResolver;
import org.sonar.server.project.ProjectDefaultVisibility;
public static final String PARAM_REPOSITORY_KEY = "repositoryKey";
private final DbClient dbClient;
+
+ private final ManagedProjectService managedProjectService;
private final UserSession userSession;
private final ProjectDefaultVisibility projectDefaultVisibility;
private final GithubApplicationClient githubApplicationClient;
private final GitHubSettings gitHubSettings;
@Inject
- public ImportGithubProjectAction(DbClient dbClient, UserSession userSession, ProjectDefaultVisibility projectDefaultVisibility,
+ public ImportGithubProjectAction(DbClient dbClient, ManagedProjectService managedProjectService, UserSession userSession, ProjectDefaultVisibility projectDefaultVisibility,
GithubApplicationClientImpl githubApplicationClient, ComponentUpdater componentUpdater, ImportHelper importHelper,
ProjectKeyGenerator projectKeyGenerator, NewCodeDefinitionResolver newCodeDefinitionResolver,
DefaultBranchNameResolver defaultBranchNameResolver, GitHubSettings gitHubSettings) {
this.dbClient = dbClient;
+ this.managedProjectService = managedProjectService;
this.userSession = userSession;
this.projectDefaultVisibility = projectDefaultVisibility;
this.githubApplicationClient = githubApplicationClient;
componentUpdater.commitAndIndex(dbSession, componentCreationData);
+ String userUuid = Objects.requireNonNull(userSession.getUuid());
+ managedProjectService.queuePermissionSyncTask(userUuid, mainBranchDto.getUuid(), projectDto.getUuid());
+
return toCreateResponse(projectDto);
}
}
background_task.type.AUDIT_PURGE=Audit Log Purge
background_task.type.REPORT_SUBMIT=Report Email Submit
background_task.type.GITHUB_AUTH_PROVISIONING=Github Provisioning
+background_task.type.GITHUB_PROJECT_PERMISSIONS_PROVISIONING=Github project permission sync
background_tasks.page=Background Tasks
background_tasks.page.description=This page allows monitoring of the queue of tasks running asynchronously on the server. It also gives access to the history of finished tasks and their status. Analysis report processing is the most common kind of background task.