aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-webserver-webapi-v2
diff options
context:
space:
mode:
authorAntoine Vigneau <antoine.vigneau@sonarsource.com>2023-12-21 17:19:14 +0100
committersonartech <sonartech@sonarsource.com>2023-12-22 20:03:03 +0000
commitfce9db4d8dff4cce21eb3e9fbe72e7c1ad9f4540 (patch)
tree98ae0fa57abbc3e5dab809294b41286405e71d4d /server/sonar-webserver-webapi-v2
parent7453617939eff3e8d3f5cc4c84f21b38c5c50701 (diff)
downloadsonarqube-fce9db4d8dff4cce21eb3e9fbe72e7c1ad9f4540.tar.gz
sonarqube-fce9db4d8dff4cce21eb3e9fbe72e7c1ad9f4540.zip
SONAR-21121 Move Gitlab Config REST endpoints to Community edition
Diffstat (limited to 'server/sonar-webserver-webapi-v2')
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java2
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/controller/DefaultGitlabConfigurationController.java149
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/controller/GitlabConfigurationController.java97
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/controller/package-info.java23
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/request/GitlabConfigurationCreateRestRequest.java65
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/request/GitlabConfigurationUpdateRestRequest.java122
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/request/package-info.java23
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/resource/GitlabConfigurationResource.java48
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/resource/package-info.java23
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/response/GitlabConfigurationSearchRestResponse.java27
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/response/package-info.java23
-rw-r--r--server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java14
-rw-r--r--server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/gitlab/config/DefaultGitlabConfigurationControllerTest.java432
13 files changed, 1048 insertions, 0 deletions
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java
index bfb2288591b..26826997710 100644
--- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/WebApiEndpoints.java
@@ -36,6 +36,8 @@ public class WebApiEndpoints {
public static final String CLEAN_CODE_POLICY_DOMAIN = "/clean-code-policy";
public static final String RULES_ENDPOINT = CLEAN_CODE_POLICY_DOMAIN + "/rules";
+ public static final String GITLAB_CONFIGURATION_ENDPOINT = DOP_TRANSLATION_DOMAIN + "/gitlab-configurations";
+
public static final String INTERNAL = "internal";
private WebApiEndpoints() {
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/controller/DefaultGitlabConfigurationController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/controller/DefaultGitlabConfigurationController.java
new file mode 100644
index 00000000000..588457f52bd
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/controller/DefaultGitlabConfigurationController.java
@@ -0,0 +1,149 @@
+/*
+ * 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.v2.api.gitlab.config.controller;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import org.sonar.server.common.gitlab.config.GitlabConfiguration;
+import org.sonar.server.common.gitlab.config.GitlabConfigurationService;
+import org.sonar.server.common.gitlab.config.SynchronizationType;
+import org.sonar.server.common.gitlab.config.UpdateGitlabConfigurationRequest;
+import org.sonar.server.user.UserSession;
+import org.sonar.server.v2.api.gitlab.config.request.GitlabConfigurationCreateRestRequest;
+import org.sonar.server.v2.api.gitlab.config.request.GitlabConfigurationUpdateRestRequest;
+import org.sonar.server.v2.api.gitlab.config.resource.GitlabConfigurationResource;
+import org.sonar.server.v2.api.gitlab.config.response.GitlabConfigurationSearchRestResponse;
+import org.sonar.server.v2.api.response.PageRestResponse;
+
+import static org.sonar.server.common.gitlab.config.GitlabConfigurationService.UNIQUE_GITLAB_CONFIGURATION_ID;
+
+public class DefaultGitlabConfigurationController implements GitlabConfigurationController {
+
+ private final UserSession userSession;
+ private final GitlabConfigurationService gitlabConfigurationService;
+
+ public DefaultGitlabConfigurationController(UserSession userSession, GitlabConfigurationService gitlabConfigurationService) {
+ this.userSession = userSession;
+ this.gitlabConfigurationService = gitlabConfigurationService;
+ }
+
+ @Override
+ public GitlabConfigurationResource getGitlabConfiguration(String id) {
+ userSession.checkIsSystemAdministrator();
+ return getGitlabConfigurationResource(id);
+ }
+
+ @Override
+ public GitlabConfigurationSearchRestResponse searchGitlabConfiguration() {
+ userSession.checkIsSystemAdministrator();
+
+ List<GitlabConfigurationResource> gitlabConfigurationResources = gitlabConfigurationService.findConfigurations()
+ .stream()
+ .map(DefaultGitlabConfigurationController::toGitLabConfigurationResource)
+ .toList();
+
+ PageRestResponse pageRestResponse = new PageRestResponse(1, 1000, gitlabConfigurationResources.size());
+ return new GitlabConfigurationSearchRestResponse(gitlabConfigurationResources, pageRestResponse);
+ }
+
+ @Override
+ public GitlabConfigurationResource create(GitlabConfigurationCreateRestRequest createRequest) {
+ userSession.checkIsSystemAdministrator();
+ GitlabConfiguration createdConfiguration = gitlabConfigurationService.createConfiguration(toGitlabConfiguration(createRequest));
+ return toGitLabConfigurationResource(createdConfiguration);
+ }
+
+
+ private static GitlabConfiguration toGitlabConfiguration(GitlabConfigurationCreateRestRequest createRestRequest) {
+ return new GitlabConfiguration(
+ UNIQUE_GITLAB_CONFIGURATION_ID,
+ createRestRequest.enabled(),
+ createRestRequest.applicationId(),
+ createRestRequest.url(),
+ createRestRequest.secret(),
+ createRestRequest.synchronizeGroups(),
+ toSynchronizationType(createRestRequest.synchronizationType()),
+ createRestRequest.allowUsersToSignUp() != null && createRestRequest.allowUsersToSignUp(),
+ createRestRequest.provisioningToken(),
+ createRestRequest.provisioningGroups() == null ? Set.of() : Set.copyOf(createRestRequest.provisioningGroups()));
+ }
+
+ private GitlabConfigurationResource getGitlabConfigurationResource(String id) {
+ return toGitLabConfigurationResource(gitlabConfigurationService.getConfiguration(id));
+ }
+
+ @Override
+ public GitlabConfigurationResource updateGitlabConfiguration(String id, GitlabConfigurationUpdateRestRequest updateRequest) {
+ userSession.checkIsSystemAdministrator();
+ UpdateGitlabConfigurationRequest updateGitlabConfigurationRequest = toUpdateGitlabConfigurationRequest(id, updateRequest);
+ return toGitLabConfigurationResource(gitlabConfigurationService.updateConfiguration(updateGitlabConfigurationRequest));
+ }
+
+ private static UpdateGitlabConfigurationRequest toUpdateGitlabConfigurationRequest(String id,
+ GitlabConfigurationUpdateRestRequest updateRequest) {
+ return UpdateGitlabConfigurationRequest.builder()
+ .gitlabConfigurationId(id)
+ .enabled(updateRequest.getEnabled().toNonNullUpdatedValue())
+ .applicationId(updateRequest.getApplicationId().toNonNullUpdatedValue())
+ .url(updateRequest.getUrl().toNonNullUpdatedValue())
+ .secret(updateRequest.getSecret().toNonNullUpdatedValue())
+ .synchronizeGroups(updateRequest.getSynchronizeGroups().toNonNullUpdatedValue())
+ .synchronizationType(updateRequest.getSynchronizationType().map(DefaultGitlabConfigurationController::toSynchronizationType).toNonNullUpdatedValue())
+ .allowUserToSignUp(updateRequest.getAllowUsersToSignUp().toNonNullUpdatedValue())
+ .provisioningToken(updateRequest.getProvisioningToken().toUpdatedValue())
+ .provisioningGroups(updateRequest.getProvisioningGroups().map(DefaultGitlabConfigurationController::getGroups).toNonNullUpdatedValue())
+ .build();
+ }
+
+ private static Set<String> getGroups(List<String> groups) {
+ return new HashSet<>(groups);
+ }
+
+ private static GitlabConfigurationResource toGitLabConfigurationResource(GitlabConfiguration configuration) {
+ return new GitlabConfigurationResource(
+ configuration.id(),
+ configuration.enabled(),
+ configuration.applicationId(),
+ configuration.url(),
+ configuration.synchronizeGroups(),
+ toRestSynchronizationType(configuration),
+ configuration.allowUsersToSignUp(),
+ sortGroups(configuration.provisioningGroups()));
+ }
+
+ private static SynchronizationType toRestSynchronizationType(GitlabConfiguration configuration) {
+ return SynchronizationType.valueOf(configuration.synchronizationType().name());
+ }
+
+ private static SynchronizationType toSynchronizationType(SynchronizationType synchronizationType) {
+ return SynchronizationType.valueOf(synchronizationType.name());
+ }
+
+ private static List<String> sortGroups(Set<String> groups) {
+ return groups.stream().sorted().toList();
+ }
+
+ @Override
+ public void deleteGitlabConfiguration(String id) {
+ userSession.checkIsSystemAdministrator();
+ gitlabConfigurationService.deleteConfiguration(id);
+ }
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/controller/GitlabConfigurationController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/controller/GitlabConfigurationController.java
new file mode 100644
index 00000000000..474da3d29cc
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/controller/GitlabConfigurationController.java
@@ -0,0 +1,97 @@
+/*
+ * 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.v2.api.gitlab.config.controller;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.Parameter;
+import io.swagger.v3.oas.annotations.enums.ParameterIn;
+import io.swagger.v3.oas.annotations.extensions.Extension;
+import io.swagger.v3.oas.annotations.extensions.ExtensionProperty;
+import javax.validation.Valid;
+import org.sonar.server.v2.api.gitlab.config.request.GitlabConfigurationCreateRestRequest;
+import org.sonar.server.v2.api.gitlab.config.request.GitlabConfigurationUpdateRestRequest;
+import org.sonar.server.v2.api.gitlab.config.resource.GitlabConfigurationResource;
+import org.sonar.server.v2.api.gitlab.config.response.GitlabConfigurationSearchRestResponse;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PatchMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestController;
+
+import static org.sonar.server.v2.WebApiEndpoints.GITLAB_CONFIGURATION_ENDPOINT;
+import static org.sonar.server.v2.WebApiEndpoints.INTERNAL;
+import static org.sonar.server.v2.WebApiEndpoints.JSON_MERGE_PATCH_CONTENT_TYPE;
+
+@RequestMapping(GITLAB_CONFIGURATION_ENDPOINT)
+@RestController
+public interface GitlabConfigurationController {
+
+ @GetMapping(path = "/{id}")
+ @ResponseStatus(HttpStatus.OK)
+ @Operation(summary = "Fetch a GitLab configuration", description = """
+ Fetch a GitLab configuration. Requires 'Administer System' permission.
+ """,
+ extensions = @Extension(properties = {@ExtensionProperty(name = INTERNAL, value = "true")}))
+ GitlabConfigurationResource getGitlabConfiguration(
+ @PathVariable("id") @Parameter(description = "The id of the configuration to fetch.", required = true, in = ParameterIn.PATH) String id);
+
+ @GetMapping
+ @Operation(summary = "Search GitLab configs", description = """
+ Get the list of GitLab configurations.
+ Note that a single configuration is supported at this time.
+ Requires 'Administer System' permission.
+ """,
+ extensions = @Extension(properties = {@ExtensionProperty(name = INTERNAL, value = "true")}))
+ GitlabConfigurationSearchRestResponse searchGitlabConfiguration();
+
+ @PatchMapping(path = "/{id}", consumes = JSON_MERGE_PATCH_CONTENT_TYPE, produces = MediaType.APPLICATION_JSON_VALUE)
+ @ResponseStatus(HttpStatus.OK)
+ @Operation(summary = "Update a Gitlab configuration", description = """
+ Update a Gitlab configuration. Requires 'Administer System' permission.
+ """,
+ extensions = @Extension(properties = {@ExtensionProperty(name = INTERNAL, value = "true")}))
+ GitlabConfigurationResource updateGitlabConfiguration(@PathVariable("id") String id, @Valid @RequestBody GitlabConfigurationUpdateRestRequest updateRequest);
+
+ @PostMapping
+ @Operation(summary = "Create Gitlab configuration", description = """
+ Create a new Gitlab configuration.
+ Note that only a single configuration can exist at a time.
+ Requires 'Administer System' permission.
+ """,
+ extensions = @Extension(properties = {@ExtensionProperty(name = INTERNAL, value = "true")}))
+ GitlabConfigurationResource create(@Valid @RequestBody GitlabConfigurationCreateRestRequest createRequest);
+
+ @DeleteMapping(path = "/{id}")
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ @Operation(summary = "Delete a GitLab configuration", description = """
+ Delete a GitLab configuration.
+ Requires 'Administer System' permission.
+ """,
+ extensions = @Extension(properties = {@ExtensionProperty(name = INTERNAL, value = "true")}))
+ void deleteGitlabConfiguration(
+ @PathVariable("id") @Parameter(description = "The id of the configuration to delete.", required = true, in = ParameterIn.PATH) String id);
+
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/controller/package-info.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/controller/package-info.java
new file mode 100644
index 00000000000..5f2a9b2c298
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/controller/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.v2.api.gitlab.config.controller;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/request/GitlabConfigurationCreateRestRequest.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/request/GitlabConfigurationCreateRestRequest.java
new file mode 100644
index 00000000000..dadbe48f756
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/request/GitlabConfigurationCreateRestRequest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.v2.api.gitlab.config.request;
+
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.List;
+import javax.annotation.Nullable;
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import org.sonar.server.common.gitlab.config.SynchronizationType;
+
+public record GitlabConfigurationCreateRestRequest(
+
+ @NotNull
+ @Schema(description = "Enable Gitlab authentication")
+ boolean enabled,
+
+ @NotEmpty
+ @Schema(description = "Gitlab Application id")
+ String applicationId,
+ @NotEmpty
+ @Schema(description = "Url of Gitlab instance for authentication (for instance https://gitlab.com)")
+ String url,
+ @NotEmpty
+ @Schema(accessMode = Schema.AccessMode.WRITE_ONLY, description = "Secret of the application")
+ String secret,
+
+ @NotNull
+ @Schema(description = "Set whether to synchronize groups")
+ Boolean synchronizeGroups,
+
+ @NotNull
+ @Schema(description = "Type of synchronization")
+ SynchronizationType synchronizationType,
+ @Nullable
+ @Schema(accessMode = Schema.AccessMode.WRITE_ONLY, description = "Gitlab token for provisioning")
+ String provisioningToken,
+
+ @Schema(description = "Allow user to sign up")
+ @Nullable
+ Boolean allowUsersToSignUp,
+
+ @ArraySchema(arraySchema = @Schema(description = "Root GitLab groups to provision."))
+ @Nullable
+ List<String> provisioningGroups
+) {
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/request/GitlabConfigurationUpdateRestRequest.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/request/GitlabConfigurationUpdateRestRequest.java
new file mode 100644
index 00000000000..5616a86a0eb
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/request/GitlabConfigurationUpdateRestRequest.java
@@ -0,0 +1,122 @@
+/*
+ * 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.v2.api.gitlab.config.request;
+
+import io.swagger.v3.oas.annotations.media.ArraySchema;
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.List;
+import javax.validation.constraints.Size;
+import org.sonar.server.common.gitlab.config.SynchronizationType;
+import org.sonar.server.v2.common.model.UpdateField;
+
+public class GitlabConfigurationUpdateRestRequest {
+
+ private UpdateField<Boolean> enabled = UpdateField.undefined();
+ private UpdateField<String> applicationId = UpdateField.undefined();
+ private UpdateField<String> url = UpdateField.undefined();
+ private UpdateField<String> secret = UpdateField.undefined();
+ private UpdateField<Boolean> synchronizeGroups = UpdateField.undefined();
+ private UpdateField<SynchronizationType> synchronizationType = UpdateField.undefined();
+ private UpdateField<Boolean> allowUsersToSignUp = UpdateField.undefined();
+ private UpdateField<String> provisioningToken = UpdateField.undefined();
+ private UpdateField<List<String>> provisioningGroups = UpdateField.undefined();
+
+ @Schema(implementation = Boolean.class, description = "Enable Gitlab authentication")
+ public UpdateField<Boolean> getEnabled() {
+ return enabled;
+ }
+
+ public void setEnabled(Boolean enabled) {
+ this.enabled = UpdateField.withValue(enabled);
+ }
+
+ @Schema(implementation = String.class, description = "Gitlab Application id")
+ public UpdateField<String> getApplicationId() {
+ return applicationId;
+ }
+
+ public void setApplicationId(String applicationId) {
+ this.applicationId = UpdateField.withValue(applicationId);
+ }
+
+ @Schema(implementation = String.class, description = "Url of Gitlab instance for authentication (for instance https://gitlab.com/api/v4)")
+ public UpdateField<String> getUrl() {
+ return url;
+ }
+
+ public void setUrl(String url) {
+ this.url = UpdateField.withValue(url);
+ }
+
+ @Schema(implementation = String.class, description = "Secret of the application", nullable = true)
+ public UpdateField<String> getSecret() {
+ return secret;
+ }
+
+ public void setSecret(String secret) {
+ this.secret = UpdateField.withValue(secret);
+ }
+
+ @Schema(implementation = Boolean.class, description = "Set whether to synchronize groups")
+ public UpdateField<Boolean> getSynchronizeGroups() {
+ return synchronizeGroups;
+ }
+
+ public void setSynchronizeGroups(Boolean synchronizeGroups) {
+ this.synchronizeGroups = UpdateField.withValue(synchronizeGroups);
+ }
+
+ @Schema(implementation = SynchronizationType.class, description = "Type of synchronization")
+ public UpdateField<SynchronizationType> getSynchronizationType() {
+ return synchronizationType;
+ }
+
+ public void setSynchronizationType(SynchronizationType synchronizationType) {
+ this.synchronizationType = UpdateField.withValue(synchronizationType);
+ }
+
+ @Schema(implementation = Boolean.class, description = "Allow user to sign up")
+ public UpdateField<Boolean> getAllowUsersToSignUp() {
+ return allowUsersToSignUp;
+ }
+
+ public void setAllowUsersToSignUp(Boolean allowUsersToSignUp) {
+ this.allowUsersToSignUp = UpdateField.withValue(allowUsersToSignUp);
+ }
+
+ @Size(min = 1)
+ @Schema(implementation = String.class, description = "Gitlab token for provisioning", nullable = true)
+ public UpdateField<String> getProvisioningToken() {
+ return provisioningToken;
+ }
+
+ public void setProvisioningToken(String provisioningToken) {
+ this.provisioningToken = UpdateField.withValue(provisioningToken);
+ }
+
+ @ArraySchema(arraySchema = @Schema(description = "Root gitlab groups to provision."), schema = @Schema(implementation = String.class))
+ public UpdateField<List<String>> getProvisioningGroups() {
+ return provisioningGroups;
+ }
+
+ public void setProvisioningGroups(List<String> provisioningGroups) {
+ this.provisioningGroups = UpdateField.withValue(provisioningGroups);
+ }
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/request/package-info.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/request/package-info.java
new file mode 100644
index 00000000000..9557f9af25b
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/request/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.v2.api.gitlab.config.request;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/resource/GitlabConfigurationResource.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/resource/GitlabConfigurationResource.java
new file mode 100644
index 00000000000..eca624cc19a
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/resource/GitlabConfigurationResource.java
@@ -0,0 +1,48 @@
+/*
+ * 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.v2.api.gitlab.config.resource;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import java.util.List;
+import org.sonar.server.common.gitlab.config.SynchronizationType;
+
+public record GitlabConfigurationResource(
+
+ @Schema(accessMode = Schema.AccessMode.READ_ONLY)
+ String id,
+
+ boolean enabled,
+
+ @Schema(implementation = String.class, description = "Gitlab Application id")
+ String applicationId,
+
+ @Schema(description = "Url of Gitlab instance for authentication (for instance https://gitlab.com/api/v4)")
+ String url,
+
+ boolean synchronizeGroups,
+
+ SynchronizationType synchronizationType,
+
+ boolean allowUsersToSignUp,
+
+ @Schema(description = "Root Gitlab groups to provision")
+ List<String> provisioningGroups
+) {
+}
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/resource/package-info.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/resource/package-info.java
new file mode 100644
index 00000000000..47e29020ad5
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/resource/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.v2.api.gitlab.config.resource;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/response/GitlabConfigurationSearchRestResponse.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/response/GitlabConfigurationSearchRestResponse.java
new file mode 100644
index 00000000000..ddadc8c2913
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/response/GitlabConfigurationSearchRestResponse.java
@@ -0,0 +1,27 @@
+/*
+ * 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.v2.api.gitlab.config.response;
+
+import java.util.List;
+import org.sonar.server.v2.api.gitlab.config.resource.GitlabConfigurationResource;
+import org.sonar.server.v2.api.response.PageRestResponse;
+
+public record GitlabConfigurationSearchRestResponse(List<GitlabConfigurationResource> gitlabConfigurations, PageRestResponse page) {}
+
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/response/package-info.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/response/package-info.java
new file mode 100644
index 00000000000..1d3e083fd97
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/gitlab/config/response/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonar.server.v2.api.gitlab.config.response;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java
index 3df7ae8ebe1..c1b63631b08 100644
--- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java
+++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/config/PlatformLevel4WebConfig.java
@@ -22,6 +22,7 @@ package org.sonar.server.v2.config;
import javax.annotation.Nullable;
import org.sonar.api.resources.Languages;
import org.sonar.db.DbClient;
+import org.sonar.server.common.gitlab.config.GitlabConfigurationService;
import org.sonar.server.common.group.service.GroupMembershipService;
import org.sonar.server.common.group.service.GroupService;
import org.sonar.server.common.health.CeStatusNodeCheck;
@@ -35,10 +36,13 @@ import org.sonar.server.common.rule.service.RuleService;
import org.sonar.server.common.text.MacroInterpreter;
import org.sonar.server.common.user.service.UserService;
import org.sonar.server.health.HealthChecker;
+import org.sonar.server.management.ManagedInstanceService;
import org.sonar.server.platform.NodeInformation;
import org.sonar.server.rule.RuleDescriptionFormatter;
import org.sonar.server.user.SystemPasscode;
import org.sonar.server.user.UserSession;
+import org.sonar.server.v2.api.gitlab.config.controller.DefaultGitlabConfigurationController;
+import org.sonar.server.v2.api.gitlab.config.controller.GitlabConfigurationController;
import org.sonar.server.v2.api.group.controller.DefaultGroupController;
import org.sonar.server.v2.api.group.controller.GroupController;
import org.sonar.server.v2.api.membership.controller.DefaultGroupMembershipController;
@@ -121,4 +125,14 @@ public class PlatformLevel4WebConfig {
return handlerMapping;
}
+ @Bean
+ public GitlabConfigurationService gitlabConfigurationService(ManagedInstanceService managedInstanceService, DbClient dbClient) {
+ return new GitlabConfigurationService( managedInstanceService, dbClient);
+ }
+
+ @Bean
+ public GitlabConfigurationController gitlabConfigurationController(UserSession userSession, GitlabConfigurationService gitlabConfigurationService) {
+ return new DefaultGitlabConfigurationController(userSession, gitlabConfigurationService);
+ }
+
}
diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/gitlab/config/DefaultGitlabConfigurationControllerTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/gitlab/config/DefaultGitlabConfigurationControllerTest.java
new file mode 100644
index 00000000000..bcaea1bb2b5
--- /dev/null
+++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/gitlab/config/DefaultGitlabConfigurationControllerTest.java
@@ -0,0 +1,432 @@
+/*
+ * 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.v2.api.gitlab.config;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.server.common.NonNullUpdatedValue;
+import org.sonar.server.common.UpdatedValue;
+import org.sonar.server.common.gitlab.config.GitlabConfiguration;
+import org.sonar.server.common.gitlab.config.GitlabConfigurationService;
+import org.sonar.server.common.gitlab.config.SynchronizationType;
+import org.sonar.server.common.gitlab.config.UpdateGitlabConfigurationRequest;
+import org.sonar.server.exceptions.NotFoundException;
+import org.sonar.server.tester.UserSessionRule;
+import org.sonar.server.v2.api.ControllerTester;
+import org.sonar.server.v2.api.gitlab.config.controller.DefaultGitlabConfigurationController;
+import org.sonar.server.v2.api.gitlab.config.resource.GitlabConfigurationResource;
+import org.sonar.server.v2.api.gitlab.config.response.GitlabConfigurationSearchRestResponse;
+import org.springframework.http.MediaType;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.sonar.server.common.gitlab.config.SynchronizationType.AUTO_PROVISIONING;
+import static org.sonar.server.common.gitlab.config.SynchronizationType.JIT;
+import static org.sonar.server.v2.WebApiEndpoints.GITLAB_CONFIGURATION_ENDPOINT;
+import static org.sonar.server.v2.WebApiEndpoints.JSON_MERGE_PATCH_CONTENT_TYPE;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+public class DefaultGitlabConfigurationControllerTest {
+ private static final Gson GSON = new GsonBuilder().create();
+
+ private static final GitlabConfiguration GITLAB_CONFIGURATION = new GitlabConfiguration(
+ "existing-id",
+ true,
+ "application-id",
+ "www.url.com",
+ "secret",
+ true,
+ AUTO_PROVISIONING,
+ true,
+ "provisioning-token",
+ Set.of("provisioning-group2", "provisioning-group1"));
+ private static final GitlabConfigurationResource EXPECTED_GITLAB_CONF_RESOURCE = new GitlabConfigurationResource(
+ GITLAB_CONFIGURATION.id(),
+ GITLAB_CONFIGURATION.enabled(),
+ GITLAB_CONFIGURATION.applicationId(),
+ GITLAB_CONFIGURATION.url(),
+ GITLAB_CONFIGURATION.synchronizeGroups(),
+ SynchronizationType.valueOf(GITLAB_CONFIGURATION.synchronizationType().name()),
+ GITLAB_CONFIGURATION.allowUsersToSignUp(),
+ List.of("provisioning-group1", "provisioning-group2"));
+ private static final String EXPECTED_CONFIGURATION = """
+ {
+ "id": "existing-id",
+ "enabled": true,
+ "applicationId": "application-id",
+ "url": "www.url.com",
+ "synchronizeGroups": true,
+ "synchronizationType": "AUTO_PROVISIONING",
+ "allowUsersToSignUp": true,
+ "provisioningGroups": [
+ "provisioning-group2",
+ "provisioning-group1"
+ ]
+ }
+ """;
+
+ @Rule
+ public UserSessionRule userSession = UserSessionRule.standalone();
+ private final GitlabConfigurationService gitlabConfigurationService = mock();
+ private final MockMvc mockMvc = ControllerTester.getMockMvc(new DefaultGitlabConfigurationController(userSession, gitlabConfigurationService));
+
+ @Test
+ public void fetchConfiguration_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception {
+ userSession.logIn().setNonSystemAdministrator();
+
+ mockMvc.perform(get(GITLAB_CONFIGURATION_ENDPOINT + "/1"))
+ .andExpectAll(
+ status().isForbidden(),
+ content().json("{\"message\":\"Insufficient privileges\"}"));
+ }
+
+ @Test
+ public void fetchConfiguration_whenConfigNotFound_throws() throws Exception {
+ userSession.logIn().setSystemAdministrator();
+ when(gitlabConfigurationService.getConfiguration("not-existing")).thenThrow(new NotFoundException("bla"));
+
+ mockMvc.perform(get(GITLAB_CONFIGURATION_ENDPOINT + "/not-existing"))
+ .andExpectAll(
+ status().isNotFound(),
+ content().json("{\"message\":\"bla\"}"));
+ }
+
+ @Test
+ public void fetchConfiguration_whenConfigFound_returnsIt() throws Exception {
+ userSession.logIn().setSystemAdministrator();
+ when(gitlabConfigurationService.getConfiguration("existing-id")).thenReturn(GITLAB_CONFIGURATION);
+
+ mockMvc.perform(get(GITLAB_CONFIGURATION_ENDPOINT + "/existing-id"))
+ .andExpectAll(
+ status().isOk(),
+ content().json(EXPECTED_CONFIGURATION));
+ }
+
+ @Test
+ public void search_whenNoParameters_shouldUseDefaultAndForwardToGroupMembershipService() throws Exception {
+ userSession.logIn().setSystemAdministrator();
+ when(gitlabConfigurationService.findConfigurations()).thenReturn(Optional.of(GITLAB_CONFIGURATION));
+
+ MvcResult mvcResult = mockMvc.perform(get(GITLAB_CONFIGURATION_ENDPOINT))
+ .andExpect(status().isOk())
+ .andReturn();
+
+ GitlabConfigurationSearchRestResponse gitlabConfigurationResource = GSON.fromJson(mvcResult.getResponse().getContentAsString(), GitlabConfigurationSearchRestResponse.class);
+
+ assertThat(gitlabConfigurationResource.page().pageSize()).isEqualTo(1000);
+ assertThat(gitlabConfigurationResource.page().pageIndex()).isEqualTo(1);
+ assertThat(gitlabConfigurationResource.page().total()).isEqualTo(1);
+ assertThat(gitlabConfigurationResource.gitlabConfigurations()).containsExactly(EXPECTED_GITLAB_CONF_RESOURCE);
+ }
+
+ @Test
+ public void search_whenNoParametersAndNoConfig_shouldReturnEmptyList() throws Exception {
+ userSession.logIn().setSystemAdministrator();
+ when(gitlabConfigurationService.findConfigurations()).thenReturn(Optional.empty());
+
+ MvcResult mvcResult = mockMvc.perform(get(GITLAB_CONFIGURATION_ENDPOINT))
+ .andExpect(status().isOk())
+ .andReturn();
+
+ GitlabConfigurationSearchRestResponse gitlabConfigurationResource = GSON.fromJson(mvcResult.getResponse().getContentAsString(), GitlabConfigurationSearchRestResponse.class);
+
+ assertThat(gitlabConfigurationResource.page().pageSize()).isEqualTo(1000);
+ assertThat(gitlabConfigurationResource.page().pageIndex()).isEqualTo(1);
+ assertThat(gitlabConfigurationResource.page().total()).isZero();
+ assertThat(gitlabConfigurationResource.gitlabConfigurations()).isEmpty();
+ }
+
+ @Test
+ public void updateConfiguration_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception {
+ userSession.logIn().setNonSystemAdministrator();
+
+ mockMvc.perform(patch(GITLAB_CONFIGURATION_ENDPOINT + "/existing-id")
+ .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
+ .content("{}"))
+ .andExpectAll(
+ status().isForbidden(),
+ content().json("{\"message\":\"Insufficient privileges\"}"));
+ }
+
+ @Test
+ public void updateConfiguration_whenAllFieldsUpdated_performUpdates() throws Exception {
+ userSession.logIn().setSystemAdministrator();
+ when(gitlabConfigurationService.updateConfiguration(any())).thenReturn(GITLAB_CONFIGURATION);
+
+ String payload = """
+ {
+ "enabled": true,
+ "applicationId": "application-id",
+ "url": "www.url.com",
+ "secret": "newSecret",
+ "synchronizeGroups": true,
+ "synchronizationType": "AUTO_PROVISIONING",
+ "allowUsersToSignUp": true,
+ "provisioningToken": "token",
+ "provisioningGroups": [
+ "provisioning-group2",
+ "provisioning-group1"
+ ]
+ }
+ """;
+
+ mockMvc.perform(patch(GITLAB_CONFIGURATION_ENDPOINT + "/existing-id")
+ .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
+ .content(payload))
+ .andExpectAll(
+ status().isOk(),
+ content().json(EXPECTED_CONFIGURATION));
+
+ verify(gitlabConfigurationService).updateConfiguration(new UpdateGitlabConfigurationRequest(
+ "existing-id",
+ NonNullUpdatedValue.withValueOrThrow(true),
+ NonNullUpdatedValue.withValueOrThrow("application-id"),
+ NonNullUpdatedValue.withValueOrThrow("www.url.com"),
+ NonNullUpdatedValue.withValueOrThrow("newSecret"),
+ NonNullUpdatedValue.withValueOrThrow(true),
+ NonNullUpdatedValue.withValueOrThrow(AUTO_PROVISIONING),
+ NonNullUpdatedValue.withValueOrThrow(true),
+ UpdatedValue.withValue("token"),
+ NonNullUpdatedValue.withValueOrThrow(Set.of("provisioning-group2", "provisioning-group1"))));
+ }
+
+ @Test
+ public void updateConfiguration_whenSomeFieldsUpdated_performUpdates() throws Exception {
+ userSession.logIn().setSystemAdministrator();
+ when(gitlabConfigurationService.updateConfiguration(any())).thenReturn(GITLAB_CONFIGURATION);
+
+ String payload = """
+ {
+ "enabled": false,
+ "synchronizationType": "JIT",
+ "allowUsersToSignUp": false,
+ "provisioningToken": null
+ }
+ """;
+
+ mockMvc.perform(patch(GITLAB_CONFIGURATION_ENDPOINT + "/existing-id")
+ .contentType(JSON_MERGE_PATCH_CONTENT_TYPE)
+ .content(payload))
+ .andExpectAll(
+ status().isOk(),
+ content().json(EXPECTED_CONFIGURATION));
+
+ verify(gitlabConfigurationService).updateConfiguration(new UpdateGitlabConfigurationRequest(
+ "existing-id",
+ NonNullUpdatedValue.withValueOrThrow(false),
+ NonNullUpdatedValue.undefined(),
+ NonNullUpdatedValue.undefined(),
+ NonNullUpdatedValue.undefined(),
+ NonNullUpdatedValue.undefined(),
+ NonNullUpdatedValue.withValueOrThrow(JIT),
+ NonNullUpdatedValue.withValueOrThrow(false),
+ UpdatedValue.withValue(null),
+ NonNullUpdatedValue.undefined()));
+ }
+
+ @Test
+ public void create_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception {
+ userSession.logIn().setNonSystemAdministrator();
+
+ mockMvc.perform(
+ post(GITLAB_CONFIGURATION_ENDPOINT)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content("""
+ {
+ "enabled": true,
+ "applicationId": "application-id",
+ "url": "www.url.com",
+ "secret": "123",
+ "synchronizeGroups": true,
+ "synchronizationType": "AUTO_PROVISIONING",
+ "allowUsersToSignUp": true,
+ "provisioningGroups": [
+ "provisioning-group2",
+ "provisioning-group1"
+ ]
+ }
+ """))
+ .andExpectAll(
+ status().isForbidden(),
+ content().json("{\"message\":\"Insufficient privileges\"}"));
+ }
+
+ @Test
+ public void create_whenConfigCreated_returnsIt() throws Exception {
+ userSession.logIn().setSystemAdministrator();
+ when(gitlabConfigurationService.createConfiguration(any())).thenReturn(GITLAB_CONFIGURATION);
+
+ mockMvc.perform(
+ post(GITLAB_CONFIGURATION_ENDPOINT)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content("""
+ {
+ "enabled": true,
+ "applicationId": "application-id",
+ "secret": "123",
+ "url": "www.url.com",
+ "synchronizeGroups": true,
+ "synchronizationType": "AUTO_PROVISIONING",
+ "allowUsersToSignUp": true,
+ "provisioningGroups": [
+ "provisioning-group2",
+ "provisioning-group1"
+ ]
+ }
+
+ """))
+ .andExpectAll(
+ status().isOk(),
+ content().json("""
+ {
+ "id": "existing-id",
+ "enabled": true,
+ "applicationId": "application-id",
+ "url": "www.url.com",
+ "synchronizeGroups": true,
+ "synchronizationType": "AUTO_PROVISIONING",
+ "allowUsersToSignUp": true,
+ "provisioningGroups": [
+ "provisioning-group2",
+ "provisioning-group1"
+ ]
+ }
+ """));
+
+ }
+ @Test
+ public void create_whenConfigCreatedWithoutOptionalParams_returnsIt() throws Exception {
+ userSession.logIn().setSystemAdministrator();
+ when(gitlabConfigurationService.createConfiguration(any())).thenReturn(GITLAB_CONFIGURATION);
+
+ mockMvc.perform(
+ post(GITLAB_CONFIGURATION_ENDPOINT)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content("""
+ {
+ "enabled": true,
+ "applicationId": "application-id",
+ "secret": "123",
+ "url": "www.url.com",
+ "synchronizeGroups": true,
+ "synchronizationType": "AUTO_PROVISIONING"
+ }
+
+ """))
+ .andExpectAll(
+ status().isOk(),
+ content().json("""
+ {
+ "id": "existing-id",
+ "enabled": true,
+ "applicationId": "application-id",
+ "url": "www.url.com",
+ "synchronizeGroups": true,
+ "synchronizationType": "AUTO_PROVISIONING",
+ "allowUsersToSignUp": true,
+ "provisioningGroups": [
+ "provisioning-group2",
+ "provisioning-group1"
+ ]
+ }
+ """));
+
+ }
+
+ @Test
+ public void create_whenRequiredParameterIsMissing_shouldReturnBadRequest() throws Exception {
+ userSession.logIn().setSystemAdministrator();
+
+ mockMvc.perform(
+ post(GITLAB_CONFIGURATION_ENDPOINT)
+ .contentType(MediaType.APPLICATION_JSON_VALUE)
+ .content("""
+ {
+ "enabled": true,
+ "applicationId": "application-id",
+ "url": "www.url.com",
+ "synchronizeGroups": true,
+ "synchronizationType": "AUTO_PROVISIONING",
+ "allowUsersToSignUp": true,
+ "provisioningGroups": [
+ "provisioning-group2",
+ "provisioning-group1"
+ ]
+ }
+ """))
+ .andExpectAll(
+ status().isBadRequest(),
+ content().json(
+ "{\"message\":\"Value {} for field secret was rejected. Error: must not be empty.\"}"));
+
+ }
+
+ @Test
+ public void delete_whenUserIsNotAdministrator_shouldReturnForbidden() throws Exception {
+ userSession.logIn().setNonSystemAdministrator();
+
+ mockMvc.perform(
+ delete(GITLAB_CONFIGURATION_ENDPOINT + "/existing-id"))
+ .andExpectAll(
+ status().isForbidden(),
+ content().json("{\"message\":\"Insufficient privileges\"}"));
+ }
+
+ @Test
+ public void delete_whenConfigIsDeleted_returnsNoContent() throws Exception {
+ userSession.logIn().setSystemAdministrator();
+
+ mockMvc.perform(
+ delete(GITLAB_CONFIGURATION_ENDPOINT + "/existing-id"))
+ .andExpectAll(
+ status().isNoContent());
+
+ verify(gitlabConfigurationService).deleteConfiguration("existing-id");
+ }
+
+ @Test
+ public void delete_whenConfigNotFound_returnsNotFound() throws Exception {
+ userSession.logIn().setSystemAdministrator();
+ doThrow(new NotFoundException("Not found")).when(gitlabConfigurationService).deleteConfiguration("not-existing");
+
+ mockMvc.perform(
+ delete(GITLAB_CONFIGURATION_ENDPOINT + "/not-existing"))
+ .andExpectAll(
+ status().isNotFound(),
+ content().json("{\"message\":\"Not found\"}"));
+ }
+
+}