Browse Source

SONAR-21058 Add POST to /api/v2/authorizations/groups

tags/10.4.0.87286
Wojtek Wajerowicz 7 months ago
parent
commit
6da592353b
14 changed files with 343 additions and 107 deletions
  1. 15
    8
      server/sonar-webserver-common/src/main/java/org/sonar/server/common/group/service/GroupService.java
  2. 33
    8
      server/sonar-webserver-common/src/test/java/org/sonar/server/common/group/service/GroupServiceTest.java
  3. 37
    28
      server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/group/controller/DefaultGroupController.java
  4. 10
    3
      server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/group/controller/GroupController.java
  5. 41
    0
      server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/group/request/GroupCreateRestRequest.java
  6. 1
    1
      server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/group/request/GroupUpdateRestRequest.java
  7. 8
    2
      server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/group/response/GroupRestResponse.java
  8. 1
    1
      server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/group/response/GroupsSearchRestResponse.java
  9. 181
    50
      server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/group/controller/DefaultGroupControllerTest.java
  10. 5
    0
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/usergroups/ws/CreateActionIT.java
  11. 7
    2
      server/sonar-webserver-webapi/src/it/java/org/sonar/server/usergroups/ws/ExternalGroupServiceIT.java
  12. 1
    1
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/CreateAction.java
  13. 1
    1
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/ExternalGroupService.java
  14. 2
    2
      server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/UpdateAction.java

+ 15
- 8
server/sonar-webserver-common/src/main/java/org/sonar/server/common/group/service/GroupService.java View File

@@ -63,8 +63,10 @@ public class GroupService {
return dbClient.groupDao().selectByName(dbSession, groupName);
}

