You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ImportGithubProjectActionIT.java 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534
  1. /*
  2. * SonarQube
  3. * Copyright (C) 2009-2024 SonarSource SA
  4. * mailto:info AT sonarsource DOT com
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU Lesser General Public
  8. * License as published by the Free Software Foundation; either
  9. * version 3 of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * Lesser General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU Lesser General Public License
  17. * along with this program; if not, write to the Free Software Foundation,
  18. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  19. */
  20. package org.sonar.server.almintegration.ws.github;
  21. import java.util.Optional;
  22. import java.util.Set;
  23. import org.junit.Before;
  24. import org.junit.Rule;
  25. import org.junit.Test;
  26. import org.mockito.ArgumentCaptor;
  27. import org.sonar.alm.client.github.GithubApplicationClientImpl;
  28. import org.sonar.alm.client.github.GithubPermissionConverter;
  29. import org.sonar.api.resources.Qualifiers;
  30. import org.sonar.api.server.ws.WebService;
  31. import org.sonar.api.utils.System2;
  32. import org.sonar.auth.github.AppInstallationToken;
  33. import org.sonar.auth.github.GitHubSettings;
  34. import org.sonar.auth.github.GsonRepositoryCollaborator;
  35. import org.sonar.auth.github.GsonRepositoryPermissions;
  36. import org.sonar.auth.github.client.GithubApplicationClient;
  37. import org.sonar.core.i18n.I18n;
  38. import org.sonar.core.platform.EditionProvider;
  39. import org.sonar.core.platform.PlatformEditionProvider;
  40. import org.sonar.core.util.SequenceUuidFactory;
  41. import org.sonar.db.DbSession;
  42. import org.sonar.db.DbTester;
  43. import org.sonar.db.alm.setting.AlmSettingDto;
  44. import org.sonar.db.component.BranchDto;
  45. import org.sonar.db.component.ResourceTypesRule;
  46. import org.sonar.db.entity.EntityDto;
  47. import org.sonar.db.newcodeperiod.NewCodePeriodDto;
  48. import org.sonar.db.permission.GlobalPermission;
  49. import org.sonar.db.project.CreationMethod;
  50. import org.sonar.db.project.ProjectDto;
  51. import org.sonar.db.user.UserDto;
  52. import org.sonar.server.almintegration.ws.ImportHelper;
  53. import org.sonar.server.common.almintegration.ProjectKeyGenerator;
  54. import org.sonar.server.common.almsettings.github.GithubProjectCreatorFactory;
  55. import org.sonar.server.common.component.ComponentUpdater;
  56. import org.sonar.server.es.EsTester;
  57. import org.sonar.server.es.IndexersImpl;
  58. import org.sonar.server.es.TestIndexers;
  59. import org.sonar.server.exceptions.BadConfigurationException;
  60. import org.sonar.server.exceptions.NotFoundException;
  61. import org.sonar.server.exceptions.UnauthorizedException;
  62. import org.sonar.server.favorite.FavoriteUpdater;
  63. import org.sonar.server.management.ManagedProjectService;
  64. import org.sonar.server.common.newcodeperiod.NewCodeDefinitionResolver;
  65. import org.sonar.server.common.permission.GroupPermissionChanger;
  66. import org.sonar.server.permission.PermissionService;
  67. import org.sonar.server.permission.PermissionServiceImpl;
  68. import org.sonar.server.common.permission.PermissionTemplateService;
  69. import org.sonar.server.common.permission.PermissionUpdater;
  70. import org.sonar.server.common.permission.UserPermissionChange;
  71. import org.sonar.server.common.permission.UserPermissionChanger;
  72. import org.sonar.server.permission.index.FooIndexDefinition;
  73. import org.sonar.server.permission.index.PermissionIndexer;
  74. import org.sonar.server.project.DefaultBranchNameResolver;
  75. import org.sonar.server.project.ProjectDefaultVisibility;
  76. import org.sonar.server.project.Visibility;
  77. import org.sonar.server.common.project.ProjectCreator;
  78. import org.sonar.server.tester.UserSessionRule;
  79. import org.sonar.server.ws.TestRequest;
  80. import org.sonar.server.ws.WsActionTester;
  81. import org.sonarqube.ws.Projects;
  82. import static java.lang.String.format;
  83. import static java.util.Objects.requireNonNull;
  84. import static org.assertj.core.api.Assertions.assertThat;
  85. import static org.assertj.core.api.Assertions.assertThatNoException;
  86. import static org.assertj.core.api.Assertions.assertThatThrownBy;
  87. import static org.assertj.core.api.Assertions.tuple;
  88. import static org.mockito.ArgumentMatchers.any;
  89. import static org.mockito.ArgumentMatchers.eq;
  90. import static org.mockito.Mockito.mock;
  91. import static org.mockito.Mockito.never;
  92. import static org.mockito.Mockito.verify;
  93. import static org.mockito.Mockito.when;
  94. import static org.sonar.db.component.BranchDto.DEFAULT_MAIN_BRANCH_NAME;
  95. import static org.sonar.db.newcodeperiod.NewCodePeriodType.NUMBER_OF_DAYS;
  96. import static org.sonar.db.newcodeperiod.NewCodePeriodType.REFERENCE_BRANCH;
  97. import static org.sonar.server.almintegration.ws.ImportHelper.PARAM_ALM_SETTING;
  98. import static org.sonar.server.almintegration.ws.github.ImportGithubProjectAction.PARAM_REPOSITORY_KEY;
  99. import static org.sonar.server.tester.UserSessionRule.standalone;
  100. import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_TYPE;
  101. import static org.sonarqube.ws.client.project.ProjectsWsParameters.PARAM_NEW_CODE_DEFINITION_VALUE;
  102. public class ImportGithubProjectActionIT {
  103. private static final String PROJECT_KEY_NAME = "PROJECT_NAME";
  104. private static final String GENERATED_PROJECT_KEY = "generated_" + PROJECT_KEY_NAME;
  105. @Rule
  106. public UserSessionRule userSession = standalone();
  107. private final System2 system2 = mock(System2.class);
  108. private final GithubApplicationClientImpl appClient = mock(GithubApplicationClientImpl.class);
  109. private final DefaultBranchNameResolver defaultBranchNameResolver = mock(DefaultBranchNameResolver.class);
  110. @Rule
  111. public DbTester db = DbTester.create(system2);
  112. private final PermissionTemplateService permissionTemplateService = mock(PermissionTemplateService.class);
  113. public EsTester es = EsTester.createCustom(new FooIndexDefinition());
  114. private final PermissionUpdater<UserPermissionChange> userPermissionUpdater = new PermissionUpdater(
  115. new IndexersImpl(new PermissionIndexer(db.getDbClient(), es.client())),
  116. Set.of(new UserPermissionChanger(db.getDbClient(), new SequenceUuidFactory()),
  117. new GroupPermissionChanger(db.getDbClient(), new SequenceUuidFactory())));
  118. private final PermissionService permissionService = new PermissionServiceImpl(new ResourceTypesRule().setRootQualifiers(Qualifiers.PROJECT));
  119. private final ComponentUpdater componentUpdater = new ComponentUpdater(db.getDbClient(), mock(I18n.class), System2.INSTANCE,
  120. permissionTemplateService, new FavoriteUpdater(db.getDbClient()), new TestIndexers(), new SequenceUuidFactory(),
  121. defaultBranchNameResolver, userPermissionUpdater, permissionService);
  122. private final ImportHelper importHelper = new ImportHelper(db.getDbClient(), userSession);
  123. private final ProjectKeyGenerator projectKeyGenerator = mock(ProjectKeyGenerator.class);
  124. private final ProjectDefaultVisibility projectDefaultVisibility = mock(ProjectDefaultVisibility.class);
  125. private final PlatformEditionProvider editionProvider = mock(PlatformEditionProvider.class);
  126. private final GitHubSettings gitHubSettings = mock(GitHubSettings.class);
  127. private final NewCodeDefinitionResolver newCodeDefinitionResolver = new NewCodeDefinitionResolver(db.getDbClient(), editionProvider);
  128. private final ManagedProjectService managedProjectService = mock(ManagedProjectService.class);
  129. private final GithubPermissionConverter githubPermissionConverter = mock();
  130. private final ProjectCreator projectCreator = new ProjectCreator(userSession, projectDefaultVisibility, componentUpdater);
  131. private final GithubProjectCreatorFactory gitHubProjectCreatorFactory = new GithubProjectCreatorFactory(db.getDbClient(),
  132. null, appClient, projectKeyGenerator, userSession, projectCreator, gitHubSettings, githubPermissionConverter, userPermissionUpdater, permissionService,
  133. managedProjectService);
  134. private final WsActionTester ws = new WsActionTester(new ImportGithubProjectAction(db.getDbClient(), userSession,
  135. componentUpdater, importHelper, newCodeDefinitionResolver, defaultBranchNameResolver, gitHubProjectCreatorFactory));
  136. @Before
  137. public void before() {
  138. when(projectDefaultVisibility.get(any())).thenReturn(Visibility.PUBLIC);
  139. when(defaultBranchNameResolver.getEffectiveMainBranchName()).thenReturn(DEFAULT_MAIN_BRANCH_NAME);
  140. }
  141. @Test
  142. public void importProject_ifProjectWithSameNameDoesNotExist_importSucceed() {
  143. AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
  144. GithubApplicationClient.Repository repository = mockGithubDevOpsAppInteractions();
  145. mockGithubAuthAppInteractions();
  146. when(gitHubSettings.isProvisioningEnabled()).thenReturn(true);
  147. Projects.CreateWsResponse response = callWebService(githubAlmSetting);
  148. Projects.CreateWsResponse.Project result = response.getProject();
  149. assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
  150. assertThat(result.getName()).isEqualTo(repository.getName());
  151. Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
  152. assertThat(projectDto).isPresent();
  153. assertThat(db.getDbClient().projectAlmSettingDao().selectByProject(db.getSession(), projectDto.get())).isPresent();
  154. Optional<BranchDto> mainBranch = db.getDbClient().branchDao().selectByProject(db.getSession(), projectDto.get()).stream().filter(BranchDto::isMain).findAny();
  155. assertThat(mainBranch).isPresent();
  156. assertThat(mainBranch.get().getKey()).isEqualTo("default-branch");
  157. verify(managedProjectService).queuePermissionSyncTask(userSession.getUuid(), mainBranch.get().getUuid(), projectDto.get().getUuid());
  158. }
  159. @Test
  160. public void importProject_withNCD_developer_edition() {
  161. when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.DEVELOPER));
  162. AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
  163. mockGithubDevOpsAppInteractions();
  164. Projects.CreateWsResponse response = ws.newRequest()
  165. .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
  166. .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
  167. .setParam(PARAM_NEW_CODE_DEFINITION_TYPE, "NUMBER_OF_DAYS")
  168. .setParam(PARAM_NEW_CODE_DEFINITION_VALUE, "30")
  169. .executeProtobuf(Projects.CreateWsResponse.class);
  170. Projects.CreateWsResponse.Project result = response.getProject();
  171. assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
  172. Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
  173. assertThat(projectDto).isPresent();
  174. assertThat(db.getDbClient().newCodePeriodDao().selectByProject(db.getSession(), projectDto.get().getUuid()))
  175. .isPresent()
  176. .get()
  177. .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue, NewCodePeriodDto::getBranchUuid)
  178. .containsExactly(NUMBER_OF_DAYS, "30", null);
  179. }
  180. @Test
  181. public void importProject_withNCD_community_edition() {
  182. when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.COMMUNITY));
  183. AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
  184. mockGithubDevOpsAppInteractions();
  185. Projects.CreateWsResponse response = ws.newRequest()
  186. .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
  187. .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
  188. .setParam(PARAM_NEW_CODE_DEFINITION_TYPE, "NUMBER_OF_DAYS")
  189. .setParam(PARAM_NEW_CODE_DEFINITION_VALUE, "30")
  190. .executeProtobuf(Projects.CreateWsResponse.class);
  191. Projects.CreateWsResponse.Project result = response.getProject();
  192. assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
  193. Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
  194. assertThat(projectDto).isPresent();
  195. BranchDto branchDto = db.getDbClient().branchDao().selectMainBranchByProjectUuid(db.getSession(), projectDto.get().getUuid()).orElseThrow();
  196. String projectUuid = projectDto.get().getUuid();
  197. assertThat(db.getDbClient().newCodePeriodDao().selectByBranch(db.getSession(), projectUuid, branchDto.getUuid()))
  198. .isPresent()
  199. .get()
  200. .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue, NewCodePeriodDto::getBranchUuid)
  201. .containsExactly(NUMBER_OF_DAYS, "30", branchDto.getUuid());
  202. }
  203. @Test
  204. public void importProject_reference_branch_ncd_no_default_branch() {
  205. when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.DEVELOPER));
  206. when(defaultBranchNameResolver.getEffectiveMainBranchName()).thenReturn("default-branch");
  207. AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
  208. mockGithubDevOpsAppInteractions();
  209. Projects.CreateWsResponse response = ws.newRequest()
  210. .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
  211. .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
  212. .setParam(PARAM_NEW_CODE_DEFINITION_TYPE, "reference_branch")
  213. .executeProtobuf(Projects.CreateWsResponse.class);
  214. Projects.CreateWsResponse.Project result = response.getProject();
  215. assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
  216. Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
  217. assertThat(projectDto).isPresent();
  218. assertThat(db.getDbClient().newCodePeriodDao().selectByProject(db.getSession(), projectDto.get().getUuid()))
  219. .isPresent()
  220. .get()
  221. .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue)
  222. .containsExactly(REFERENCE_BRANCH, "default-branch");
  223. }
  224. @Test
  225. public void importProject_reference_branch_ncd() {
  226. when(editionProvider.get()).thenReturn(Optional.of(EditionProvider.Edition.DEVELOPER));
  227. AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
  228. mockGithubDevOpsAppInteractions();
  229. Projects.CreateWsResponse response = ws.newRequest()
  230. .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
  231. .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
  232. .setParam(PARAM_NEW_CODE_DEFINITION_TYPE, "reference_branch")
  233. .executeProtobuf(Projects.CreateWsResponse.class);
  234. Projects.CreateWsResponse.Project result = response.getProject();
  235. assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
  236. Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), result.getKey());
  237. assertThat(projectDto).isPresent();
  238. assertThat(db.getDbClient().newCodePeriodDao().selectByProject(db.getSession(), projectDto.get().getUuid()))
  239. .isPresent()
  240. .get()
  241. .extracting(NewCodePeriodDto::getType, NewCodePeriodDto::getValue)
  242. .containsExactly(REFERENCE_BRANCH, "default-branch");
  243. }
  244. @Test
  245. public void importProject_ifProjectWithSameNameAlreadyExists_importSucceed() {
  246. AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
  247. db.components().insertPublicProject(p -> p.setKey("Hello-World")).getMainBranchComponent();
  248. GithubApplicationClient.Repository repository = mockGithubDevOpsAppInteractions();
  249. Projects.CreateWsResponse response = ws.newRequest()
  250. .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
  251. .setParam(PARAM_REPOSITORY_KEY, "Hello-World")
  252. .executeProtobuf(Projects.CreateWsResponse.class);
  253. Projects.CreateWsResponse.Project result = response.getProject();
  254. assertThat(result.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
  255. assertThat(result.getName()).isEqualTo(repository.getName());
  256. }
  257. @Test
  258. public void importProject_whenGithubProvisioningIsDisabled_shouldApplyPermissionTemplateAndSetDefaultVisibility() {
  259. AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
  260. mockGithubDevOpsAppInteractions();
  261. when(gitHubSettings.isProvisioningEnabled()).thenReturn(false);
  262. ws.newRequest()
  263. .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
  264. .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
  265. .executeProtobuf(Projects.CreateWsResponse.class);
  266. ArgumentCaptor<EntityDto> projectDtoArgumentCaptor = ArgumentCaptor.forClass(EntityDto.class);
  267. verify(permissionTemplateService).applyDefaultToNewComponent(any(DbSession.class), projectDtoArgumentCaptor.capture(), eq(userSession.getUuid()));
  268. EntityDto capturedProjectDto = projectDtoArgumentCaptor.getValue();
  269. assertThat(capturedProjectDto.getKey()).isEqualTo(GENERATED_PROJECT_KEY);
  270. assertThat(capturedProjectDto.isPrivate()).isFalse();
  271. }
  272. @Test
  273. public void importProject_whenGithubProvisioningIsEnabled_shouldNotApplyPermissionTemplate() {
  274. AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
  275. when(gitHubSettings.isProvisioningEnabled()).thenReturn(true);
  276. mockGithubDevOpsAppInteractions();
  277. mockGithubAuthAppInteractions();
  278. ws.newRequest()
  279. .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
  280. .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
  281. .executeProtobuf(Projects.CreateWsResponse.class);
  282. verify(permissionTemplateService, never()).applyDefaultToNewComponent(any(), any(), any());
  283. }
  284. private void mockGithubAuthAppInteractions() {
  285. when(gitHubSettings.appId()).thenReturn("432");
  286. when(gitHubSettings.privateKey()).thenReturn("private key");
  287. when(gitHubSettings.apiURL()).thenReturn("http://www.url.com");
  288. AppInstallationToken appInstallationToken = mock();
  289. when(appClient.getInstallationId(any(), any())).thenReturn(Optional.of(321L));
  290. when(appClient.createAppInstallationToken(any(), eq(321L))).thenReturn(Optional.of(appInstallationToken));
  291. GsonRepositoryCollaborator gsonRepositoryCollaborator = new GsonRepositoryCollaborator("toto", 2, "admin", new GsonRepositoryPermissions(true, true, true, true, true));
  292. when(appClient.getRepositoryCollaborators(gitHubSettings.apiURL(), appInstallationToken, "octocat", PROJECT_KEY_NAME)).thenReturn(Set.of(gsonRepositoryCollaborator));
  293. String role = gsonRepositoryCollaborator.roleName();
  294. GsonRepositoryPermissions permissions = gsonRepositoryCollaborator.permissions();
  295. when(githubPermissionConverter.toSonarqubeRolesWithFallbackOnRepositoryPermissions(Set.of(), role, permissions)).thenReturn(Set.of("scan"));
  296. }
  297. @Test
  298. public void importProject_shouldSetCreationMethodToApi_ifNonBrowserRequest() {
  299. AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
  300. mockGithubDevOpsAppInteractions();
  301. Projects.CreateWsResponse response = callWebService(githubAlmSetting);
  302. assertThat(response.getProject().getKey()).isEqualTo(GENERATED_PROJECT_KEY);
  303. Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), response.getProject().getKey());
  304. assertThat(projectDto.orElseThrow().getCreationMethod()).isEqualTo(CreationMethod.ALM_IMPORT_API);
  305. }
  306. @Test
  307. public void importProject_shouldSetCreationMethodToBrowser_ifBrowserRequest() {
  308. AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
  309. userSession.flagSessionAsGui();
  310. mockGithubDevOpsAppInteractions();
  311. Projects.CreateWsResponse response = callWebService(githubAlmSetting);
  312. assertThat(response.getProject().getKey()).isEqualTo(GENERATED_PROJECT_KEY);
  313. Optional<ProjectDto> projectDto = db.getDbClient().projectDao().selectProjectByKey(db.getSession(), response.getProject().getKey());
  314. assertThat(projectDto.orElseThrow().getCreationMethod()).isEqualTo(CreationMethod.ALM_IMPORT_BROWSER);
  315. }
  316. @Test
  317. public void importProject_whenAlmSettingKeyDoesNotExist_shouldThrow() {
  318. UserDto user = db.users().insertUser();
  319. userSession.logIn(user).addPermission(GlobalPermission.PROVISION_PROJECTS);
  320. TestRequest request = ws.newRequest()
  321. .setParam(PARAM_ALM_SETTING, "unknown")
  322. .setParam(PARAM_REPOSITORY_KEY, "test/repo");
  323. assertThatThrownBy(request::execute)
  324. .isInstanceOf(NotFoundException.class)
  325. .hasMessage("DevOps Platform configuration 'unknown' not found.");
  326. }
  327. @Test
  328. public void importProject_whenNoAlmSettingKeyAndNoConfig_shouldThrow() {
  329. UserDto user = db.users().insertUser();
  330. userSession.logIn(user).addPermission(GlobalPermission.PROVISION_PROJECTS);
  331. TestRequest request = ws.newRequest()
  332. .setParam(PARAM_REPOSITORY_KEY, "test/repo");
  333. assertThatThrownBy(request::execute)
  334. .isInstanceOf(NotFoundException.class)
  335. .hasMessage("There is no GITHUB configuration for DevOps Platform. Please add one.");
  336. }
  337. @Test
  338. public void importProject_whenNoAlmSettingKeyAndMultipleConfigs_shouldThrow() {
  339. UserDto user = db.users().insertUser();
  340. userSession.logIn(user).addPermission(GlobalPermission.PROVISION_PROJECTS);
  341. db.almSettings().insertGitHubAlmSetting();
  342. db.almSettings().insertGitHubAlmSetting();
  343. TestRequest request = ws.newRequest()
  344. .setParam(PARAM_REPOSITORY_KEY, "test/repo");
  345. assertThatThrownBy(request::execute)
  346. .isInstanceOf(IllegalArgumentException.class)
  347. .hasMessage("Parameter almSetting is required as there are multiple DevOps Platform configurations.");
  348. }
  349. @Test
  350. public void importProject_whenProvisioningIsEnabledButConfigDoesNotAllowAccessToRepo_shouldThrow() {
  351. AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
  352. when(gitHubSettings.isProvisioningEnabled()).thenReturn(true);
  353. mockGithubDevOpsAppInteractions();
  354. mockGithubAuthAppInteractions();
  355. when(appClient.getInstallationId(any(), any())).thenReturn(Optional.empty());
  356. TestRequest request = ws.newRequest()
  357. .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
  358. .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME);
  359. assertThatThrownBy(request::execute)
  360. .isInstanceOf(BadConfigurationException.class)
  361. .hasMessage(format("GitHub auto-provisioning is activated. However the repo %s is not in the scope of the authentication application. "
  362. + "The permissions can't be checked, and the project can not be created.",
  363. "octocat/" + PROJECT_KEY_NAME));
  364. }
  365. @Test
  366. public void importProject_whenNoAlmSettingKeyAndOnlyOneConfig_shouldImport() {
  367. AlmSettingDto githubAlmSetting = setupUserWithPatAndAlmSettings();
  368. mockGithubDevOpsAppInteractions();
  369. TestRequest request = ws.newRequest()
  370. .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
  371. .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME);
  372. assertThatNoException().isThrownBy(request::execute);
  373. }
  374. private Projects.CreateWsResponse callWebService(AlmSettingDto githubAlmSetting) {
  375. return ws.newRequest()
  376. .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
  377. .setParam(PARAM_REPOSITORY_KEY, "octocat/" + PROJECT_KEY_NAME)
  378. .executeProtobuf(Projects.CreateWsResponse.class);
  379. }
  380. private GithubApplicationClient.Repository mockGithubDevOpsAppInteractions() {
  381. GithubApplicationClient.Repository repository = new GithubApplicationClient.Repository(1L, PROJECT_KEY_NAME, false,
  382. "octocat/" + PROJECT_KEY_NAME,
  383. "https://github.sonarsource.com/api/v3/repos/octocat/" + PROJECT_KEY_NAME, "default-branch");
  384. when(appClient.getRepository(any(), any(), any())).thenReturn(Optional.of(repository));
  385. when(projectKeyGenerator.generateUniqueProjectKey(repository.getFullName())).thenReturn(GENERATED_PROJECT_KEY);
  386. return repository;
  387. }
  388. @Test
  389. public void fail_when_not_logged_in() {
  390. TestRequest request = ws.newRequest()
  391. .setParam(PARAM_ALM_SETTING, "asdfghjkl")
  392. .setParam(PARAM_REPOSITORY_KEY, "test/repo");
  393. assertThatThrownBy(request::execute)
  394. .isInstanceOf(UnauthorizedException.class);
  395. }
  396. @Test
  397. public void fail_when_missing_create_project_permission() {
  398. TestRequest request = ws.newRequest();
  399. assertThatThrownBy(request::execute)
  400. .isInstanceOf(UnauthorizedException.class);
  401. }
  402. @Test
  403. public void fail_when_personal_access_token_doesnt_exist() {
  404. AlmSettingDto githubAlmSetting = setupUserAndAlmSettings();
  405. TestRequest request = ws.newRequest()
  406. .setParam(PARAM_ALM_SETTING, githubAlmSetting.getKey())
  407. .setParam(PARAM_REPOSITORY_KEY, "test/repo");
  408. assertThatThrownBy(request::execute)
  409. .isInstanceOf(IllegalArgumentException.class)
  410. .hasMessage("No personal access token found");
  411. }
  412. @Test
  413. public void definition() {
  414. WebService.Action def = ws.getDef();
  415. assertThat(def.since()).isEqualTo("8.4");
  416. assertThat(def.isPost()).isTrue();
  417. assertThat(def.params())
  418. .extracting(WebService.Param::key, WebService.Param::isRequired)
  419. .containsExactlyInAnyOrder(
  420. tuple(PARAM_ALM_SETTING, false),
  421. tuple(PARAM_REPOSITORY_KEY, true),
  422. tuple(PARAM_NEW_CODE_DEFINITION_TYPE, false),
  423. tuple(PARAM_NEW_CODE_DEFINITION_VALUE, false));
  424. }
  425. private AlmSettingDto setupUserWithPatAndAlmSettings() {
  426. AlmSettingDto almSettings = setupUserAndAlmSettings();
  427. db.almPats().insert(p -> p.setAlmSettingUuid(almSettings.getUuid()).setUserUuid(requireNonNull(userSession.getUuid())));
  428. return almSettings;
  429. }
  430. private AlmSettingDto setupUserAndAlmSettings() {
  431. UserDto user = db.users().insertUser();
  432. userSession.logIn(user).addPermission(GlobalPermission.PROVISION_PROJECTS);
  433. return db.almSettings().insertGitHubAlmSetting(alm -> alm.setClientId("client_123").setClientSecret("client_secret_123"));
  434. }
  435. }