diff options
author | Wojtek Wajerowicz <115081248+wojciech-wajerowicz-sonarsource@users.noreply.github.com> | 2023-09-04 13:55:21 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2023-09-04 15:48:18 +0000 |
commit | a2e9af8f2ed602ccc96eebe74a007c4b8a4e8ca2 (patch) | |
tree | ad7893ae0cc7017e7511e2d33221ae60c2f5d295 /server/sonar-webserver-webapi-v2 | |
parent | a506cb857145802319545ff8ca4d4e5258fc8f67 (diff) | |
download | sonarqube-a2e9af8f2ed602ccc96eebe74a007c4b8a4e8ca2.tar.gz sonarqube-a2e9af8f2ed602ccc96eebe74a007c4b8a4e8ca2.zip |
SONAR-20285 PATCH endpoint to update users
Co-authored-by: Aurelien Poscia <aurelien.poscia@sonarsource.com>
Diffstat (limited to 'server/sonar-webserver-webapi-v2')
15 files changed, 454 insertions, 104 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 87ef45e672c..665f7e2eff7 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 @@ -24,6 +24,7 @@ public class WebApiEndpoints { public static final String LIVENESS_ENDPOINT = SYSTEM_ENDPOINTS + "/liveness"; public static final String HEALTH_ENDPOINT = SYSTEM_ENDPOINTS + "/health"; public static final String USER_ENDPOINT = "/users"; + public static final String JSON_MERGE_PATCH_CONTENT_TYPE = "application/json-merge-patch+json"; private WebApiEndpoints() { } diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/controller/DefaultUserController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/controller/DefaultUserController.java index 7a100dfdb77..d29515e9047 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/controller/DefaultUserController.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/controller/DefaultUserController.java @@ -24,16 +24,18 @@ import javax.annotation.Nullable; import org.sonar.server.common.PaginationInformation; import org.sonar.server.common.SearchResults; import org.sonar.server.common.user.service.UserCreateRequest; -import org.sonar.server.common.user.service.UserSearchResult; +import org.sonar.server.common.user.service.UserInformation; import org.sonar.server.common.user.service.UserService; import org.sonar.server.common.user.service.UsersSearchRequest; import org.sonar.server.exceptions.ForbiddenException; +import org.sonar.server.user.UpdateUser; import org.sonar.server.user.UserSession; import org.sonar.server.v2.api.model.RestPage; import org.sonar.server.v2.api.model.RestSortOrder; import org.sonar.server.v2.api.user.converter.UsersSearchRestResponseGenerator; import org.sonar.server.v2.api.user.model.RestUser; import org.sonar.server.v2.api.user.request.UserCreateRestRequest; +import org.sonar.server.v2.api.user.request.UserUpdateRestRequest; import org.sonar.server.v2.api.user.request.UsersSearchRestRequest; import org.sonar.server.v2.api.user.response.UsersSearchRestResponse; @@ -59,7 +61,7 @@ public class DefaultUserController implements UserController { throwIfAdminOnlyParametersAreUsed(usersSearchRestRequest); checkRequest(!RestSortOrder.DESC.equals(order), "order parameter is present for doc-demo purpose, it will be removed."); - SearchResults<UserSearchResult> userSearchResults = userService.findUsers(toUserSearchRequest(usersSearchRestRequest, page)); + SearchResults<UserInformation> userSearchResults = userService.findUsers(toUserSearchRequest(usersSearchRestRequest, page)); PaginationInformation paging = forPageIndex(page.pageIndex()).withPageSize(page.pageSize()).andTotal(userSearchResults.total()); return usersSearchResponseGenerator.toUsersForResponse(userSearchResults.searchResults(), paging); @@ -109,6 +111,22 @@ public class DefaultUserController implements UserController { } @Override + public RestUser updateUser(String login, UserUpdateRestRequest updateRequest) { + userSession.checkLoggedIn().checkIsSystemAdministrator(); + UpdateUser update = toUpdateUser(updateRequest); + UserInformation updatedUser = userService.updateUser(login, update); + return usersSearchResponseGenerator.toRestUser(updatedUser); + } + + private static UpdateUser toUpdateUser(UserUpdateRestRequest updateRequest) { + UpdateUser update = new UpdateUser(); + updateRequest.getName().applyIfDefined(update::setName); + updateRequest.getEmail().applyIfDefined(update::setEmail); + updateRequest.getScmAccounts().applyIfDefined(update::setScmAccounts); + return update; + } + + @Override public RestUser create(UserCreateRestRequest userCreateRestRequest) { userSession.checkLoggedIn().checkIsSystemAdministrator(); UserCreateRequest userCreateRequest = toUserCreateRequest(userCreateRestRequest); diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/controller/UserController.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/controller/UserController.java index 2d027f43b85..e80151fdf8c 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/controller/UserController.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/controller/UserController.java @@ -28,6 +28,7 @@ import org.sonar.server.v2.api.model.RestPage; import org.sonar.server.v2.api.model.RestSortOrder; import org.sonar.server.v2.api.user.model.RestUser; import org.sonar.server.v2.api.user.request.UserCreateRestRequest; +import org.sonar.server.v2.api.user.request.UserUpdateRestRequest; import org.sonar.server.v2.api.user.request.UsersSearchRestRequest; import org.sonar.server.v2.api.user.response.UsersSearchRestResponse; import org.springdoc.api.annotations.ParameterObject; @@ -35,6 +36,7 @@ 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; @@ -43,12 +45,14 @@ import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; +import static org.sonar.server.v2.WebApiEndpoints.JSON_MERGE_PATCH_CONTENT_TYPE; import static org.sonar.server.v2.WebApiEndpoints.USER_ENDPOINT; @RequestMapping(USER_ENDPOINT) @RestController public interface UserController { + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.OK) @Operation(summary = "Users search", description = """ @@ -72,12 +76,8 @@ public interface UserController { @ResponseStatus(HttpStatus.NO_CONTENT) @Operation(summary = "Deactivate a user", description = "Deactivates a user. Requires Administer System permission.") void deactivate( - @PathVariable("login") - @Parameter(description = "The login of the user to delete.", required = true, in = ParameterIn.PATH) - String login, - @RequestParam(value = "anonymize", required = false, defaultValue = "false") - @Parameter(description = "Anonymize user in addition to deactivating it.") - Boolean anonymize); + @PathVariable("login") @Parameter(description = "The login of the user to delete.", required = true, in = ParameterIn.PATH) String login, + @RequestParam(value = "anonymize", required = false, defaultValue = "false") @Parameter(description = "Anonymize user in addition to deactivating it.") Boolean anonymize); @GetMapping(path = "/{login}") @ResponseStatus(HttpStatus.OK) @@ -95,6 +95,14 @@ public interface UserController { """) RestUser fetchUser(@PathVariable("login") @Parameter(description = "The login of the user to fetch.", required = true, in = ParameterIn.PATH) String login); + @PatchMapping(path = "/{login}", consumes = JSON_MERGE_PATCH_CONTENT_TYPE, produces = MediaType.APPLICATION_JSON_VALUE) + @ResponseStatus(HttpStatus.OK) + @Operation(summary = "Update a user", description = """ + Update a user. + Allows updating user's name, email and SCM accounts. + """) + RestUser updateUser(@PathVariable("login") String login, @Valid @RequestBody UserUpdateRestRequest updateRequest); + @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @ResponseStatus(HttpStatus.OK) @Operation(summary = "User creation", description = """ diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGenerator.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGenerator.java index ef3c07b5702..f6ec6336fa5 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGenerator.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGenerator.java @@ -27,7 +27,7 @@ import org.sonar.api.utils.DateUtils; import org.sonar.db.user.UserDto; import org.sonar.server.common.PaginationInformation; import org.sonar.server.common.user.UsersSearchResponseGenerator; -import org.sonar.server.common.user.service.UserSearchResult; +import org.sonar.server.common.user.service.UserInformation; import org.sonar.server.user.UserSession; import org.sonar.server.v2.api.response.PageRestResponse; import org.sonar.server.v2.api.user.model.RestUser; @@ -45,20 +45,20 @@ public class UsersSearchRestResponseGenerator implements UsersSearchResponseGene } @Override - public UsersSearchRestResponse toUsersForResponse(List<UserSearchResult> userSearchResults, PaginationInformation paginationInformation) { - List<RestUser> usersForResponse = toUsersForResponse(userSearchResults); + public UsersSearchRestResponse toUsersForResponse(List<UserInformation> userInformations, PaginationInformation paginationInformation) { + List<RestUser> usersForResponse = toUsersForResponse(userInformations); PageRestResponse pageRestResponse = new PageRestResponse(paginationInformation.pageIndex(), paginationInformation.pageSize(), paginationInformation.total()); return new UsersSearchRestResponse(usersForResponse, pageRestResponse); } - private List<RestUser> toUsersForResponse(List<UserSearchResult> userSearchResults) { - return userSearchResults.stream() + private List<RestUser> toUsersForResponse(List<UserInformation> userInformations) { + return userInformations.stream() .map(this::toRestUser) .toList(); } - public RestUser toRestUser(UserSearchResult userSearchResult) { - UserDto userDto = userSearchResult.userDto(); + public RestUser toRestUser(UserInformation userInformation) { + UserDto userDto = userInformation.userDto(); String login = userDto.getLogin(); String name = userDto.getName(); @@ -66,17 +66,17 @@ public class UsersSearchRestResponseGenerator implements UsersSearchResponseGene return new RestUserForAnonymousUsers(login, login, name); } - String avatar = userSearchResult.avatar().orElse(null); + String avatar = userInformation.avatar().orElse(null); Boolean active = userDto.isActive(); Boolean local = userDto.isLocal(); String email = userDto.getEmail(); String externalIdentityProvider = userDto.getExternalIdentityProvider(); if (userSession.isSystemAdministrator() || Objects.equals(userSession.getUuid(), userDto.getUuid())) { String externalLogin = userDto.getExternalLogin(); - Boolean managed = userSearchResult.managed(); + Boolean managed = userInformation.managed(); String sqLastConnectionDate = toDateTime(userDto.getLastConnectionDate()); String slLastConnectionDate = toDateTime(userDto.getLastSonarlintConnectionDate()); - List<String> scmAccounts = userSearchResult.userDto().getSortedScmAccounts(); + List<String> scmAccounts = userInformation.userDto().getSortedScmAccounts(); return new RestUserForAdmins( login, login, diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForAdmins.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForAdmins.java index 5de5cc9bdbc..e7c4ea755cd 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForAdmins.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/model/RestUserForAdmins.java @@ -19,20 +19,25 @@ */ package org.sonar.server.v2.api.user.model; +import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; import javax.annotation.Nullable; public record RestUserForAdmins( + @Schema(accessMode = Schema.AccessMode.READ_ONLY) String id, String login, String name, @Nullable String email, @Nullable + @Schema(accessMode = Schema.AccessMode.READ_ONLY) Boolean active, @Nullable + @Schema(accessMode = Schema.AccessMode.READ_ONLY) Boolean local, @Nullable + @Schema(accessMode = Schema.AccessMode.READ_ONLY) Boolean managed, @Nullable String externalLogin, @@ -41,8 +46,10 @@ public record RestUserForAdmins( @Nullable String avatar, @Nullable + @Schema(accessMode = Schema.AccessMode.READ_ONLY) String sonarQubeLastConnectionDate, @Nullable + @Schema(accessMode = Schema.AccessMode.READ_ONLY) String sonarLintLastConnectionDate, @Nullable List<String> scmAccounts diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/request/UserCreateRestRequest.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/request/UserCreateRestRequest.java index 9b28d032c22..a7e05e6a062 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/request/UserCreateRestRequest.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/request/UserCreateRestRequest.java @@ -29,7 +29,7 @@ import javax.validation.constraints.Size; public record UserCreateRestRequest( @Nullable @Email - @Size(max = 100) + @Size(min = 1, max = 100) @Schema(description = "User email") String email, @@ -50,7 +50,7 @@ public record UserCreateRestRequest( String name, @Nullable - @Schema(description = "User password. Only mandatory when creating local user, otherwise it should not be set") + @Schema(description = "User password. Only mandatory when creating local user, otherwise it should not be set", accessMode = Schema.AccessMode.WRITE_ONLY) String password, @Nullable diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/request/UserUpdateRestRequest.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/request/UserUpdateRestRequest.java new file mode 100644 index 00000000000..5418c74664e --- /dev/null +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/user/request/UserUpdateRestRequest.java @@ -0,0 +1,64 @@ +/* + * 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.user.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.Email; +import javax.validation.constraints.Size; +import org.sonar.server.v2.common.model.UpdateField; + +public class UserUpdateRestRequest { + + private UpdateField<String> name = UpdateField.undefined(); + private UpdateField<String> email = UpdateField.undefined(); + private UpdateField<List<String>> scmAccounts = UpdateField.undefined(); + + @Size(max = 200) + @Schema(description = "User first name and last name", implementation = String.class) + public UpdateField< String> getName() { + return name; + } + + public void setName(String name) { + this.name = UpdateField.withValue(name); + } + + @Email + @Size(min = 1, max = 100) + @Schema(implementation = String.class, description = "Email") + public UpdateField<String> getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = UpdateField.withValue(email); + } + + @ArraySchema(arraySchema = @Schema(description = "List of SCM accounts."), schema = @Schema(implementation = String.class)) + public UpdateField<List<String>> getScmAccounts() { + return scmAccounts; + } + + public void setScmAccounts(List<String> scmAccounts) { + this.scmAccounts = UpdateField.withValue(scmAccounts); + } +} diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/validation/UpdateFieldValueExtractor.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/validation/UpdateFieldValueExtractor.java new file mode 100644 index 00000000000..1e7967a05f2 --- /dev/null +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/validation/UpdateFieldValueExtractor.java @@ -0,0 +1,34 @@ +/* + * 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.validation; + +import javax.validation.valueextraction.ExtractedValue; +import javax.validation.valueextraction.UnwrapByDefault; +import javax.validation.valueextraction.ValueExtractor; +import org.sonar.server.v2.common.model.UpdateField; + +@UnwrapByDefault +public class UpdateFieldValueExtractor implements ValueExtractor<UpdateField<@ExtractedValue ?>> { + + @Override + public void extractValues(UpdateField<?> originalValue, ValueReceiver receiver) { + receiver.value(null, originalValue.getValue()); + } +} diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/validation/package-info.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/validation/package-info.java new file mode 100644 index 00000000000..97a74ea79f0 --- /dev/null +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/api/validation/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.validation; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/RestResponseEntityExceptionHandler.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/RestResponseEntityExceptionHandler.java index 8bd0012b75c..dd8d133e37d 100644 --- a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/RestResponseEntityExceptionHandler.java +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/RestResponseEntityExceptionHandler.java @@ -63,7 +63,7 @@ public class RestResponseEntityExceptionHandler { String fieldName = fieldError.getField(); String rejectedValueAsString = Optional.ofNullable(fieldError.getRejectedValue()).map(Object::toString).orElse("{}"); String defaultMessage = fieldError.getDefaultMessage(); - return String.format("Value %s for field %s was rejected. Error: %s", rejectedValueAsString, fieldName, defaultMessage); + return String.format("Value %s for field %s was rejected. Error: %s.", rejectedValueAsString, fieldName, defaultMessage); } @ExceptionHandler({ServerException.class, ForbiddenException.class, UnauthorizedException.class, BadRequestException.class}) diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/model/UpdateField.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/model/UpdateField.java new file mode 100644 index 00000000000..d07b6a9e7a5 --- /dev/null +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/model/UpdateField.java @@ -0,0 +1,64 @@ +/* + * 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.common.model; + +import java.util.function.Consumer; +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import javax.validation.valueextraction.UnwrapByDefault; + +@UnwrapByDefault +public class UpdateField<T> { + private final T value; + private final boolean isDefined; + + private UpdateField(@Nullable T value, boolean isDefined) { + this.value = value; + this.isDefined = isDefined; + } + + public static <T> UpdateField<T> withValue(@Nullable T value) { + return new UpdateField<>(value, true); + } + + public static <T> UpdateField<T> undefined() { + return new UpdateField<>(null, false); + } + + @CheckForNull + public T getValue() { + return value; + } + + public boolean isDefined() { + return isDefined; + } + + public void applyIfDefined(Consumer<T> consumer) { + if (isDefined) { + consumer.accept(value); + } + } + + @Override + public String toString() { + return value.toString(); + } +} diff --git a/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/model/package-info.java b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/model/package-info.java new file mode 100644 index 00000000000..c0cc455b026 --- /dev/null +++ b/server/sonar-webserver-webapi-v2/src/main/java/org/sonar/server/v2/common/model/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.common.model; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/server/sonar-webserver-webapi-v2/src/main/resources/META-INF/services/javax.validation.valueextraction.ValueExtractor b/server/sonar-webserver-webapi-v2/src/main/resources/META-INF/services/javax.validation.valueextraction.ValueExtractor new file mode 100644 index 00000000000..8d8a32809f4 --- /dev/null +++ b/server/sonar-webserver-webapi-v2/src/main/resources/META-INF/services/javax.validation.valueextraction.ValueExtractor @@ -0,0 +1 @@ +org.sonar.server.v2.api.validation.UpdateFieldValueExtractor diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerTest.java index ed6a8bd2b55..3c6efc91b4e 100644 --- a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerTest.java +++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/controller/DefaultUserControllerTest.java @@ -33,13 +33,15 @@ import org.junit.Test; import org.mockito.ArgumentCaptor; import org.sonar.db.user.UserDto; import org.sonar.server.common.SearchResults; -import org.sonar.server.common.user.service.UserSearchResult; +import org.sonar.server.common.user.service.UserInformation; import org.sonar.server.common.user.service.UserService; import org.sonar.server.common.user.service.UsersSearchRequest; import org.sonar.server.exceptions.BadRequestException; import org.sonar.server.exceptions.NotFoundException; import org.sonar.server.tester.UserSessionRule; +import org.sonar.server.user.UpdateUser; import org.sonar.server.v2.api.ControllerTester; +import org.sonar.server.v2.api.model.RestError; import org.sonar.server.v2.api.response.PageRestResponse; import org.sonar.server.v2.api.user.converter.UsersSearchRestResponseGenerator; import org.sonar.server.v2.api.user.model.RestUser; @@ -58,11 +60,13 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import static org.sonar.api.utils.DateUtils.formatDateTime; +import static org.sonar.server.v2.WebApiEndpoints.JSON_MERGE_PATCH_CONTENT_TYPE; import static org.sonar.server.v2.WebApiEndpoints.USER_ENDPOINT; import static org.sonar.server.v2.api.model.RestPage.DEFAULT_PAGE_INDEX; 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; @@ -75,7 +79,7 @@ public class DefaultUserControllerTest { private final UsersSearchRestResponseGenerator responseGenerator = mock(UsersSearchRestResponseGenerator.class); private final MockMvc mockMvc = ControllerTester.getMockMvc(new DefaultUserController(userSession, userService, responseGenerator)); - private static final Gson gson = new GsonBuilder().registerTypeAdapter(RestUser.class, new RestUserDeserializer()).create(); + private static final Gson gson = new GsonBuilder().registerTypeAdapter(RestUser.class, new RestUserDeserializer()).create(); @Test public void search_whenNoParameters_shouldUseDefaultAndForwardToUserService() throws Exception { @@ -118,25 +122,25 @@ public class DefaultUserControllerTest { @Test public void search_whenAdminParametersUsedButNotAdmin_shouldFail() throws Exception { mockMvc.perform(get(USER_ENDPOINT) - .param("sonarQubeLastConnectionDateFrom", "2020-01-01T00:00:00+0100")) + .param("sonarQubeLastConnectionDateFrom", "2020-01-01T00:00:00+0100")) .andExpectAll( status().isForbidden(), content().string("{\"message\":\"parameter sonarQubeLastConnectionDateFrom requires Administer System permission.\"}")); mockMvc.perform(get(USER_ENDPOINT) - .param("sonarQubeLastConnectionDateTo", "2020-01-01T00:00:00+0100")) + .param("sonarQubeLastConnectionDateTo", "2020-01-01T00:00:00+0100")) .andExpectAll( status().isForbidden(), content().string("{\"message\":\"parameter sonarQubeLastConnectionDateTo requires Administer System permission.\"}")); mockMvc.perform(get(USER_ENDPOINT) - .param("sonarLintLastConnectionDateFrom", "2020-01-01T00:00:00+0100")) + .param("sonarLintLastConnectionDateFrom", "2020-01-01T00:00:00+0100")) .andExpectAll( status().isForbidden(), content().string("{\"message\":\"parameter sonarLintLastConnectionDateFrom requires Administer System permission.\"}")); mockMvc.perform(get(USER_ENDPOINT) - .param("sonarLintLastConnectionDateTo", "2020-01-01T00:00:00+0100")) + .param("sonarLintLastConnectionDateTo", "2020-01-01T00:00:00+0100")) .andExpectAll( status().isForbidden(), content().string("{\"message\":\"parameter sonarLintLastConnectionDateTo requires Administer System permission.\"}")); @@ -144,12 +148,12 @@ public class DefaultUserControllerTest { @Test public void search_whenUserServiceReturnUsers_shouldReturnThem() throws Exception { - UserSearchResult user1 = generateUserSearchResult("user1", true, true, false, 2, 3); - UserSearchResult user2 = generateUserSearchResult("user2", true, false, false, 3, 0); - UserSearchResult user3 = generateUserSearchResult("user3", true, false, true, 1, 1); - UserSearchResult user4 = generateUserSearchResult("user4", false, true, false, 0, 0); - List<UserSearchResult> users = List.of(user1, user2, user3, user4); - SearchResults<UserSearchResult> searchResult = new SearchResults<>(users, users.size()); + UserInformation user1 = generateUserSearchResult("user1", true, true, false, 2, 3); + UserInformation user2 = generateUserSearchResult("user2", true, false, false, 3, 0); + UserInformation user3 = generateUserSearchResult("user3", true, false, true, 1, 1); + UserInformation user4 = generateUserSearchResult("user4", false, true, false, 0, 0); + List<UserInformation> users = List.of(user1, user2, user3, user4); + SearchResults<UserInformation> searchResult = new SearchResults<>(users, users.size()); when(userService.findUsers(any())).thenReturn(searchResult); List<RestUser> restUserForAdmins = List.of(toRestUser(user1), toRestUser(user2), toRestUser(user3), toRestUser(user4)); when(responseGenerator.toUsersForResponse(eq(searchResult.searchResults()), any())).thenReturn(new UsersSearchRestResponse(restUserForAdmins, new PageRestResponse(1, 50, 4))); @@ -179,7 +183,7 @@ public class DefaultUserControllerTest { } } - private UserSearchResult generateUserSearchResult(String id, boolean active, boolean local, boolean managed, int groupsCount, int tokensCount) { + private UserInformation generateUserSearchResult(String id, boolean active, boolean local, boolean managed, int groupsCount, int tokensCount) { UserDto userDto = new UserDto() .setLogin("login_" + id) .setUuid("uuid_" + id) @@ -196,24 +200,24 @@ public class DefaultUserControllerTest { List<String> groups = new ArrayList<>(); IntStream.range(1, groupsCount).forEach(i -> groups.add("group" + i)); - return new UserSearchResult(userDto, managed, Optional.of("avatar_" + id), groups, tokensCount); + return new UserInformation(userDto, managed, Optional.of("avatar_" + id), groups, tokensCount); } - private RestUserForAdmins toRestUser(UserSearchResult userSearchResult) { + private RestUserForAdmins toRestUser(UserInformation userInformation) { return new RestUserForAdmins( - userSearchResult.userDto().getLogin(), - userSearchResult.userDto().getLogin(), - userSearchResult.userDto().getName(), - userSearchResult.userDto().getEmail(), - userSearchResult.userDto().isActive(), - userSearchResult.userDto().isLocal(), - userSearchResult.managed(), - userSearchResult.userDto().getExternalLogin(), - userSearchResult.userDto().getExternalIdentityProvider(), - userSearchResult.avatar().orElse(""), - formatDateTime(userSearchResult.userDto().getLastConnectionDate()), - formatDateTime(userSearchResult.userDto().getLastSonarlintConnectionDate()), - userSearchResult.userDto().getSortedScmAccounts()); + userInformation.userDto().getLogin(), + userInformation.userDto().getLogin(), + userInformation.userDto().getName(), + userInformation.userDto().getEmail(), + userInformation.userDto().isActive(), + userInformation.userDto().isLocal(), + userInformation.managed(), + userInformation.userDto().getExternalLogin(), + userInformation.userDto().getExternalIdentityProvider(), + userInformation.avatar().orElse(""), + formatDateTime(userInformation.userDto().getLastConnectionDate()), + formatDateTime(userInformation.userDto().getLastSonarlintConnectionDate()), + userInformation.userDto().getSortedScmAccounts()); } @Test @@ -310,7 +314,7 @@ public class DefaultUserControllerTest { @Test public void fetchUser_whenUserExists_shouldReturnUser() throws Exception { - UserSearchResult user = generateUserSearchResult("user1", true, true, false, 2, 3); + UserInformation user = generateUserSearchResult("user1", true, true, false, 2, 3); RestUserForAdmins restUserForAdmins = toRestUser(user); when(userService.fetchUser("userLogin")).thenReturn(user); when(responseGenerator.toRestUser(user)).thenReturn(restUserForAdmins); @@ -326,9 +330,9 @@ public class DefaultUserControllerTest { userSession.logIn().setNonSystemAdministrator(); mockMvc.perform( - post(USER_ENDPOINT) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .content(gson.toJson(new UserCreateRestRequest(null, null, "login", "name", null, null)))) + post(USER_ENDPOINT) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(gson.toJson(new UserCreateRestRequest(null, null, "login", "name", null, null)))) .andExpectAll( status().isForbidden(), content().json("{\"message\":\"Insufficient privileges\"}")); @@ -339,12 +343,12 @@ public class DefaultUserControllerTest { userSession.logIn().setSystemAdministrator(); mockMvc.perform( - post(USER_ENDPOINT) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .content(gson.toJson(new UserCreateRestRequest(null, null, null, "name", null, null)))) + post(USER_ENDPOINT) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(gson.toJson(new UserCreateRestRequest(null, null, null, "name", null, null)))) .andExpectAll( status().isBadRequest(), - content().json("{\"message\":\"Value {} for field login was rejected. Error: must not be null\"}")); + content().json("{\"message\":\"Value {} for field login was rejected. Error: must not be null.\"}")); } @Test @@ -352,12 +356,12 @@ public class DefaultUserControllerTest { userSession.logIn().setSystemAdministrator(); mockMvc.perform( - post(USER_ENDPOINT) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .content(gson.toJson(new UserCreateRestRequest(null, null, "login", null, null, null)))) + post(USER_ENDPOINT) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(gson.toJson(new UserCreateRestRequest(null, null, "login", null, null, null)))) .andExpectAll( status().isBadRequest(), - content().json("{\"message\":\"Value {} for field name was rejected. Error: must not be null\"}")); + content().json("{\"message\":\"Value {} for field name was rejected. Error: must not be null.\"}")); } @Test @@ -366,9 +370,9 @@ public class DefaultUserControllerTest { when(userService.createUser(any())).thenThrow(new IllegalArgumentException("IllegalArgumentException")); mockMvc.perform( - post(USER_ENDPOINT) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .content(gson.toJson(new UserCreateRestRequest("e@mail.com", true, "login", "name", "password", List.of("scm"))))) + post(USER_ENDPOINT) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(gson.toJson(new UserCreateRestRequest("e@mail.com", true, "login", "name", "password", List.of("scm"))))) .andExpectAll( status().isBadRequest(), content().json("{\"message\":\"IllegalArgumentException\"}")); @@ -377,20 +381,123 @@ public class DefaultUserControllerTest { @Test public void create_whenUserServiceReturnUser_shouldReturnIt() throws Exception { userSession.logIn().setSystemAdministrator(); - UserSearchResult userSearchResult = generateUserSearchResult("1", true, true, false, 1, 2); - UserDto userDto = userSearchResult.userDto(); - when(userService.createUser(any())).thenReturn(userSearchResult); - when(responseGenerator.toRestUser(userSearchResult)).thenReturn(toRestUser(userSearchResult)); + UserInformation userInformation = generateUserSearchResult("1", true, true, false, 1, 2); + UserDto userDto = userInformation.userDto(); + when(userService.createUser(any())).thenReturn(userInformation); + when(responseGenerator.toRestUser(userInformation)).thenReturn(toRestUser(userInformation)); MvcResult mvcResult = mockMvc.perform( - post(USER_ENDPOINT) - .contentType(MediaType.APPLICATION_JSON_VALUE) - .content(gson.toJson(new UserCreateRestRequest( - userDto.getEmail(), userDto.isLocal(), userDto.getLogin(), userDto.getName(), "password", userDto.getSortedScmAccounts())))) + post(USER_ENDPOINT) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .content(gson.toJson(new UserCreateRestRequest( + userDto.getEmail(), userDto.isLocal(), userDto.getLogin(), userDto.getName(), "password", userDto.getSortedScmAccounts())))) .andExpect(status().isOk()) .andReturn(); RestUserForAdmins responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), RestUserForAdmins.class); - assertThat(responseUser).isEqualTo(toRestUser(userSearchResult)); + assertThat(responseUser).isEqualTo(toRestUser(userInformation)); + } + + @Test + public void updateUser_whenUserDoesntExist_shouldReturnNotFound() throws Exception { + userSession.logIn().setSystemAdministrator(); + when(userService.updateUser(eq("userLogin"), any(UpdateUser.class))).thenThrow(new NotFoundException("Not found")); + mockMvc.perform(patch(USER_ENDPOINT + "/userLogin") + .contentType(JSON_MERGE_PATCH_CONTENT_TYPE) + .content("{}")) + .andExpectAll( + status().isNotFound(), + content().json("{\"message\":\"Not found\"}")); + } + + @Test + public void updateUser_whenCallerIsNotAdmin_shouldReturnForbidden() throws Exception { + userSession.logIn().setNonSystemAdministrator(); + + mockMvc.perform( + patch(USER_ENDPOINT + "/userLogin") + .contentType(JSON_MERGE_PATCH_CONTENT_TYPE) + .content("{}")) + .andExpectAll( + status().isForbidden(), + content().json("{\"message\":\"Insufficient privileges\"}")); + } + + @Test + public void updateUser_whenEmailIsProvided_shouldUpdateUserAndReturnUpdatedValue() throws Exception { + UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"email\":\"newemail@example.com\"}"); + assertThat(userUpdate.email()).isEqualTo("newemail@example.com"); + assertThat(userUpdate.name()).isNull(); + assertThat(userUpdate.scmAccounts()).isNull(); + } + + private UpdateUser performPatchCallAndVerifyResponse(String payload) throws Exception { + userSession.logIn().setSystemAdministrator(); + UserInformation userInformation = generateUserSearchResult("1", true, true, false, 1, 2); + + when(userService.updateUser(eq("userLogin"), any())).thenReturn(userInformation); + when(responseGenerator.toRestUser(userInformation)).thenReturn(toRestUser(userInformation)); + + MvcResult mvcResult = mockMvc.perform(patch(USER_ENDPOINT + "/userLogin") + .contentType(JSON_MERGE_PATCH_CONTENT_TYPE) + .content(payload)) + .andExpect( + status().isOk()) + .andReturn(); + + RestUserForAdmins responseUser = gson.fromJson(mvcResult.getResponse().getContentAsString(), RestUserForAdmins.class); + assertThat(responseUser).isEqualTo(toRestUser(userInformation)); + + ArgumentCaptor<UpdateUser> updateUserCaptor = ArgumentCaptor.forClass(UpdateUser.class); + verify(userService).updateUser(eq("userLogin"), updateUserCaptor.capture()); + return updateUserCaptor.getValue(); + } + + @Test + public void updateUser_whenNameIsProvided_shouldUpdateUserAndReturnUpdatedValue() throws Exception { + UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"name\":\"new name\"}"); + assertThat(userUpdate.email()).isNull(); + assertThat(userUpdate.name()).isEqualTo("new name"); + assertThat(userUpdate.scmAccounts()).isNull(); + } + + @Test + public void updateUser_whenScmAccountsAreProvided_shouldUpdateUserAndReturnUpdatedValue() throws Exception { + UpdateUser userUpdate = performPatchCallAndVerifyResponse("{\"scmAccounts\":[\"account1\",\"account2\"]}"); + assertThat(userUpdate.email()).isNull(); + assertThat(userUpdate.name()).isNull(); + assertThat(userUpdate.scmAccounts()).containsExactly("account1", "account2"); + } + + @Test + public void updateUser_whenEmailIsInvalid_shouldReturnBadRequest() throws Exception { + performPatchCallAndExpectBadRequest("{\"email\":\"notavalidemail\"}", "Value notavalidemail for field email was rejected. Error: must be a well-formed email address."); + } + + private void performPatchCallAndExpectBadRequest(String payload, String expectedMessage) throws Exception { + userSession.logIn().setSystemAdministrator(); + + MvcResult mvcResult = mockMvc.perform(patch(USER_ENDPOINT + "/userLogin") + .contentType(JSON_MERGE_PATCH_CONTENT_TYPE) + .content(payload)) + .andExpect( + status().isBadRequest()) + .andReturn(); + + RestError error = gson.fromJson(mvcResult.getResponse().getContentAsString(), RestError.class); + assertThat(error.message()).isEqualTo(expectedMessage); + } + + @Test + public void updateUser_whenEmailIsEmpty_shouldReturnBadRequest() throws Exception { + performPatchCallAndExpectBadRequest("{\"email\":\"\"}", "Value for field email was rejected. Error: size must be between 1 and 100."); + } + + @Test + public void updateUser_whenNameIsTooLong_shouldReturnBadRequest() throws Exception { + String tooLong = "toolong".repeat(30); + String payload = "{\"name\":\"" + tooLong + "\"}"; + String message = "Value " + tooLong + " for field name was rejected. Error: size must be between 0 and 200."; + performPatchCallAndExpectBadRequest(payload, message); } } diff --git a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGeneratorTest.java b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGeneratorTest.java index d1f40524fd9..fbbaa1d11f9 100644 --- a/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGeneratorTest.java +++ b/server/sonar-webserver-webapi-v2/src/test/java/org/sonar/server/v2/api/user/converter/UsersSearchRestResponseGeneratorTest.java @@ -30,7 +30,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.sonar.api.utils.DateUtils; import org.sonar.db.user.UserDto; import org.sonar.server.common.PaginationInformation; -import org.sonar.server.common.user.service.UserSearchResult; +import org.sonar.server.common.user.service.UserInformation; import org.sonar.server.user.UserSession; import org.sonar.server.v2.api.response.PageRestResponse; import org.sonar.server.v2.api.user.model.RestUserForAdmins; @@ -70,19 +70,19 @@ public class UsersSearchRestResponseGeneratorTest { PaginationInformation paging = forPageIndex(1).withPageSize(2).andTotal(3); - UserSearchResult userSearchResult1 = mockSearchResult(1, true); - UserSearchResult userSearchResult2 = mockSearchResult(2, false); + UserInformation userInformation1 = mockSearchResult(1, true); + UserInformation userInformation2 = mockSearchResult(2, false); - UsersSearchRestResponse usersForResponse = usersSearchRestResponseGenerator.toUsersForResponse(List.of(userSearchResult1, userSearchResult2), paging); + UsersSearchRestResponse usersForResponse = usersSearchRestResponseGenerator.toUsersForResponse(List.of(userInformation1, userInformation2), paging); - RestUserForAdmins expectUser1 = buildExpectedResponseForAdmin(userSearchResult1); - RestUserForAdmins expectUser2 = buildExpectedResponseForAdmin(userSearchResult2); + RestUserForAdmins expectUser1 = buildExpectedResponseForAdmin(userInformation1); + RestUserForAdmins expectUser2 = buildExpectedResponseForAdmin(userInformation2); assertThat(usersForResponse.users()).containsExactly(expectUser1, expectUser2); assertPaginationInformationAreCorrect(paging, usersForResponse.page()); } - private static RestUserForAdmins buildExpectedResponseForAdmin(UserSearchResult userSearchResult) { - UserDto userDto = userSearchResult.userDto(); + private static RestUserForAdmins buildExpectedResponseForAdmin(UserInformation userInformation) { + UserDto userDto = userInformation.userDto(); return new RestUserForAdmins( userDto.getLogin(), userDto.getLogin(), @@ -90,13 +90,13 @@ public class UsersSearchRestResponseGeneratorTest { userDto.getEmail(), userDto.isActive(), userDto.isLocal(), - userSearchResult.managed(), + userInformation.managed(), userDto.getExternalLogin(), userDto.getExternalIdentityProvider(), - userSearchResult.avatar().orElse(null), + userInformation.avatar().orElse(null), toDateTime(userDto.getLastConnectionDate()), toDateTime(userDto.getLastSonarlintConnectionDate()), - userSearchResult.userDto().getSortedScmAccounts() + userInformation.userDto().getSortedScmAccounts() ); } @@ -106,19 +106,19 @@ public class UsersSearchRestResponseGeneratorTest { PaginationInformation paging = forPageIndex(1).withPageSize(2).andTotal(3); - UserSearchResult userSearchResult1 = mockSearchResult(1, true); - UserSearchResult userSearchResult2 = mockSearchResult(2, false); + UserInformation userInformation1 = mockSearchResult(1, true); + UserInformation userInformation2 = mockSearchResult(2, false); - UsersSearchRestResponse usersForResponse = usersSearchRestResponseGenerator.toUsersForResponse(List.of(userSearchResult1, userSearchResult2), paging); + UsersSearchRestResponse usersForResponse = usersSearchRestResponseGenerator.toUsersForResponse(List.of(userInformation1, userInformation2), paging); - RestUserForLoggedInUsers expectUser1 = buildExpectedResponseForUser(userSearchResult1); - RestUserForLoggedInUsers expectUser2 = buildExpectedResponseForUser(userSearchResult2); + RestUserForLoggedInUsers expectUser1 = buildExpectedResponseForUser(userInformation1); + RestUserForLoggedInUsers expectUser2 = buildExpectedResponseForUser(userInformation2); assertThat(usersForResponse.users()).containsExactly(expectUser1, expectUser2); assertPaginationInformationAreCorrect(paging, usersForResponse.page()); } - private static RestUserForLoggedInUsers buildExpectedResponseForUser(UserSearchResult userSearchResult) { - UserDto userDto = userSearchResult.userDto(); + private static RestUserForLoggedInUsers buildExpectedResponseForUser(UserInformation userInformation) { + UserDto userDto = userInformation.userDto(); return new RestUserForLoggedInUsers( userDto.getLogin(), userDto.getLogin(), @@ -127,7 +127,7 @@ public class UsersSearchRestResponseGeneratorTest { userDto.isActive(), userDto.isLocal(), userDto.getExternalIdentityProvider(), - userSearchResult.avatar().orElse(null) + userInformation.avatar().orElse(null) ); } @@ -135,19 +135,19 @@ public class UsersSearchRestResponseGeneratorTest { public void toUsersForResponse_whenAnonymous_returnsOnlyNameAndLogin() { PaginationInformation paging = forPageIndex(1).withPageSize(2).andTotal(3); - UserSearchResult userSearchResult1 = mockSearchResult(1, true); - UserSearchResult userSearchResult2 = mockSearchResult(2, false); + UserInformation userInformation1 = mockSearchResult(1, true); + UserInformation userInformation2 = mockSearchResult(2, false); - UsersSearchRestResponse usersForResponse = usersSearchRestResponseGenerator.toUsersForResponse(List.of(userSearchResult1, userSearchResult2), paging); + UsersSearchRestResponse usersForResponse = usersSearchRestResponseGenerator.toUsersForResponse(List.of(userInformation1, userInformation2), paging); - RestUserForAnonymousUsers expectUser1 = buildExpectedResponseForAnonymous(userSearchResult1); - RestUserForAnonymousUsers expectUser2 = buildExpectedResponseForAnonymous(userSearchResult2); + RestUserForAnonymousUsers expectUser1 = buildExpectedResponseForAnonymous(userInformation1); + RestUserForAnonymousUsers expectUser2 = buildExpectedResponseForAnonymous(userInformation2); assertThat(usersForResponse.users()).containsExactly(expectUser1, expectUser2); assertPaginationInformationAreCorrect(paging, usersForResponse.page()); } - private static RestUserForAnonymousUsers buildExpectedResponseForAnonymous(UserSearchResult userSearchResult) { - UserDto userDto = userSearchResult.userDto(); + private static RestUserForAnonymousUsers buildExpectedResponseForAnonymous(UserInformation userInformation) { + UserDto userDto = userInformation.userDto(); return new RestUserForAnonymousUsers( userDto.getLogin(), userDto.getLogin(), @@ -159,8 +159,8 @@ public class UsersSearchRestResponseGeneratorTest { return Optional.ofNullable(dateTimeMs).map(DateUtils::formatDateTime).orElse(null); } - private static UserSearchResult mockSearchResult(int i, boolean booleanFlagsValue) { - UserSearchResult userSearchResult = mock(UserSearchResult.class, RETURNS_DEEP_STUBS); + private static UserInformation mockSearchResult(int i, boolean booleanFlagsValue) { + UserInformation userInformation = mock(UserInformation.class, RETURNS_DEEP_STUBS); UserDto user1 = new UserDto() .setUuid("uuid_" + i) .setLogin("login_" + i) @@ -174,9 +174,9 @@ public class UsersSearchRestResponseGeneratorTest { .setLocal(booleanFlagsValue) .setActive(booleanFlagsValue); - when(userSearchResult.userDto()).thenReturn(user1); - when(userSearchResult.managed()).thenReturn(booleanFlagsValue); - return userSearchResult; + when(userInformation.userDto()).thenReturn(user1); + when(userInformation.managed()).thenReturn(booleanFlagsValue); + return userInformation; } private static void assertPaginationInformationAreCorrect(PaginationInformation paginationInformation, PageRestResponse pageRestResponse) { |