UserController.java
package api.controllers.users;
import java.util.List;
import org.hibernate.Hibernate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import api.dtos.ErrorDto;
import api.dtos.UserDto;
import api.entities.User;
import api.mapper.UserMapper;
import api.services.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
/**
* {@link UserController}.
*/
@RestController
@RequestMapping("/users")
@Tag(name = "Users", description = "System users.")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private UserMapper userMapper;
@Autowired
private PasswordEncoder encoder;
@Value("${pagination.default-page-size:10}")
private int defaultPageSize;
@Value("${pagination.max-page-size:100}")
private int maxPageSize;
/**
* Get me.
*
* @param user User
* @return {@link UserDto}
*/
// region
@Operation(
summary = "Get Me",
description = "Get the current user's information."
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
content = @Content(
schema = @Schema(implementation = UserDto.class),
mediaType = "application/json"
)
),
})
// endregion
@GetMapping("/me")
public UserDto getMe(@AuthenticationPrincipal User user) {
return userMapper.toDto(user);
}
/**
* Update me.
*
* @param user User
* @param userDto Updated info
* @return {@link UserDto}
*/
// region
@Operation(
summary = "Update Me",
description = "Update the current user's information."
+ "<ul>"
+ "<li>Ignores <em>id</em> and <em>password</em> from request body.</li>"
+ "<li>Any field missing or null from request body will be left unchanged in user information.</li>"
+ "</ul>"
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
content = @Content(
schema = @Schema(implementation = UserDto.class),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "400",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "409",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
})
// endregion
@PutMapping("/me")
public UserDto updateMe(@AuthenticationPrincipal User user, @RequestBody UserDto userDto) {
return userMapper.toDto(userService.update(user, userDto));
}
/**
* Update my password.
*
* @param user User
* @param password New password
*/
// region
@Operation(
summary = "Update My Password",
description = "Updates the current user's password."
)
@ApiResponses({
@ApiResponse(
responseCode = "200"
),
@ApiResponse(
responseCode = "400",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
})
// endregion
@PutMapping("/me/password")
public void updateMyPassword(@AuthenticationPrincipal User user, @RequestBody String password) {
if (encoder.matches(password, user.getPassword())) {
throw new IllegalArgumentException("New password matches existing password.");
}
user.setPassword(encoder.encode(password));
userService.save(user);
}
/**
* Delete me.
*
* @param user User
*/
// region
@Operation(
summary = "Delete Me",
description = "Deletes the current user."
)
@ApiResponses({
@ApiResponse(
responseCode = "200"
),
})
// endregion
@DeleteMapping("/me")
public void deleteMe(@AuthenticationPrincipal User user) {
userService.delete(user);
}
/**
* Get users.
*
* @param page Page index
* @param size Page size
* @return {@link List} of {@link UserDto}
*/
// region
@Operation(
summary = "Get Users",
description = "Get paginated list of user's information."
+ "<ul>"
+ "<li>If <em>size</em> excedes limit, response will contain up to limit.</li>"
+ "</ul>"
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
content = @Content(
array = @ArraySchema(schema = @Schema(implementation = UserDto.class)),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "403",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
})
// endregion
@GetMapping("")
@PreAuthorize("hasAuthority(@DbSetup.USER_READ)")
public List<UserDto> getUsers(
@RequestParam(defaultValue = "0") int page,
@RequestParam(required = false) Integer size
) {
int requestedSize = (size != null) ? size : defaultPageSize;
int safeSize = Math.min(requestedSize, maxPageSize);
Pageable pageable = PageRequest.of(page, safeSize, Sort.by("id").ascending());
return userService.getAll(pageable)
.map(userMapper::toDto)
.getContent();
}
/**
* Get user.
*
* @param id User id
* @return {@link UserDto}
*/
// region
@Operation(
summary = "Get User",
description = "Get specific user's information."
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
content = @Content(
schema = @Schema(implementation = UserDto.class),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "403",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "404",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
})
// endregion
@GetMapping("/{id}")
@PreAuthorize("hasAuthority(@DbSetup.USER_READ)")
public UserDto getUser(@PathVariable int id) {
User user = userService.get(id);
Hibernate.initialize(user);
return userMapper.toDto(user);
}
/**
* Update user.
*
* @param id User id
* @param userDto Updated info
* @return {@link UserDto}
*/
// region
@Operation(
summary = "Update User",
description = "Update specific user's information.<br>"
+ "<ul>"
+ "<li>Ignores <em>id</em> and <em>password</em> from request body.</li>"
+ "<li>Any field missing or null from request body will be left unchanged in user information.</li>"
+ "</ul>"
)
@ApiResponses({
@ApiResponse(
responseCode = "200",
content = @Content(
array = @ArraySchema(schema = @Schema(implementation = Integer.class)),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "403",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "404",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "409",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
})
// endregion
@PutMapping("/{id}")
@PreAuthorize("hasAuthority(@DbSetup.USER_READ) and hasAuthority(@DbSetup.USER_WRITE)")
public UserDto updateUser(@PathVariable int id, @RequestBody UserDto userDto) {
return userMapper.toDto(userService.update(id, userDto));
}
/**
* Delete user.
*
* @param id User id
*/
// region
@Operation(
summary = "Delete User",
description = "Deletes specific user."
)
@ApiResponses({
@ApiResponse(
responseCode = "200"
),
@ApiResponse(
responseCode = "403",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
@ApiResponse(
responseCode = "404",
content = @Content(
schema = @Schema(implementation = ErrorDto.class),
mediaType = "application/json"
)
),
})
// endregion
@DeleteMapping("/{id}")
@PreAuthorize("hasAuthority(@DbSetup.USER_WRITE)")
public void deleteUser(@PathVariable int id) {
userService.delete(id);
}
}