public Optional<GroupDto> findGroupByUuid(DbSession dbSession, String groupUuid) {
return Optional.ofNullable(dbClient.groupDao().selectByUuid(dbSession, groupUuid));
public Optional<GroupInformation> findGroupByUuid(DbSession dbSession, String groupUuid) {

return Optional.ofNullable(dbClient.groupDao().selectByUuid(dbSession, groupUuid))
.map(group -> groupDtoToGroupInformation(group, dbSession));
}

public SearchResults<GroupInformation> search(DbSession dbSession, GroupSearchRequest groupSearchRequest) {
@@ -126,18 +128,18 @@ public class GroupService {
removeGroup(dbSession, group);
}

public GroupDto updateGroup(DbSession dbSession, GroupDto group, @Nullable String newName) {
public GroupInformation updateGroup(DbSession dbSession, GroupDto group, @Nullable String newName) {
checkGroupIsNotDefault(dbSession, group);
return updateName(dbSession, group, newName);
return groupDtoToGroupInformation(updateName(dbSession, group, newName), dbSession);
}

public GroupDto updateGroup(DbSession dbSession, GroupDto group, @Nullable String newName, @Nullable String newDescription) {
public GroupInformation updateGroup(DbSession dbSession, GroupDto group, @Nullable String newName, @Nullable String newDescription) {
checkGroupIsNotDefault(dbSession, group);
GroupDto withUpdatedName = updateName(dbSession, group, newName);
return updateDescription(dbSession, withUpdatedName, newDescription);
return groupDtoToGroupInformation(updateDescription(dbSession, withUpdatedName, newDescription), dbSession);
}

public GroupDto createGroup(DbSession dbSession, String name, @Nullable String description) {
public GroupInformation createGroup(DbSession dbSession, String name, @Nullable String description) {
validateGroupName(name);
checkNameDoesNotExist(dbSession, name);

@@ -145,7 +147,12 @@ public class GroupService {
.setUuid(uuidFactory.create())
.setName(name)
.setDescription(description);
return dbClient.groupDao().insert(dbSession, group);
return groupDtoToGroupInformation(dbClient.groupDao().insert(dbSession, group), dbSession);
}

private GroupInformation groupDtoToGroupInformation(GroupDto groupDto, DbSession dbSession) {
return new GroupInformation(groupDto, managedInstanceService.isGroupManaged(dbSession, groupDto.getUuid()),
defaultGroupFinder.findDefaultGroup(dbSession).getUuid().equals(groupDto.getUuid()));
}

private GroupDto updateName(DbSession dbSession, GroupDto group, @Nullable String newName) {

+ 33
- 8
server/sonar-webserver-common/src/test/java/org/sonar/server/common/group/service/GroupServiceTest.java View File

@@ -136,13 +136,31 @@ public class GroupServiceTest {
}

@Test
public void findGroupByUuid_whenGroupExists_returnsIt() {
public void findGroupByUuid_whenGroupExistsAndIsManagedAndDefault_returnsItWithCorrectValues() {
GroupDto groupDto = mockGroupDto();

when(defaultGroupFinder.findDefaultGroup(dbSession)).thenReturn(new GroupDto().setUuid(GROUP_UUID).setName("default-group"));

when(dbClient.groupDao().selectByUuid(dbSession, GROUP_UUID))
.thenReturn(groupDto);
when(managedInstanceService.isGroupManaged(dbSession, groupDto.getUuid())).thenReturn(true);

GroupInformation expected = new GroupInformation(groupDto, true, true);
assertThat(groupService.findGroupByUuid(dbSession, GROUP_UUID)).contains(expected);
}

@Test
public void findGroupByUuid_whenGroupExistsAndIsNotManagedAndDefault_returnsItWithCorrectValues() {
GroupDto groupDto = mockGroupDto();

when(defaultGroupFinder.findDefaultGroup(dbSession)).thenReturn(new GroupDto().setUuid("another-uuid").setName("default-group"));

when(dbClient.groupDao().selectByUuid(dbSession, GROUP_UUID))
.thenReturn(groupDto);
when(managedInstanceService.isGroupManaged(dbSession, groupDto.getUuid())).thenReturn(false);

assertThat(groupService.findGroupByUuid(dbSession, GROUP_UUID)).contains(groupDto);
GroupInformation expected = new GroupInformation(groupDto, false, false);
assertThat(groupService.findGroupByUuid(dbSession, GROUP_UUID)).contains(expected);
}

@Test
@@ -201,8 +219,10 @@ public class GroupServiceTest {
public void updateGroup_updatesGroupNameAndDescription() {
GroupDto group = mockGroupDto();
GroupDto groupWithUpdatedName = mockGroupDto();
GroupDto groupWithUpdatedDescription = mockGroupDto();
mockDefaultGroup();
when(dbClient.groupDao().update(dbSession, group)).thenReturn(groupWithUpdatedName);
when(dbClient.groupDao().update(dbSession, groupWithUpdatedName)).thenReturn(groupWithUpdatedDescription);

groupService.updateGroup(dbSession, group, "new-name", "New Description");
verify(group).setName("new-name");
@@ -216,6 +236,7 @@ public class GroupServiceTest {
GroupDto group = mockGroupDto();
mockDefaultGroup();

when(dbClient.groupDao().update(dbSession, group)).thenReturn(group);
groupService.updateGroup(dbSession, group, "new-name");
verify(group).setName("new-name");
verify(dbClient.groupDao()).update(dbSession, group);
@@ -240,7 +261,9 @@ public class GroupServiceTest {
GroupDto group = mockGroupDto();
mockDefaultGroup();

when(dbClient.groupDao().update(dbSession, group)).thenReturn(group);
groupService.updateGroup(dbSession, group, group.getName(), "New Description");

verify(group).setDescription("New Description");
verify(dbClient.groupDao()).update(dbSession, group);
}
@@ -293,14 +316,17 @@ public class GroupServiceTest {
public void createGroup_whenNameAndDescriptionIsProvided_createsGroup() {

when(uuidFactory.create()).thenReturn("1234");
GroupDto createdGroup = mockGroupDto();
when(dbClient.groupDao().insert(eq(dbSession), any())).thenReturn(createdGroup);
mockDefaultGroup();
groupService.createGroup(dbSession, "Name", "Description");

ArgumentCaptor<GroupDto> groupCaptor = ArgumentCaptor.forClass(GroupDto.class);
verify(dbClient.groupDao()).insert(eq(dbSession), groupCaptor.capture());
GroupDto createdGroup = groupCaptor.getValue();
assertThat(createdGroup.getName()).isEqualTo("Name");
assertThat(createdGroup.getDescription()).isEqualTo("Description");
assertThat(createdGroup.getUuid()).isEqualTo("1234");
GroupDto groupToCreate = groupCaptor.getValue();
assertThat(groupToCreate.getName()).isEqualTo("Name");
assertThat(groupToCreate.getDescription()).isEqualTo("Description");
assertThat(groupToCreate.getUuid()).isEqualTo("1234");
}

@Test
@@ -347,8 +373,7 @@ public class GroupServiceTest {
Map<String, Boolean> groupUuidToManaged = Map.of(
groupDto1.getUuid(), false,
groupDto2.getUuid(), true,
defaultGroup.getUuid(), false
);
defaultGroup.getUuid(), false);
when(managedInstanceService.getGroupUuidToManaged(dbSession, groupUuidToManaged.keySet())).thenReturn(groupUuidToManaged);

when(dbClient.groupDao().countByQuery(eq(dbSession), any())).thenReturn(300);

+ 37
- 28
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/group/controller/DefaultGroupController.java View File

@@ -22,7 +22,6 @@ package org.sonar.server.v2.api.group.controller;
import java.util.List;
import org.sonar.db.DbClient;
import org.sonar.db.DbSession;
import org.sonar.db.user.GroupDto;
import org.sonar.server.common.SearchResults;
import org.sonar.server.common.group.service.GroupInformation;
import org.sonar.server.common.group.service.GroupSearchRequest;
@@ -30,10 +29,11 @@ import org.sonar.server.common.group.service.GroupService;
import org.sonar.server.common.management.ManagedInstanceChecker;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.user.UserSession;
import org.sonar.server.v2.api.group.request.GroupCreateRestRequest;
import org.sonar.server.v2.api.group.request.GroupUpdateRestRequest;
import org.sonar.server.v2.api.group.request.GroupsSearchRestRequest;
import org.sonar.server.v2.api.group.response.GroupsSearchRestResponse;
import org.sonar.server.v2.api.group.response.RestGroupResponse;
import org.sonar.server.v2.api.group.response.GroupRestResponse;
import org.sonar.server.v2.api.model.RestPage;
import org.sonar.server.v2.api.response.PageRestResponse;

@@ -58,63 +58,72 @@ public class DefaultGroupController implements GroupController {
try (DbSession dbSession = dbClient.openSession(false)) {
GroupSearchRequest groupSearchRequest = new GroupSearchRequest(groupsSearchRestRequest.q(), groupsSearchRestRequest.managed(), restPage.pageIndex(), restPage.pageSize());
SearchResults<GroupInformation> searchResults = groupService.search(dbSession, groupSearchRequest);
List<RestGroupResponse> restGroupResponses = toRestGroupResponses(searchResults);
return new GroupsSearchRestResponse(restGroupResponses, new PageRestResponse(restPage.pageIndex(), restPage.pageSize(), searchResults.total()));
List<GroupRestResponse> groupRestResponses = toGroupRestResponses(searchResults);
return new GroupsSearchRestResponse(groupRestResponses, new PageRestResponse(restPage.pageIndex(), restPage.pageSize(), searchResults.total()));
}
}

private static List<RestGroupResponse> toRestGroupResponses(SearchResults<GroupInformation> searchResults) {
private static List<GroupRestResponse> toGroupRestResponses(SearchResults<GroupInformation> searchResults) {
return searchResults.searchResults().stream()
.map(groupInformation -> toRestGroup(groupInformation.groupDto()))
.map(DefaultGroupController::toRestGroup)
.toList();
}

@Override
public RestGroupResponse fetchGroup(String id) {
public GroupRestResponse fetchGroup(String id) {
userSession.checkLoggedIn().checkIsSystemAdministrator();
try (DbSession session = dbClient.openSession(false)) {
GroupDto groupDto = findGroupDtoOrThrow(id, session);
return toRestGroup(groupDto);
GroupInformation groupInformation = findGroupInformationOrThrow(id, session);
return toRestGroup(groupInformation);
}
}

@Override
public void deleteGroup(String id) {
throwIfNotAllowedToChangeGroupName();
throwIfNotAllowedToModifyGroups();
try (DbSession session = dbClient.openSession(false)) {
GroupDto group = findGroupDtoOrThrow(id, session);
groupService.delete(session, group);
GroupInformation group = findGroupInformationOrThrow(id, session);
groupService.delete(session, group.groupDto());
session.commit();
}
}

@Override
public RestGroupResponse updateGroup(String id, GroupUpdateRestRequest updateRequest) {
throwIfNotAllowedToChangeGroupName();
public GroupRestResponse updateGroup(String id, GroupUpdateRestRequest updateRequest) {
throwIfNotAllowedToModifyGroups();
try (DbSession session = dbClient.openSession(false)) {
GroupDto group = findGroupDtoOrThrow(id, session);
GroupDto updatedGroup = groupService.updateGroup(
GroupInformation group = findGroupInformationOrThrow(id, session);
GroupInformation updatedGroup = groupService.updateGroup(
session,
group,
updateRequest.getName().orElse(group.getName()),
updateRequest.getDescription().orElse(group.getDescription())
);
group.groupDto(),
updateRequest.getName().orElse(group.groupDto().getName()),
updateRequest.getDescription().orElse(group.groupDto().getDescription()));
session.commit();
return toRestGroup(updatedGroup);
}
}

private void throwIfNotAllowedToChangeGroupName() {
userSession.checkIsSystemAdministrator();
managedInstanceChecker.throwIfInstanceIsManaged();
}

private GroupDto findGroupDtoOrThrow(String id, DbSession session) {
private GroupInformation findGroupInformationOrThrow(String id, DbSession session) {
return groupService.findGroupByUuid(session, id)
.orElseThrow(() -> new NotFoundException(String.format(GROUP_NOT_FOUND_MESSAGE, id)));
}

private static RestGroupResponse toRestGroup(GroupDto groupDto) {
return new RestGroupResponse(groupDto.getUuid(), groupDto.getName(), groupDto.getDescription());
@Override
public GroupRestResponse create(GroupCreateRestRequest request) {
throwIfNotAllowedToModifyGroups();
try (DbSession session = dbClient.openSession(false)) {
GroupInformation createdGroup = groupService.createGroup(session, request.name(), request.description());
session.commit();
return toRestGroup(createdGroup);
}
}

private void throwIfNotAllowedToModifyGroups() {
userSession.checkIsSystemAdministrator();
managedInstanceChecker.throwIfInstanceIsManaged();
}

private static GroupRestResponse toRestGroup(GroupInformation group) {
return new GroupRestResponse(group.groupDto().getUuid(), group.groupDto().getName(), group.groupDto().getDescription(), group.isManaged(), group.isDefault());
}
}

+ 10
- 3
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/group/controller/GroupController.java View File

@@ -23,10 +23,11 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import javax.validation.Valid;
import org.sonar.server.v2.api.group.request.GroupCreateRestRequest;
import org.sonar.server.v2.api.group.request.GroupUpdateRestRequest;
import org.sonar.server.v2.api.group.request.GroupsSearchRestRequest;
import org.sonar.server.v2.api.group.response.GroupsSearchRestResponse;
import org.sonar.server.v2.api.group.response.RestGroupResponse;
import org.sonar.server.v2.api.group.response.GroupRestResponse;
import org.sonar.server.v2.api.model.RestPage;
import org.springdoc.api.annotations.ParameterObject;
import org.springframework.http.HttpStatus;
@@ -35,6 +36,7 @@ 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;
@@ -60,7 +62,12 @@ public interface GroupController {
@GetMapping(path = "/{id}")
@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Fetch a single group", description = "Fetch a single group.")
RestGroupResponse fetchGroup(@PathVariable("id") @Parameter(description = "The id of the group to fetch.", required = true, in = ParameterIn.PATH) String id);
GroupRestResponse fetchGroup(@PathVariable("id") @Parameter(description = "The id of the group to fetch.", required = true, in = ParameterIn.PATH) String id);

@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseStatus(HttpStatus.CREATED)
@Operation(summary = "Create a new group", description = "Create a new group.")
GroupRestResponse create(@Valid @RequestBody GroupCreateRestRequest request);

@DeleteMapping(path = "/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
@@ -73,5 +80,5 @@ public interface GroupController {
Update a user.
Allows updating user's name, email and SCM accounts.
""")
RestGroupResponse updateGroup(@PathVariable("id") String id, @Valid @RequestBody GroupUpdateRestRequest updateRequest);
GroupRestResponse updateGroup(@PathVariable("id") String id, @Valid @RequestBody GroupUpdateRestRequest updateRequest);
}

+ 41
- 0
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/group/request/GroupCreateRestRequest.java View File

@@ -0,0 +1,41 @@
/*
* 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.group.request;

import io.swagger.v3.oas.annotations.media.Schema;
import javax.annotation.Nullable;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.Size;

import static org.sonar.api.user.UserGroupValidation.GROUP_NAME_MAX_LENGTH;

public record GroupCreateRestRequest(

@NotEmpty
@Size(min = 1, max = GROUP_NAME_MAX_LENGTH)
@Schema(description = "Name for the new group. Must be unique. The value 'anyone' is reserved and cannot be used.")
String name,

@Nullable
@Size(max = 200)
@Schema(description = "Description for the new group.")
String description

) {}

+ 1
- 1
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/group/request/GroupUpdateRestRequest.java View File

@@ -40,7 +40,7 @@ public class GroupUpdateRestRequest {
}

@Size(max = 200)
@Schema(implementation = String.class, description = "Description of the gorup")
@Schema(implementation = String.class, description = "Description of the group")
public UpdateField<String> getDescription() {
return description;
}

server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/group/response/RestGroupResponse.java → server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/group/response/GroupRestResponse.java View File

@@ -19,13 +19,19 @@
*/
package org.sonar.server.v2.api.group.response;

import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import javax.annotation.Nullable;

public record RestGroupResponse(
public record GroupRestResponse(
@Schema(accessMode = Schema.AccessMode.READ_ONLY)
String id,
String name,
@Nullable
String description) {
String description,
@Schema(accessMode = Schema.AccessMode.READ_ONLY)
Boolean managed,
@Schema(accessMode = Schema.AccessMode.READ_ONLY)
@JsonProperty("default")
Boolean isDefault) {
}

+ 1
- 1
server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/group/response/GroupsSearchRestResponse.java View File

@@ -22,5 +22,5 @@ package org.sonar.server.v2.api.group.response;
import java.util.List;
import org.sonar.server.v2.api.response.PageRestResponse;

public record GroupsSearchRestResponse(List<RestGroupResponse> groups, PageRestResponse page) {
public record GroupsSearchRestResponse(List<GroupRestResponse> groups, PageRestResponse page) {
}

+ 181
- 50
server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/group/controller/DefaultGroupControllerTest.java View File

@@ -19,8 +19,7 @@
*/
package org.sonar.server.v2.api.group.controller;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -45,7 +44,8 @@ import org.sonar.server.exceptions.BadRequestException;
import org.sonar.server.tester.UserSessionRule;
import org.sonar.server.v2.api.ControllerTester;
import org.sonar.server.v2.api.group.response.GroupsSearchRestResponse;
import org.sonar.server.v2.api.group.response.RestGroupResponse;
import org.sonar.server.v2.api.group.response.GroupRestResponse;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;

@@ -63,6 +63,7 @@ import static org.sonar.server.v2.api.model.RestPage.DEFAULT_PAGE_SIZE;
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;

@@ -70,7 +71,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
public class DefaultGroupControllerTest {

private static final String GROUP_UUID = "1234";
private static final Gson GSON = new GsonBuilder().create();
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
@Rule
public UserSessionRule userSession = UserSessionRule.standalone();

@@ -78,7 +79,8 @@ public class DefaultGroupControllerTest {
private final DbClient dbClient = mock();
private final DbSession dbSession = mock();
private final ManagedInstanceChecker managedInstanceChecker = mock();
private final MockMvc mockMvc = ControllerTester.getMockMvc(new DefaultGroupController(groupService, dbClient, managedInstanceChecker, userSession));
private final MockMvc mockMvc = ControllerTester
.getMockMvc(new DefaultGroupController(groupService, dbClient, managedInstanceChecker, userSession));

@Before
public void setUp() {
@@ -89,8 +91,9 @@ public class DefaultGroupControllerTest {
public void fetchGroup_whenGroupExists_returnsTheGroup() throws Exception {

GroupDto groupDto = new GroupDto().setUuid(GROUP_UUID).setName("name").setDescription("description");
GroupInformation groupInformation = new GroupInformation(groupDto, false, false);

when(groupService.findGroupByUuid(dbSession, GROUP_UUID)).thenReturn(Optional.of(groupDto));
when(groupService.findGroupByUuid(dbSession, GROUP_UUID)).thenReturn(Optional.of(groupInformation));

userSession.logIn().setSystemAdministrator();
mockMvc.perform(get(GROUPS_ENDPOINT + "/" + GROUP_UUID))
@@ -100,7 +103,32 @@ public class DefaultGroupControllerTest {
{
"id": "1234",
"name": "name",
"description": "description"
"description": "description",
"managed": false,
"default": false
}
"""));
}

@Test
public void fetchGroup_whenGroupIsManagedOrDefault_returnsCorrectValues() throws Exception {

GroupDto groupDto = new GroupDto().setUuid(GROUP_UUID).setName("name").setDescription("description");
GroupInformation groupInformation = new GroupInformation(groupDto, true, true);

when(groupService.findGroupByUuid(dbSession, GROUP_UUID)).thenReturn(Optional.of(groupInformation));

userSession.logIn().setSystemAdministrator();
mockMvc.perform(get(GROUPS_ENDPOINT + "/" + GROUP_UUID))
.andExpectAll(
status().isOk(),
content().json("""
{
"id": "1234",
"name": "name",
"description": "description",
"managed": true,
"default": true
}
"""));
}
@@ -109,7 +137,7 @@ public class DefaultGroupControllerTest {
public void fetchGroup_whenCallerIsNotAdmin_shouldReturnForbidden() throws Exception {
userSession.logIn().setNonSystemAdministrator();
mockMvc.perform(
get(GROUPS_ENDPOINT + "/" + GROUP_UUID))
get(GROUPS_ENDPOINT + "/" + GROUP_UUID))
.andExpectAll(
status().isForbidden(),
content().json("{\"message\":\"Insufficient privileges\"}"));
@@ -120,7 +148,7 @@ public class DefaultGroupControllerTest {
userSession.logIn().setSystemAdministrator();
when(groupService.findGroupByUuid(dbSession, GROUP_UUID)).thenReturn(Optional.empty());
mockMvc.perform(
get(GROUPS_ENDPOINT + "/" + GROUP_UUID).content("{}"))
get(GROUPS_ENDPOINT + "/" + GROUP_UUID).content("{}"))
.andExpectAll(
status().isNotFound(),
content().json("{\"message\":\"Group '1234' not found\"}"));
@@ -130,7 +158,7 @@ public class DefaultGroupControllerTest {
public void deleteGroup_whenCallerIsNotAdmin_shouldReturnForbidden() throws Exception {
userSession.logIn().setNonSystemAdministrator();
mockMvc.perform(
delete(GROUPS_ENDPOINT + "/" + GROUP_UUID))
delete(GROUPS_ENDPOINT + "/" + GROUP_UUID))
.andExpectAll(
status().isForbidden(),
content().json("{\"message\":\"Insufficient privileges\"}"));
@@ -141,7 +169,7 @@ public class DefaultGroupControllerTest {
userSession.logIn().setSystemAdministrator();
doThrow(BadRequestException.create("the instance is managed")).when(managedInstanceChecker).throwIfInstanceIsManaged();
mockMvc.perform(
delete(GROUPS_ENDPOINT + "/" + GROUP_UUID))
delete(GROUPS_ENDPOINT + "/" + GROUP_UUID))
.andExpectAll(
status().isBadRequest(),
content().json("{\"message\":\"the instance is managed\"}"));
@@ -152,7 +180,7 @@ public class DefaultGroupControllerTest {
userSession.logIn().setSystemAdministrator();
when(groupService.findGroupByUuid(dbSession, GROUP_UUID)).thenReturn(Optional.empty());
mockMvc.perform(
delete(GROUPS_ENDPOINT + "/" + GROUP_UUID).content("{}"))
delete(GROUPS_ENDPOINT + "/" + GROUP_UUID).content("{}"))
.andExpectAll(
status().isNotFound(),
content().json("{\"message\":\"Group '1234' not found\"}"));
@@ -161,12 +189,13 @@ public class DefaultGroupControllerTest {
@Test
public void deleteGroup_whenGroupExists_shouldDeleteAndReturn204() throws Exception {
GroupDto groupDto = new GroupDto().setUuid(GROUP_UUID).setName("name").setDescription("description");
GroupInformation groupInformation = new GroupInformation(groupDto, false, false);

when(groupService.findGroupByUuid(dbSession, GROUP_UUID)).thenReturn(Optional.of(groupDto));
when(groupService.findGroupByUuid(dbSession, GROUP_UUID)).thenReturn(Optional.of(groupInformation));

userSession.logIn().setSystemAdministrator();
mockMvc.perform(
delete(GROUPS_ENDPOINT + "/" + GROUP_UUID))
delete(GROUPS_ENDPOINT + "/" + GROUP_UUID))
.andExpectAll(
status().isNoContent(),
content().string(""));
@@ -176,8 +205,7 @@ public class DefaultGroupControllerTest {
public void patchGroup_whenCallerIsNotAdmin_shouldReturnForbidden() throws Exception {
userSession.logIn().setNonSystemAdministrator();
mockMvc.perform(
patch(GROUPS_ENDPOINT + "/" + GROUP_UUID).contentType(JSON_MERGE_PATCH_CONTENT_TYPE).content("{}")
)
patch(GROUPS_ENDPOINT + "/" + GROUP_UUID).contentType(JSON_MERGE_PATCH_CONTENT_TYPE).content("{}"))
.andExpectAll(
status().isForbidden(),
content().json("{\"message\":\"Insufficient privileges\"}"));
@@ -188,8 +216,7 @@ public class DefaultGroupControllerTest {
userSession.logIn().setSystemAdministrator();
doThrow(BadRequestException.create("the instance is managed")).when(managedInstanceChecker).throwIfInstanceIsManaged();
mockMvc.perform(
patch(GROUPS_ENDPOINT + "/" + GROUP_UUID).contentType(JSON_MERGE_PATCH_CONTENT_TYPE).content("{}")
)
patch(GROUPS_ENDPOINT + "/" + GROUP_UUID).contentType(JSON_MERGE_PATCH_CONTENT_TYPE).content("{}"))
.andExpectAll(
status().isBadRequest(),
content().json("{\"message\":\"the instance is managed\"}"));
@@ -200,8 +227,7 @@ public class DefaultGroupControllerTest {
userSession.logIn().setSystemAdministrator();
when(groupService.findGroupByUuid(dbSession, GROUP_UUID)).thenReturn(Optional.empty());
mockMvc.perform(
patch(GROUPS_ENDPOINT + "/" + GROUP_UUID).contentType(JSON_MERGE_PATCH_CONTENT_TYPE).content("{}")
)
patch(GROUPS_ENDPOINT + "/" + GROUP_UUID).contentType(JSON_MERGE_PATCH_CONTENT_TYPE).content("{}"))
.andExpectAll(
status().isNotFound(),
content().json("{\"message\":\"Group '1234' not found\"}"));
@@ -223,38 +249,142 @@ public class DefaultGroupControllerTest {
patchGroupAndAssertResponse("newName", "newDescription");
}

private void patchGroupAndAssertResponse(@Nullable String newName,@Nullable String newDescription) throws Exception {
private void patchGroupAndAssertResponse(@Nullable String newName, @Nullable String newDescription) throws Exception {
userSession.logIn().setSystemAdministrator();
GroupDto groupDto = new GroupDto().setUuid(GROUP_UUID).setName("name").setDescription("description");
when(groupService.findGroupByUuid(dbSession, GROUP_UUID)).thenReturn(Optional.of(groupDto));
GroupInformation groupInformation = new GroupInformation(groupDto, false, false);

GroupDto newDto = new GroupDto().setUuid(GROUP_UUID).setName(newName).setDescription(newDescription);
when(groupService.updateGroup(dbSession, groupDto, newName, newDescription)).thenReturn(newDto);
GroupInformation newGroupInformation = new GroupInformation(newDto, false, false);

when(groupService.findGroupByUuid(dbSession, GROUP_UUID))
.thenReturn(Optional.of(groupInformation));
when(groupService.updateGroup(dbSession, groupDto, newName, newDescription)).thenReturn(newGroupInformation);

MvcResult mvcResult = mockMvc.perform(
patch(GROUPS_ENDPOINT + "/" + GROUP_UUID).contentType(JSON_MERGE_PATCH_CONTENT_TYPE).content(
"""
{
"name": "%s",
"description": %s
}
""".formatted(newName, newDescription == null ? "null" : "\"" + newDescription + "\"")
)
)
patch(GROUPS_ENDPOINT + "/" + GROUP_UUID).contentType(JSON_MERGE_PATCH_CONTENT_TYPE).content(
"""
{
"name": "%s",
"description": %s
}
""".formatted(newName, newDescription == null ? "null" : "\"" + newDescription + "\"")))
.andExpect(status().isOk())
.andReturn();

RestGroupResponse restGroupResponse = GSON.fromJson(mvcResult.getResponse().getContentAsString(), RestGroupResponse.class);
assertThat(restGroupResponse.id()).isEqualTo(GROUP_UUID);
assertThat(restGroupResponse.name()).isEqualTo(newName);
assertThat(restGroupResponse.description()).isEqualTo(newDescription);
GroupRestResponse groupRestResponse = OBJECT_MAPPER.readValue(mvcResult.getResponse().getContentAsString(), GroupRestResponse.class);
assertThat(groupRestResponse.id()).isEqualTo(GROUP_UUID);
assertThat(groupRestResponse.name()).isEqualTo(newName);
assertThat(groupRestResponse.description()).isEqualTo(newDescription);
}

@Test
public void create_whenInstanceIsManaged_shouldReturnException() throws Exception {
userSession.logIn().setSystemAdministrator();
doThrow(BadRequestException.create("the instance is managed")).when(managedInstanceChecker).throwIfInstanceIsManaged();
mockMvc.perform(
post(GROUPS_ENDPOINT)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content("""
{
"name": "name",
"description": "description"
}
"""))
.andExpectAll(
status().isBadRequest(),
content().json("{\"message\":\"the instance is managed\"}"));
}

@Test
public void create_whenCallersIsNotAdmin_shouldReturnForbidden() throws Exception {
userSession.logIn().setNonSystemAdministrator();
mockMvc.perform(
post(GROUPS_ENDPOINT)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content("""
{
"name": "name",
"description": "description"
}
"""))
.andExpectAll(
status().isForbidden(),
content().json("{\"message\":\"Insufficient privileges\"}"));
}

@Test
public void create_whenUserIsAnAdmin_shouldReturnCreatedGroup() throws Exception {
userSession.logIn().setSystemAdministrator();

GroupDto groupDto = new GroupDto().setUuid(GROUP_UUID).setName("name").setDescription("description");
GroupInformation groupInformation = new GroupInformation(groupDto, false, false);

when(groupService.createGroup(dbSession, "name", "description")).thenReturn(groupInformation);

mockMvc.perform(
post(GROUPS_ENDPOINT)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content("""
{
"name": "name",
"description": "description"
}
"""))
.andExpectAll(
status().isCreated(),
content().json("""
{
"id": "1234",
"name": "name",
"description": "description",
"managed": false,
"default": false
}
"""));
}

@Test
public void create_whenNameIsTooLong_returnBadRequest() throws Exception {
userSession.logIn().setSystemAdministrator();

String tooLongName = "a".repeat(501);
mockMvc.perform(
post(GROUPS_ENDPOINT)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content("""
{
"name": "%s",
"description": "description"
}
""".formatted(tooLongName)))
.andExpectAll(
status().isBadRequest());
}

@Test
public void create_whenDescriptionIsTooLong_returnBadRequest() throws Exception {
userSession.logIn().setSystemAdministrator();

String tooLongDescription = "a".repeat(201);
mockMvc.perform(
post(GROUPS_ENDPOINT)
.contentType(MediaType.APPLICATION_JSON_VALUE)
.content("""
{
"name": "name",
"description": "%s"
}
""".formatted(tooLongDescription)))
.andExpectAll(
status().isBadRequest());
}

@Test
public void search_whenCallerIsNotAdmin_shouldReturnForbidden() throws Exception {
userSession.logIn().setNonSystemAdministrator();
mockMvc.perform(
get(GROUPS_ENDPOINT + "/" + GROUP_UUID)
)
get(GROUPS_ENDPOINT + "/" + GROUP_UUID))
.andExpectAll(
status().isForbidden(),
content().json("{\"message\":\"Insufficient privileges\"}"));
@@ -281,10 +411,10 @@ public class DefaultGroupControllerTest {
when(groupService.search(eq(dbSession), any())).thenReturn(new SearchResults<>(List.of(), 0));

mockMvc.perform(get(GROUPS_ENDPOINT)
.param("managed", "true")
.param("q", "q")
.param("pageSize", "100")
.param("pageIndex", "2"))
.param("managed", "true")
.param("q", "q")
.param("pageSize", "100")
.param("pageIndex", "2"))
.andExpect(status().isOk());

ArgumentCaptor<GroupSearchRequest> requestCaptor = ArgumentCaptor.forClass(GroupSearchRequest.class);
@@ -310,9 +440,9 @@ public class DefaultGroupControllerTest {
.andExpect(status().isOk())
.andReturn();

GroupsSearchRestResponse actualGroupsSearchRestResponse = GSON.fromJson(mvcResult.getResponse().getContentAsString(), GroupsSearchRestResponse.class);
Map<String, RestGroupResponse> groupIdToGroupResponse = actualGroupsSearchRestResponse.groups().stream()
.collect(Collectors.toMap(RestGroupResponse::id, Function.identity()));
GroupsSearchRestResponse actualGroupsSearchRestResponse = OBJECT_MAPPER.readValue(mvcResult.getResponse().getContentAsString(), GroupsSearchRestResponse.class);
Map<String, GroupRestResponse> groupIdToGroupResponse = actualGroupsSearchRestResponse.groups().stream()
.collect(Collectors.toMap(GroupRestResponse::id, Function.identity()));

assertResponseContains(groupIdToGroupResponse, group1);
assertResponseContains(groupIdToGroupResponse, group2);
@@ -324,19 +454,20 @@ public class DefaultGroupControllerTest {

}

private void assertResponseContains(Map<String, RestGroupResponse> groupIdToGroupResponse, GroupInformation expectedGroup) {
RestGroupResponse restGroup = groupIdToGroupResponse.get(expectedGroup.groupDto().getUuid());
private void assertResponseContains(Map<String, GroupRestResponse> groupIdToGroupResponse, GroupInformation expectedGroup) {
GroupRestResponse restGroup = groupIdToGroupResponse.get(expectedGroup.groupDto().getUuid());
assertThat(restGroup).isNotNull();
assertThat(restGroup.name()).isEqualTo(expectedGroup.groupDto().getName());
assertThat(restGroup.description()).isEqualTo(expectedGroup.groupDto().getDescription());
//TODO add assertions for managed & default flag
assertThat(restGroup.managed()).isEqualTo(expectedGroup.isManaged());
assertThat(restGroup.isDefault()).isEqualTo(expectedGroup.isDefault());
}

private GroupInformation generateGroupSearchResult(String id, boolean managed, boolean isDefault) {
GroupDto groupDto = new GroupDto()
.setUuid(id)
.setName("name_"+id)
.setDescription("description_"+id);
.setName("name_" + id)
.setDescription("description_" + id);
return new GroupInformation(groupDto, managed, isDefault);
}
}

+ 5
- 0
server/sonar-webserver-webapi/src/it/java/org/sonar/server/usergroups/ws/CreateActionIT.java View File

@@ -20,6 +20,7 @@
package org.sonar.server.usergroups.ws;

import org.apache.commons.lang.StringUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.server.ws.Change;
@@ -59,6 +60,10 @@ public class CreateActionIT {
private final CreateAction underTest = new CreateAction(db.getDbClient(), userSession, groupService, managedInstanceChecker);
private final WsActionTester tester = new WsActionTester(underTest);

@Before
public void setUp() {
db.users().insertDefaultGroup();
}
@Test
public void define_create_action() {
WebService.Action action = tester.getDef();

+ 7
- 2
server/sonar-webserver-webapi/src/it/java/org/sonar/server/usergroups/ws/ExternalGroupServiceIT.java View File

@@ -20,6 +20,7 @@
package org.sonar.server.usergroups.ws;

import java.util.Optional;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.sonar.api.utils.System2;
@@ -54,6 +55,11 @@ public class ExternalGroupServiceIT {

private final ExternalGroupService externalGroupService = new ExternalGroupService(dbClient, groupService);

@Before
public void setUp() {
dbTester.users().insertDefaultGroup();
}

@Test
public void createOrUpdateExternalGroup_whenNewGroup_shouldCreateIt() {
externalGroupService.createOrUpdateExternalGroup(dbSession, new GroupRegistration(EXTERNAL_ID, EXTERNAL_IDENTITY_PROVIDER, GROUP_NAME));
@@ -67,13 +73,12 @@ public class ExternalGroupServiceIT {

externalGroupService.createOrUpdateExternalGroup(dbSession, new GroupRegistration(EXTERNAL_ID, EXTERNAL_IDENTITY_PROVIDER, GROUP_NAME));

assertThat(dbTester.users().countAllGroups()).isEqualTo(1);
assertThat(dbTester.users().countAllGroups()).isEqualTo(2);
assertGroupAndExternalGroup();
}

@Test
public void createOrUpdateExternalGroup_whenExistingExternalGroup_shouldUpdate() {
dbTester.users().insertDefaultGroup();
GroupDto existingGroupDto = dbTester.users().insertGroup(GROUP_NAME);
dbTester.users().insertExternalGroup(new ExternalGroupDto(existingGroupDto.getUuid(), EXTERNAL_ID, EXTERNAL_IDENTITY_PROVIDER));


+ 1
- 1
server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/CreateAction.java View File

@@ -88,7 +88,7 @@ public class CreateAction implements UserGroupsWsAction {
managedInstanceChecker.throwIfInstanceIsManaged();
String groupName = request.mandatoryParam(PARAM_GROUP_NAME);
String groupDescription = request.param(PARAM_GROUP_DESCRIPTION);
GroupDto group = groupService.createGroup(dbSession, groupName, groupDescription);
GroupDto group = groupService.createGroup(dbSession, groupName, groupDescription).groupDto();
dbSession.commit();
writeResponse(request, response, group);
}

+ 1
- 1
server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/ExternalGroupService.java View File

@@ -72,7 +72,7 @@ public class ExternalGroupService {
return groupDto.get();
} else {
LOG.debug("Creating new group {}", groupRegistration.name());
return groupService.createGroup(dbSession, groupRegistration.name(), null);
return groupService.createGroup(dbSession, groupRegistration.name(), null).groupDto();
}
}


+ 2
- 2
server/sonar-webserver-webapi/src/main/java/org/sonar/server/usergroups/ws/UpdateAction.java View File

@@ -29,8 +29,8 @@ import org.sonar.db.DbSession;
import org.sonar.db.user.GroupDto;
import org.sonar.db.user.UserMembershipQuery;
import org.sonar.server.common.group.service.GroupService;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.common.management.ManagedInstanceChecker;
import org.sonar.server.exceptions.NotFoundException;
import org.sonar.server.user.UserSession;
import org.sonarqube.ws.UserGroups;

@@ -107,7 +107,7 @@ public class UpdateAction implements UserGroupsWsAction {
String newName = request.param(PARAM_GROUP_NAME);
String description = request.param(PARAM_GROUP_DESCRIPTION);

GroupDto updatedGroup = groupService.updateGroup(dbSession, group, newName, description);
GroupDto updatedGroup = groupService.updateGroup(dbSession, group, newName, description).groupDto();
dbSession.commit();

writeResponse(dbSession, request, response, updatedGroup);

Loading…
Cancel
Save