Closes #2348 - adds feature to request the current user via /users?currentUser

Signed-off-by: Kálmán Képes <2853992+nyuuyn@users.noreply.github.com>
This commit is contained in:
Kálmán Képes 2023-08-10 14:58:36 +02:00 committed by Elena Mokeeva
parent 44c023746d
commit 208ee77474
4 changed files with 120 additions and 5 deletions

View File

@ -11,6 +11,9 @@ Whenever a parameter is a complex type, the attributes of the value-object can b
For example, a complex parameter with the name "complex-query-param" and attributes "attribute1" and "attribute2" would be specified in the following way: +
complex-query-param={"attribute1":"value1","attribute2":"value2"}
Whenever a parameter is a value-less type (e.g owner-is-null and current-user) it is expected to be defined without a value,
i.e., it should be specified as ?parameter and not ?parameter= or ?parameter=someValue
=== Hypermedia Support
NOTE: HATEOAS support is still in development.

View File

@ -38,7 +38,7 @@ public class QueryParamsValidator {
checkExactParam(request, "owner-is-null");
}
private static void checkExactParam(HttpServletRequest request, String queryParameter) {
public static void checkExactParam(HttpServletRequest request, String queryParameter) {
String queryString = request.getQueryString();
boolean containParam = queryString != null && queryString.contains(queryParameter);
if (containParam) {

View File

@ -2,6 +2,8 @@ package pro.taskana.user.rest;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.hateoas.config.EnableHypermediaSupport;
import org.springframework.hateoas.config.EnableHypermediaSupport.HypermediaType;
@ -18,7 +20,9 @@ import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import pro.taskana.common.api.exceptions.InvalidArgumentException;
import pro.taskana.common.api.exceptions.NotAuthorizedException;
import pro.taskana.common.api.security.CurrentUserContext;
import pro.taskana.common.rest.RestEndpoints;
import pro.taskana.common.rest.util.QueryParamsValidator;
import pro.taskana.user.api.UserService;
import pro.taskana.user.api.exceptions.UserAlreadyExistException;
import pro.taskana.user.api.exceptions.UserNotFoundException;
@ -34,10 +38,15 @@ public class UserController {
private final UserService userService;
private final UserRepresentationModelAssembler userAssembler;
private final CurrentUserContext currentUserContext;
@Autowired
UserController(UserService userService, UserRepresentationModelAssembler userAssembler) {
UserController(UserService userService,
UserRepresentationModelAssembler userAssembler,
CurrentUserContext currentUserContext) {
this.userService = userService;
this.userAssembler = userAssembler;
this.currentUserContext = currentUserContext;
}
/**
@ -54,24 +63,39 @@ public class UserController {
public ResponseEntity<UserRepresentationModel> getUser(@PathVariable String userId)
throws UserNotFoundException, InvalidArgumentException {
User user = userService.getUser(userId);
return ResponseEntity.ok(userAssembler.toModel(user));
}
/**
* This endpoint retrieves multiple Users. If a userId can't be found in the database it will be
* ignored. If none of the given userIds is valid, the returned list will be empty.
* If currentUser is set, the current User from the context will be retrieved as well
*
* @title Get multiple Users
* @param request the HttpServletRequest of the request itself
* @param userIds the ids of the requested Users
* @param currentUser Indicates whether to fetch the current user or not as well
* @return the requested Users
* @throws InvalidArgumentException if the userIds are null or empty
* @throws UserNotFoundException if the current User was not found
*/
@GetMapping(RestEndpoints.URL_USERS)
@Transactional(readOnly = true, rollbackFor = Exception.class)
public ResponseEntity<UserCollectionRepresentationModel> getUsers(
@RequestParam(name = "user-id") String[] userIds) throws InvalidArgumentException {
List<User> users = userService.getUsers(new HashSet<>(List.of(userIds)));
HttpServletRequest request,
@RequestParam(name = "user-id", required = false) String[] userIds,
@RequestParam(name = "current-user", required = false) String currentUser)
throws InvalidArgumentException, UserNotFoundException {
Set<User> users = new HashSet<>();
if (userIds != null) {
users.addAll(userService.getUsers(new HashSet<>(List.of(userIds))));
}
if (currentUser != null) {
QueryParamsValidator.checkExactParam(request, "current-user");
users.add(userService.getUser(this.currentUserContext.getUserid()));
}
return ResponseEntity.ok(userAssembler.toTaskanaCollectionModel(users));
}

View File

@ -63,6 +63,94 @@ class UserControllerIntTest {
.containsExactlyInAnyOrder("Max", "Elena");
}
@Test
void should_ReturnCurrentUser() {
String url = restHelper.toUrl(RestEndpoints.URL_USERS) + "?current-user";
HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
ResponseEntity<UserCollectionRepresentationModel> response =
TEMPLATE.exchange(
url,
HttpMethod.GET,
auth,
ParameterizedTypeReference.forType(UserCollectionRepresentationModel.class));
assertThat(response.getBody()).isNotNull();
assertThat(response.getBody().getContent()).hasSize(1);
assertThat(response.getBody().getContent()).extracting("userId").containsExactly("teamlead-1");
}
@Test
void should_ReturnExceptionCurrentUserWithBadValue() {
String url = restHelper.toUrl(RestEndpoints.URL_USERS) + "?current-user=asd";
HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
ThrowingCallable httpCall =
() -> TEMPLATE.exchange(
url,
HttpMethod.GET,
auth,
ParameterizedTypeReference.forType(UserCollectionRepresentationModel.class));
assertThatThrownBy(httpCall)
.isInstanceOf(HttpStatusCodeException.class)
.extracting(HttpStatusCodeException.class::cast)
.extracting(HttpStatusCodeException::getStatusCode)
.isEqualTo(HttpStatus.BAD_REQUEST);
}
@Test
void should_ReturnExceptionCurrentUserWithEmptyValue() {
String url = restHelper.toUrl(RestEndpoints.URL_USERS) + "?current-user=";
HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
ThrowingCallable httpCall =
() -> TEMPLATE.exchange(
url,
HttpMethod.GET,
auth,
ParameterizedTypeReference.forType(UserCollectionRepresentationModel.class));
assertThatThrownBy(httpCall)
.isInstanceOf(HttpStatusCodeException.class)
.extracting(HttpStatusCodeException.class::cast)
.extracting(HttpStatusCodeException::getStatusCode)
.isEqualTo(HttpStatus.BAD_REQUEST);
}
@Test
void should_ReturnOnlyCurrentUserWhileUsingUserIds() {
String url = restHelper.toUrl(RestEndpoints.URL_USERS) + "?current-user&user-id=teamlead-1";
HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
ResponseEntity<UserCollectionRepresentationModel> response =
TEMPLATE.exchange(
url,
HttpMethod.GET,
auth,
ParameterizedTypeReference.forType(UserCollectionRepresentationModel.class));
assertThat(response.getBody()).isNotNull();
assertThat(response.getBody().getContent()).hasSize(1);
assertThat(response.getBody().getContent()).extracting("userId").containsExactly("teamlead-1");
}
@Test
void should_ReturnExistingUsersAndCurrentUser() throws Exception {
String url = restHelper.toUrl(RestEndpoints.URL_USERS)
+ "?user-id=user-1-1&user-id=USER-1-2&current-user";
HttpEntity<?> auth = new HttpEntity<>(RestHelper.generateHeadersForUser("teamlead-1"));
ResponseEntity<UserCollectionRepresentationModel> responseEntity =
TEMPLATE.exchange(
url,
HttpMethod.GET,
auth,
ParameterizedTypeReference.forType(UserCollectionRepresentationModel.class));
UserCollectionRepresentationModel response = responseEntity.getBody();
assertThat(response).isNotNull();
assertThat(response.getContent()).hasSize(3);
assertThat(response.getContent())
.extracting("userId")
.containsExactlyInAnyOrder("user-1-1", "user-1-2", "teamlead-1");
}
@Test
void should_ReturnExistingUsers_When_ParameterContainsDuplicateAndInvalidIds() throws Exception {
// also testing different query parameter format