Skip to content

Commit

Permalink
add custom annotation @UserRoleDescription to print roles in swagger …
Browse files Browse the repository at this point in the history
…doc.
  • Loading branch information
Krasnov-Maksim committed Mar 26, 2024
1 parent 516ab86 commit a63c700
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package mate.academy.carsharing.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface UserRoleDescription {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package mate.academy.carsharing.annotation;

import io.swagger.v3.oas.models.Operation;
import org.springdoc.core.customizers.OperationCustomizer;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;

@Component
public class UserRoleDescriptionCustomizer implements OperationCustomizer {
@Override
public Operation customize(Operation operation, HandlerMethod handlerMethod) {
UserRoleDescription userRoleAnnotation =
handlerMethod.getMethodAnnotation(UserRoleDescription.class);
if (userRoleAnnotation != null) {
PreAuthorize preAuthorizeAnnotation =
handlerMethod.getMethodAnnotation(PreAuthorize.class);
if (preAuthorizeAnnotation != null
&& preAuthorizeAnnotation.value().contains("ROLE_")) {
String description = operation.getDescription() == null
? "" : (operation.getDescription()/* + "\n"*/);
int firstBraceIndex = preAuthorizeAnnotation.value().indexOf('(');
int lastBraceIndex = preAuthorizeAnnotation.value().lastIndexOf(')');
String rolesString = preAuthorizeAnnotation.value()
.substring(firstBraceIndex + 1, lastBraceIndex);
operation.setDescription(description + " Required roles: " + rolesString + '.');
}
}
return operation;
}
}
20 changes: 20 additions & 0 deletions src/main/java/mate/academy/carsharing/config/OpenApiConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package mate.academy.carsharing.config;

import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.info.Info;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
import io.swagger.v3.oas.annotations.security.SecuritySchemes;
import org.springframework.context.annotation.Configuration;

@Configuration
@OpenAPIDefinition(info = @Info(title = "Car-Sharing-App REST API", version = "1.0",
description = "REST API for Car-Sharing-App. Register, Login and use JWT token."),
security = {@SecurityRequirement(name = "bearerToken")}
)
@SecuritySchemes({@SecurityScheme(name = "bearerToken", type = SecuritySchemeType.HTTP,
scheme = "bearer", bearerFormat = "JWT")}
)
public class OpenApiConfig {
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package mate.academy.carsharing.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
Expand All @@ -17,7 +18,7 @@
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "Authentication manager", description = "Endpoints for registration")
@Tag(name = "Authentication controller", description = "Endpoints for registration and login.")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/auth")
Expand All @@ -26,13 +27,16 @@ public class AuthenticationController {
private final AuthenticationService authenticationService;

@ResponseStatus(HttpStatus.CREATED)
@Operation(summary = "Register a new user.", description = "Register a new user.")
@PostMapping("/registration")
public UserResponseDto register(@RequestBody @Valid UserRegistrationRequestDto request)
throws RegistrationException {
return userService.register(request);
}

@PostMapping("/login")
@Operation(summary = "Login to the app.",
description = "Login to the app, get JWT Token and use it to request data.")
public UserLoginResponseDto login(@RequestBody @Valid UserLoginRequestDto requestDto) {
return authenticationService.authentication(requestDto);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import jakarta.validation.Valid;
import java.util.List;
import lombok.RequiredArgsConstructor;
import mate.academy.carsharing.annotation.UserRoleDescription;
import mate.academy.carsharing.dto.car.CarResponseDto;
import mate.academy.carsharing.dto.car.CreateCarRequestDto;
import mate.academy.carsharing.service.CarService;
Expand All @@ -25,29 +26,32 @@
@RestController
@RequiredArgsConstructor
@RequestMapping(path = "/api/cars")
@Tag(name = "Car management", description = "Cars Management Endpoints")
@Tag(name = "Car management", description = "Cars Management Endpoints.")
public class CarController {
private final CarService carService;

@ResponseStatus(HttpStatus.CREATED)
@PreAuthorize("hasRole('ROLE_MANAGER')")
@Operation(summary = "Create a new car", description = "Create a new car")
@UserRoleDescription
@Operation(summary = "Create a new car.", description = "Create a new car.")
@PostMapping
public CarResponseDto create(@RequestBody @Valid CreateCarRequestDto carDto) {
return carService.save(carDto);
}

@ResponseStatus(HttpStatus.NO_CONTENT)
@PreAuthorize("hasRole('ROLE_MANAGER')")
@Operation(summary = "Delete a car by id", description = "Delete car with specified id")
@UserRoleDescription
@Operation(summary = "Delete a car by id.", description = "Delete car with specified id.")
@DeleteMapping("/{id}")
public void deleteById(@PathVariable Long id) {
carService.deleteById(id);
}

@ResponseStatus(HttpStatus.OK)
@PreAuthorize("hasRole('ROLE_MANAGER')")
@Operation(summary = "Update a car by id", description = "Update car with specified id")
@UserRoleDescription
@Operation(summary = "Update a car by id.", description = "Update car with specified id.")
@PutMapping("/{id}")
public CarResponseDto updateById(@PathVariable Long id,
@RequestBody @Valid CreateCarRequestDto requestDto) {
Expand All @@ -56,18 +60,20 @@ public CarResponseDto updateById(@PathVariable Long id,

@ResponseStatus(HttpStatus.OK)
@PreAuthorize("hasAnyRole('ROLE_MANAGER', 'ROLE_CUSTOMER', 'ROLE_ANONYMOUS')")
@Operation(summary = "Get a car by id", description = "Get a car with specified id")
@UserRoleDescription
@Operation(summary = "Get a car by id.", description = "Get a car with specified id.")
@GetMapping("/{id}")
public CarResponseDto getById(@PathVariable Long id) {
return carService.getById(id);
}

@ResponseStatus(HttpStatus.OK)
@PreAuthorize("permitAll()")
@Operation(summary = "Get all cars", description = "Get a list of all cars")
@UserRoleDescription
@Operation(summary = "Get all cars.", description = "Get a list of all cars.")
@Parameter(name = "page", description = "page index, default value = 0")
@Parameter(name = "size", description = "elements per page, default value = 20")
@Parameter(name = "sort", description = "sort criteria", example = "title,Desc")
@Parameter(name = "sort", description = "sort criteria", example = "brand,Desc")
@GetMapping
public List<CarResponseDto> getAll(Pageable pageable) {
return carService.getAll(pageable);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package mate.academy.carsharing.controller;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.util.List;
import lombok.RequiredArgsConstructor;
import mate.academy.carsharing.annotation.UserRoleDescription;
import mate.academy.carsharing.dto.payment.CreatePaymentRequestDto;
import mate.academy.carsharing.dto.payment.PaymentResponseDto;
import mate.academy.carsharing.dto.payment.PaymentSearchParametersDto;
Expand All @@ -31,7 +33,8 @@ public class PaymentController {

@ResponseStatus(HttpStatus.CREATED)
@PreAuthorize("hasRole('ROLE_MANAGER')")
@Operation(summary = "Create new payment", description = "Create new payment session")
@UserRoleDescription
@Operation(summary = "Create new payment.", description = "Create new payment session.")
@PostMapping()
public PaymentResponseDto createPayment(
@RequestBody @Valid CreatePaymentRequestDto requestDto) {
Expand All @@ -40,6 +43,12 @@ public PaymentResponseDto createPayment(

@ResponseStatus(HttpStatus.OK)
@PreAuthorize("hasAnyRole('ROLE_MANAGER', 'ROLE_CUSTOMER')")
@UserRoleDescription
@Operation(summary = "Search payments.", description = "User can view own payments. "
+ "Admin can view all payments.")
@Parameter(name = "page", description = "page index, default value = 0")
@Parameter(name = "size", description = "elements per page, default value = 20")
@Parameter(name = "sort", description = "sort criteria", example = "amountToPay,Desc")
@GetMapping("/search")
public List<PaymentResponseDto> searchPayments(Authentication authentication,
PaymentSearchParametersDto searchParameters, Pageable pageable) {
Expand All @@ -48,8 +57,9 @@ public List<PaymentResponseDto> searchPayments(Authentication authentication,

@ResponseStatus(HttpStatus.OK)
@PreAuthorize("permitAll()")
@Operation(summary = "Stripe redirection endpoint",
description = "Mark payment as success, send message to Telegram")
@UserRoleDescription
@Operation(summary = "Stripe redirection endpoint marks payment as success.",
description = "Mark payment as success, send message to Telegram.")
@GetMapping("/success")
public PaymentResponseDto processSuccessfulPayment(
@RequestParam(name = "session_id") String sessionId) {
Expand All @@ -58,8 +68,9 @@ public PaymentResponseDto processSuccessfulPayment(

@ResponseStatus(HttpStatus.OK)
@PreAuthorize("permitAll()")
@Operation(summary = "Stripe redirection endpoint",
description = "Mark payment as canceled, send message to Telegram")
@UserRoleDescription
@Operation(summary = "Stripe redirection endpoint marks payment as canceled",
description = "Marks payment as canceled, send message to Telegram.")
@GetMapping("/cancel")
public PaymentResponseDto processCanceledPayment(
@RequestParam(name = "session_id") String sessionId) {
Expand All @@ -68,8 +79,9 @@ public PaymentResponseDto processCanceledPayment(

@ResponseStatus(HttpStatus.OK)
@PreAuthorize("hasAnyRole('ROLE_MANAGER', 'ROLE_CUSTOMER')")
@Operation(summary = "Endpoint to renew expired session",
description = "User can renew expired session and get new payment link")
@UserRoleDescription
@Operation(summary = "Renew expired session.",
description = "User can renew expired session and get new payment link.")
@PostMapping("/renew")
public PaymentResponseDto renewPaymentSession(
@RequestBody @Valid RenewPaymentRequestDto requestDto,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import jakarta.validation.Valid;
import java.util.List;
import lombok.RequiredArgsConstructor;
import mate.academy.carsharing.annotation.UserRoleDescription;
import mate.academy.carsharing.dto.rental.CreateRentalRequestDto;
import mate.academy.carsharing.dto.rental.RentalResponseDto;
import mate.academy.carsharing.dto.rental.RentalSearchParametersDto;
Expand All @@ -22,7 +23,7 @@
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "Rental managing", description = "Endpoint to rentals managing")
@Tag(name = "Rental managing", description = "Endpoint for managing rentals")
@RequiredArgsConstructor
@RestController
@RequestMapping("api/rentals")
Expand All @@ -31,16 +32,18 @@ public class RentalController {

@ResponseStatus(HttpStatus.CREATED)
@PreAuthorize("hasRole('ROLE_MANAGER')")
@Operation(summary = "Create a new rental", description = "Admin creates a new rental")
@UserRoleDescription
@Operation(summary = "Create a new rental.", description = "Admin can create a new rental.")
@PostMapping()
public RentalResponseDto create(@RequestBody @Valid CreateRentalRequestDto requestDto) {
return rentalService.save(requestDto);
}

@ResponseStatus(HttpStatus.OK)
@PreAuthorize("hasRole('ROLE_MANAGER')")
@Operation(summary = "Get list of rentals", description = "Admin can get list of "
+ "active/nonactive rentals for any number of specified users or for all users")
@UserRoleDescription
@Operation(summary = "Get list of rentals.", description = "Admin can get list of "
+ "active/nonactive rentals for any number of specified users or for all users.")
@Parameter(name = "user_id", description = "Get rentals for users with specified 'user_id'."
+ "To get rentals for all users do not specify any value.", example = "2, 57")
@Parameter(name = "is_active", description = "Specify 'true' to get active rentals or "
Expand All @@ -54,7 +57,8 @@ public List<RentalResponseDto> searchRentals(

@ResponseStatus(HttpStatus.OK)
@PreAuthorize("hasRole('ROLE_CUSTOMER')")
@Operation(summary = "Get rental info by id", description = "Get rental info by id")
@UserRoleDescription
@Operation(summary = "Get rental info by id.", description = "Get rental info by id.")
@Parameter(name = "id", description = "Rental id", example = "247")
@GetMapping("/{id}")
public RentalResponseDto getRental(@PathVariable Long id, Authentication authentication) {
Expand All @@ -63,7 +67,8 @@ public RentalResponseDto getRental(@PathVariable Long id, Authentication authent

@ResponseStatus(HttpStatus.OK)
@PreAuthorize("hasRole('ROLE_MANAGER')")
@Operation(summary = "Return rental", description = "Admin can return rental by id")
@UserRoleDescription
@Operation(summary = "Return rental.", description = "Admin can return rental by id.")
@Parameter(name = "id", description = "Rental id", example = "247")
@PostMapping("/{id}/return")
public RentalResponseDto returnRental(@PathVariable Long id) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import mate.academy.carsharing.annotation.UserRoleDescription;
import mate.academy.carsharing.dto.user.UserResponseDto;
import mate.academy.carsharing.dto.user.UserResponseDtoWithRoles;
import mate.academy.carsharing.dto.user.UserUpdateInfoRequestDto;
Expand All @@ -21,16 +22,18 @@
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "Users managing", description = "Endpoints to user managing")
@Tag(name = "Users managing", description = "Endpoints for managing users")
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;

@ResponseStatus(HttpStatus.OK)
@Operation(summary = "Update user role", description = "Manager updates role for specify user")
@Operation(summary = "Update user role.",
description = "Manager updates role for specify user.")
@PreAuthorize("hasRole('ROLE_MANAGER')")
@UserRoleDescription
@PutMapping("/{id}/role")
public UserResponseDtoWithRoles updateRole(@PathVariable Long id,
@RequestBody @Valid UserUpdateRoleRequestDto requestDto) {
Expand All @@ -39,8 +42,9 @@ public UserResponseDtoWithRoles updateRole(@PathVariable Long id,

@ResponseStatus(HttpStatus.OK)
@PreAuthorize("hasRole('ROLE_CUSTOMER')")
@Operation(summary = "Update info about user",
description = "Update firstName, lastName or password")
@UserRoleDescription
@Operation(summary = "Update info about user.",
description = "User can update firstName, lastName or password.")
@PatchMapping("/me")
public UserResponseDto updateUserInfo(Authentication authentication,
@RequestBody @Valid UserUpdateInfoRequestDto requestDto) {
Expand All @@ -49,7 +53,8 @@ public UserResponseDto updateUserInfo(Authentication authentication,

@ResponseStatus(HttpStatus.OK)
@PreAuthorize("hasAnyRole('ROLE_MANAGER', 'ROLE_CUSTOMER')")
@Operation(summary = "Get user info", description = "Get info about user")
@UserRoleDescription
@Operation(summary = "Get user info.", description = "Get info about user.")
@GetMapping("/me")
public UserResponseDtoWithRoles getUserInfo(Authentication authentication) {
return userService.getUserInfo(authentication.getName());
Expand Down

0 comments on commit a63c700

Please sign in to comment.