From e92fa9c5c53b6d0dbe529b4069d6f9aa89991649 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Fri, 10 Apr 2026 15:04:38 +0300 Subject: [PATCH 1/9] feat: add item requests and gateway module --- gateway/pom.xml | 43 ++++++ .../ru/practicum/shareit/ShareItGateway.java | 11 ++ .../shareit/booking/BookingClient.java | 54 ++++++++ .../shareit/booking/BookingController.java | 55 ++++++++ .../shareit/booking/dto/BookingDto.java | 10 +- .../practicum/shareit/client/BaseClient.java | 123 ++++++++++++++++++ .../shareit/client/RestTemplateConfig.java | 14 ++ .../shareit/exception/ErrorHandler.java | 25 ++++ .../ru/practicum/shareit/item/ItemClient.java | 59 +++++++++ .../shareit/item/ItemController.java | 66 ++++++++++ .../shareit/item/dto/CommentDto.java | 15 +++ .../practicum/shareit/item/dto/ItemDto.java | 21 +++ .../shareit/request/RequestClient.java | 48 +++++++ .../shareit/request/RequestController.java | 48 +++++++ .../shareit/request/dto/RequestDto.java | 14 ++ .../ru/practicum/shareit/user/UserClient.java | 46 +++++++ .../shareit/user/UserController.java | 42 ++++++ .../practicum/shareit/user/dto/UserDto.java | 19 +++ .../src/main/resources/application.properties | 2 + pom.xml | 103 ++++----------- server/pom.xml | 48 +++++++ .../java/ru/practicum/shareit/ShareItApp.java | 0 .../shareit/booking/BookingController.java | 16 ++- .../shareit/booking/BookingRepository.java | 21 +-- .../shareit/booking/BookingService.java | 6 +- .../shareit/booking/BookingServiceImpl.java | 73 ++++++----- .../shareit/booking/dto/BookingDto.java | 15 +++ .../booking/dto/BookingResponseDto.java | 0 .../shareit/booking/mapper/BookingMapper.java | 0 .../shareit/booking/model/Booking.java | 0 .../shareit/booking/model/Status.java | 0 .../shareit/exception/ConflictException.java | 0 .../shareit/exception/ErrorHandler.java | 34 +++++ .../shareit/exception/ForbiddenException.java | 7 + .../shareit/exception/NotFoundException.java | 0 .../exception/ValidationException.java | 0 .../shareit/item/CommentRepository.java | 0 .../shareit/item/ItemController.java | 49 +++++++ .../shareit/item/ItemRepository.java | 20 +++ .../practicum/shareit/item/ItemService.java | 6 +- .../shareit/item/ItemServiceImpl.java | 31 +++-- .../shareit/item/dto/CommentDto.java | 3 - .../practicum/shareit/item/dto/ItemDto.java | 8 +- .../shareit/item/mapper/CommentMapper.java | 0 .../shareit/item/mapper/ItemMapper.java | 31 +++++ .../practicum/shareit/item/model/Comment.java | 0 .../ru/practicum/shareit/item/model/Item.java | 0 .../shareit/request/ItemRequest.java | 29 +++++ .../request/ItemRequestController.java | 39 ++++++ .../request/ItemRequestRepository.java | 14 ++ .../shareit/request/ItemRequestService.java | 14 ++ .../request/ItemRequestServiceImpl.java | 77 +++++++++++ .../shareit/request/dto/ItemRequestDto.java | 18 +++ .../java/ru/practicum/shareit/user/User.java | 18 +-- .../shareit/user/UserController.java | 4 +- .../shareit/user/UserRepository.java | 0 .../practicum/shareit/user/UserService.java | 1 - .../shareit/user/UserServiceImpl.java | 34 ++--- .../practicum/shareit/user/dto/UserDto.java | 14 ++ .../shareit/user/mapper/UserMapper.java | 2 +- .../resources/application-test.properties | 0 .../main/resources/application.properties | 8 +- {src => server/src}/main/resources/schema.sql | 8 ++ .../ru/practicum/shareit/ShareItTests.java | 0 .../shareit/exception/ErrorHandler.java | 50 ------- .../shareit/item/ItemController.java | 53 -------- .../shareit/item/ItemRepository.java | 20 --- .../shareit/item/mapper/ItemMapper.java | 31 ----- .../shareit/request/ItemRequest.java | 7 - .../request/ItemRequestController.java | 10 -- .../shareit/request/dto/ItemRequestDto.java | 7 - .../practicum/shareit/user/dto/UserDto.java | 29 ----- 72 files changed, 1263 insertions(+), 410 deletions(-) create mode 100644 gateway/pom.xml create mode 100644 gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java rename {src => gateway/src}/main/java/ru/practicum/shareit/booking/dto/BookingDto.java (74%) create mode 100644 gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/client/RestTemplateConfig.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/item/ItemController.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/request/RequestController.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/request/dto/RequestDto.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/user/UserClient.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/user/UserController.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/user/dto/UserDto.java create mode 100644 gateway/src/main/resources/application.properties create mode 100644 server/pom.xml rename {src => server/src}/main/java/ru/practicum/shareit/ShareItApp.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingController.java (69%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingRepository.java (75%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingService.java (88%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java (58%) create mode 100644 server/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java rename {src => server/src}/main/java/ru/practicum/shareit/booking/dto/BookingResponseDto.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/mapper/BookingMapper.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/model/Booking.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/model/Status.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/ConflictException.java (100%) create mode 100644 server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java create mode 100644 server/src/main/java/ru/practicum/shareit/exception/ForbiddenException.java rename {src => server/src}/main/java/ru/practicum/shareit/exception/NotFoundException.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/ValidationException.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/CommentRepository.java (100%) create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemController.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemRepository.java rename {src => server/src}/main/java/ru/practicum/shareit/item/ItemService.java (75%) rename {src => server/src}/main/java/ru/practicum/shareit/item/ItemServiceImpl.java (82%) rename {src => server/src}/main/java/ru/practicum/shareit/item/dto/CommentDto.java (83%) rename {src => server/src}/main/java/ru/practicum/shareit/item/dto/ItemDto.java (81%) rename {src => server/src}/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java (100%) create mode 100644 server/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java rename {src => server/src}/main/java/ru/practicum/shareit/item/model/Comment.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/model/Item.java (100%) create mode 100644 server/src/main/java/ru/practicum/shareit/request/ItemRequest.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/ItemRequestRepository.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/ItemRequestServiceImpl.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java rename {src => server/src}/main/java/ru/practicum/shareit/user/User.java (54%) rename {src => server/src}/main/java/ru/practicum/shareit/user/UserController.java (90%) rename {src => server/src}/main/java/ru/practicum/shareit/user/UserRepository.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/UserService.java (99%) rename {src => server/src}/main/java/ru/practicum/shareit/user/UserServiceImpl.java (55%) create mode 100644 server/src/main/java/ru/practicum/shareit/user/dto/UserDto.java rename {src => server/src}/main/java/ru/practicum/shareit/user/mapper/UserMapper.java (100%) rename {src => server/src}/main/resources/application-test.properties (100%) rename {src => server/src}/main/resources/application.properties (70%) rename {src => server/src}/main/resources/schema.sql (82%) rename {src => server/src}/test/java/ru/practicum/shareit/ShareItTests.java (100%) delete mode 100644 src/main/java/ru/practicum/shareit/exception/ErrorHandler.java delete mode 100644 src/main/java/ru/practicum/shareit/item/ItemController.java delete mode 100644 src/main/java/ru/practicum/shareit/item/ItemRepository.java delete mode 100644 src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java delete mode 100644 src/main/java/ru/practicum/shareit/request/ItemRequest.java delete mode 100644 src/main/java/ru/practicum/shareit/request/ItemRequestController.java delete mode 100644 src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java delete mode 100644 src/main/java/ru/practicum/shareit/user/dto/UserDto.java diff --git a/gateway/pom.xml b/gateway/pom.xml new file mode 100644 index 0000000..43f5f06 --- /dev/null +++ b/gateway/pom.xml @@ -0,0 +1,43 @@ + + + + shareit + ru.practicum + 0.0.1-SNAPSHOT + + 4.0.0 + + shareit-gateway + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-validation + + + org.projectlombok + lombok + true + + + org.apache.httpcomponents.client5 + httpclient5 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java b/gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java new file mode 100644 index 0000000..e14acfb --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java @@ -0,0 +1,11 @@ +package ru.practicum.shareit; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ShareItGateway { + public static void main(String[] args) { + SpringApplication.run(ShareItGateway.class, args); + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java new file mode 100644 index 0000000..d1d3bce --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java @@ -0,0 +1,54 @@ +package ru.practicum.shareit.booking; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.util.DefaultUriBuilderFactory; +import ru.practicum.shareit.booking.dto.BookingDto; +import ru.practicum.shareit.client.BaseClient; +import java.util.Map; + +@Service +public class BookingClient extends BaseClient { + private static final String API_PREFIX = "/bookings"; + + @Autowired + public BookingClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { + super(builder.uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()).build()); + } + + public ResponseEntity create(Long userId, BookingDto requestDto) { + return post("", userId, requestDto); + } + + public ResponseEntity approve(Long userId, Long bookingId, boolean approved) { + Map parameters = Map.of("approved", approved); + return patch("/" + bookingId + "?approved={approved}", userId, parameters, null); + } + + public ResponseEntity getById(Long userId, Long bookingId) { + return get("/" + bookingId, userId); + } + + public ResponseEntity getAllByBooker(Long userId, String state, Integer from, Integer size) { + Map parameters = Map.of( + "state", state, + "from", from, + "size", size + ); + return get("?state={state}&from={from}&size={size}", userId, parameters); + } + + public ResponseEntity getAllByOwner(Long userId, String state, Integer from, Integer size) { + Map parameters = Map.of( + "state", state, + "from", from, + "size", size + ); + return get("/owner?state={state}&from={from}&size={size}", userId, parameters); + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java new file mode 100644 index 0000000..c40f4e2 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -0,0 +1,55 @@ +package ru.practicum.shareit.booking; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.booking.dto.BookingDto; + +@Controller +@RequestMapping(path = "/bookings") +@RequiredArgsConstructor +@Validated +public class BookingController { + private final BookingClient bookingClient; + + @PostMapping + public ResponseEntity create(@RequestHeader("X-Sharer-User-Id") Long userId, + @Valid @RequestBody BookingDto bookingDto) { + return bookingClient.create(userId, bookingDto); + } + + @PatchMapping("/{bookingId}") + public ResponseEntity approve(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long bookingId, + @RequestParam boolean approved) { + return bookingClient.approve(userId, bookingId, approved); + } + + @GetMapping("/{bookingId}") + public ResponseEntity getById(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long bookingId) { + return bookingClient.getById(userId, bookingId); + } + + @GetMapping + public ResponseEntity getAllByBooker( + @RequestHeader("X-Sharer-User-Id") Long userId, + @RequestParam(name = "state", defaultValue = "ALL") String state, + @RequestParam(name = "from", defaultValue = "0") Integer from, + @RequestParam(name = "size", defaultValue = "10") Integer size) { + return bookingClient.getAllByBooker(userId, state, from, size); + } + + @GetMapping("/owner") + public ResponseEntity getAllByOwner( + @RequestHeader("X-Sharer-User-Id") Long userId, + @RequestParam(name = "state", defaultValue = "ALL") String state, + @RequestParam(name = "from", defaultValue = "0") Integer from, + @RequestParam(name = "size", defaultValue = "10") Integer size) { + return bookingClient.getAllByOwner(userId, state, from, size); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java similarity index 74% rename from src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java rename to gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java index ffb2238..be71248 100644 --- a/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java +++ b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java @@ -1,27 +1,19 @@ package ru.practicum.shareit.booking.dto; -import jakarta.validation.constraints.Future; -import jakarta.validation.constraints.FutureOrPresent; import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.experimental.FieldDefaults; - import java.time.LocalDateTime; @Data -@AllArgsConstructor @NoArgsConstructor +@AllArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE) public class BookingDto { - @NotNull Long itemId; - @NotNull - @FutureOrPresent LocalDateTime start; - @NotNull - @Future LocalDateTime end; } \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java new file mode 100644 index 0000000..c3658e4 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java @@ -0,0 +1,123 @@ +package ru.practicum.shareit.client; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.Nullable; +import org.springframework.web.client.HttpStatusCodeException; +import org.springframework.web.client.RestTemplate; + +import java.util.List; +import java.util.Map; + +public class BaseClient { + protected final RestTemplate rest; + + public BaseClient(RestTemplate rest) { + this.rest = rest; + } + + protected ResponseEntity get(String path) { + return get(path, null, null); + } + + protected ResponseEntity get(String path, long userId) { + return get(path, userId, null); + } + + protected ResponseEntity get(String path, Long userId, @Nullable Map parameters) { + return makeAndSendRequest(HttpMethod.GET, path, userId, parameters, null); + } + + protected ResponseEntity post(String path, T body) { + return post(path, null, null, body); + } + + protected ResponseEntity post(String path, long userId, T body) { + return post(path, userId, null, body); + } + + protected ResponseEntity post(String path, Long userId, @Nullable Map parameters, T body) { + return makeAndSendRequest(HttpMethod.POST, path, userId, parameters, body); + } + + protected ResponseEntity put(String path, long userId, T body) { + return put(path, userId, null, body); + } + + protected ResponseEntity put(String path, Long userId, @Nullable Map parameters, T body) { + return makeAndSendRequest(HttpMethod.PUT, path, userId, parameters, body); + } + + protected ResponseEntity patch(String path, T body) { + return patch(path, null, null, body); + } + + protected ResponseEntity patch(String path, long userId) { + return patch(path, userId, null, null); + } + + protected ResponseEntity patch(String path, long userId, T body) { + return patch(path, userId, null, body); + } + + protected ResponseEntity patch(String path, Long userId, + @Nullable Map parameters, @Nullable T body) { + return makeAndSendRequest(HttpMethod.PATCH, path, userId, parameters, body); + } + + protected ResponseEntity delete(String path) { + return delete(path, null, null); + } + + protected ResponseEntity delete(String path, long userId) { + return delete(path, userId, null); + } + + protected ResponseEntity delete(String path, Long userId, @Nullable Map parameters) { + return makeAndSendRequest(HttpMethod.DELETE, path, userId, parameters, null); + } + + private ResponseEntity makeAndSendRequest(HttpMethod method, String path, Long userId, @Nullable Map parameters, @Nullable T body) { + HttpEntity requestEntity = new HttpEntity<>(body, defaultHeaders(userId)); + + ResponseEntity shareitServerResponse; + try { + if (parameters != null) { + shareitServerResponse = rest.exchange(path, method, requestEntity, Object.class, parameters); + } else { + shareitServerResponse = rest.exchange(path, method, requestEntity, Object.class); + } + } catch (HttpStatusCodeException e) { + return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBodyAsByteArray()); + } + return prepareGatewayResponse(shareitServerResponse); + } + + private HttpHeaders defaultHeaders(Long userId) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + if (userId != null) { + headers.set("X-Sharer-User-Id", String.valueOf(userId)); + } + return headers; + } + + private static ResponseEntity prepareGatewayResponse(ResponseEntity response) { + if (response.getStatusCode().is2xxSuccessful()) { + return response; + } + + ResponseEntity.BodyBuilder responseBuilder = ResponseEntity.status(response.getStatusCode()); + + if (response.hasBody()) { + return responseBuilder.body(response.getBody()); + } + + return responseBuilder.build(); + } + +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/client/RestTemplateConfig.java b/gateway/src/main/java/ru/practicum/shareit/client/RestTemplateConfig.java new file mode 100644 index 0000000..3fd80d2 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/client/RestTemplateConfig.java @@ -0,0 +1,14 @@ +package ru.practicum.shareit.client; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class RestTemplateConfig { + @Bean + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder.build(); + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java new file mode 100644 index 0000000..cd7c98f --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -0,0 +1,25 @@ +package ru.practicum.shareit.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import java.util.Map; + +@RestControllerAdvice +public class ErrorHandler { + + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Map handleValidation(final MethodArgumentNotValidException e) { + return Map.of("error", "Ошибка валидации"); + } + + @ExceptionHandler(Throwable.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public Map handleThrowable(final Throwable e) { + return Map.of("error", "Непредвиденная ошибка"); + } + +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java new file mode 100644 index 0000000..0604c62 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java @@ -0,0 +1,59 @@ +package ru.practicum.shareit.item; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.util.DefaultUriBuilderFactory; +import ru.practicum.shareit.client.BaseClient; +import ru.practicum.shareit.item.dto.CommentDto; +import ru.practicum.shareit.item.dto.ItemDto; + +import java.util.Map; + + +@Service +public class ItemClient extends BaseClient { + private static final String API_PREFIX = "/items"; + + @Autowired + public ItemClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { + super(builder.uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()).build()); + } + + public ResponseEntity create(Long userId, ItemDto itemDto) { + return post("", userId, itemDto); + } + + public ResponseEntity update(Long userId, Long itemId, ItemDto itemDto) { + return patch("/" + itemId, userId, itemDto); + } + + public ResponseEntity getById(Long itemId, Long userId) { + return get("/" + itemId, userId); + } + + public ResponseEntity getOwnerItems(Long userId, Integer from, Integer size) { + Map parameters = Map.of( + "from", from, + "size", size + ); + return get("?from={from}&size={size}", userId, parameters); + } + + public ResponseEntity search(String text, Integer from, Integer size) { + Map parameters = Map.of( + "text", text, + "from", from, + "size", size + ); + return get("/search?text={text}&from={from}&size={size}", null, parameters); + } + + public ResponseEntity addComment(Long userId, Long itemId, CommentDto commentDto) { + return post("/" + itemId + "/comment", userId, commentDto); + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java new file mode 100644 index 0000000..52e57b5 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -0,0 +1,66 @@ +package ru.practicum.shareit.item; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.item.dto.CommentDto; +import ru.practicum.shareit.item.dto.ItemDto; + +@Controller +@RequestMapping(path = "/items") +@RequiredArgsConstructor +@Slf4j +@Validated +public class ItemController { + private final ItemClient itemClient; + + @PostMapping + public ResponseEntity create(@RequestHeader("X-Sharer-User-Id") Long userId, + @Valid @RequestBody ItemDto itemDto) { + log.info("Creating item {} for user {}", itemDto, userId); + return itemClient.create(userId, itemDto); + } + + @PatchMapping("/{itemId}") + public ResponseEntity update(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long itemId, + @RequestBody ItemDto itemDto) { + log.info("Updating item {} for user {}", itemId, userId); + return itemClient.update(userId, itemId, itemDto); + } + + @GetMapping("/{itemId}") + public ResponseEntity getById(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long itemId) { + log.info("Get item {}", itemId); + return itemClient.getById(itemId, userId); + } + + @GetMapping + public ResponseEntity getOwnerItems( + @RequestHeader("X-Sharer-User-Id") Long userId, + @RequestParam(name = "from", defaultValue = "0") Integer from, + @RequestParam(name = "size", defaultValue = "10") Integer size) { + return itemClient.getOwnerItems(userId, from, size); + } + + @GetMapping("/search") + public ResponseEntity search( + @RequestParam String text, + @RequestParam(name = "from", defaultValue = "0") Integer from, + @RequestParam(name = "size", defaultValue = "10") Integer size) { + return itemClient.search(text, from, size); + } + + @PostMapping("/{itemId}/comment") + public ResponseEntity addComment(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long itemId, + @Valid @RequestBody CommentDto commentDto) { + log.info("Add comment to item {} from user {}", itemId, userId); + return itemClient.addComment(userId, itemId, commentDto); + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java new file mode 100644 index 0000000..b70688d --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java @@ -0,0 +1,15 @@ +package ru.practicum.shareit.item.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.*; +import lombok.experimental.FieldDefaults; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class CommentDto { + Long id; + @NotBlank(message = "Текст комментария не может быть пустым") + String text; +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java new file mode 100644 index 0000000..dc6f09f --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.item.dto; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.*; +import lombok.experimental.FieldDefaults; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ItemDto { + Long id; + @NotBlank + String name; + @NotBlank + String description; + @NotNull + Boolean available; + Long requestId; +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java b/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java new file mode 100644 index 0000000..fc85eb3 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java @@ -0,0 +1,48 @@ +package ru.practicum.shareit.request; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.util.DefaultUriBuilderFactory; +import ru.practicum.shareit.client.BaseClient; +import ru.practicum.shareit.request.dto.RequestDto; + +import java.util.Map; + +@Service +public class RequestClient extends BaseClient { + private static final String API_PREFIX = "/requests"; + + @Autowired + public RequestClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { + super(builder.uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()).build()); + } + + public ResponseEntity create(Long userId, RequestDto dto) { + return post("", userId, dto); + } + + public ResponseEntity getUserRequests(Long userId) { + return get("", userId); + } + + public ResponseEntity getAllRequests(Long userId) { + return get("/all", userId); + } + + public ResponseEntity getById(Long userId, Long requestId) { + return get("/" + requestId, userId); + } + + public ResponseEntity getAllRequests(Long userId, Integer from, Integer size) { + Map parameters = Map.of( + "from", from, + "size", size + ); + return get("/all?from={from}&size={size}", userId, parameters); + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java b/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java new file mode 100644 index 0000000..97fe261 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java @@ -0,0 +1,48 @@ +package ru.practicum.shareit.request; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.request.dto.RequestDto; + +@Controller +@RequestMapping(path = "/requests") +@RequiredArgsConstructor +@Slf4j +@Validated +public class RequestController { + private final RequestClient requestClient; + + @PostMapping + public ResponseEntity create(@RequestHeader("X-Sharer-User-Id") Long userId, + @Valid @RequestBody RequestDto dto) { + log.info("Creating request for user {}", userId); + return requestClient.create(userId, dto); + } + + @GetMapping + public ResponseEntity getUserRequests(@RequestHeader("X-Sharer-User-Id") Long userId) { + log.info("Get requests for user {}", userId); + return requestClient.getUserRequests(userId); + } + + @GetMapping("/all") + public ResponseEntity getAllRequests( + @RequestHeader("X-Sharer-User-Id") Long userId, + @RequestParam(name = "from", defaultValue = "0") Integer from, + @RequestParam(name = "size", defaultValue = "10") Integer size) { + log.info("Get all requests from={}, size={} for user {}", from, size, userId); + return requestClient.getAllRequests(userId, from, size); + } + + @GetMapping("/{requestId}") + public ResponseEntity getById(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long requestId) { + log.info("Get request {} for user {}", requestId, userId); + return requestClient.getById(userId, requestId); + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/request/dto/RequestDto.java b/gateway/src/main/java/ru/practicum/shareit/request/dto/RequestDto.java new file mode 100644 index 0000000..51d0c6e --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/dto/RequestDto.java @@ -0,0 +1,14 @@ +package ru.practicum.shareit.request.dto; + +import jakarta.validation.constraints.NotBlank; +import lombok.*; +import lombok.experimental.FieldDefaults; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class RequestDto { + @NotBlank(message = "Описание запроса не может быть пустым") + String description; +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java b/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java new file mode 100644 index 0000000..592c847 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java @@ -0,0 +1,46 @@ +package ru.practicum.shareit.user; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.util.DefaultUriBuilderFactory; +import ru.practicum.shareit.client.BaseClient; +import ru.practicum.shareit.user.dto.UserDto; + +@Service +public class UserClient extends BaseClient { + private static final String API_PREFIX = "/users"; + + @Autowired + public UserClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { + super( + builder + .uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()) + .build() + ); + } + + public ResponseEntity createUser(UserDto userDto) { + return post("", userDto); + } + + public ResponseEntity updateUser(Long userId, UserDto userDto) { + return patch("/" + userId, userDto); + } + + public ResponseEntity getUserById(Long userId) { + return get("/" + userId); + } + + public ResponseEntity getAllUsers() { + return get(""); + } + + public ResponseEntity deleteUser(Long userId) { + return delete("/" + userId); + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/user/UserController.java b/gateway/src/main/java/ru/practicum/shareit/user/UserController.java new file mode 100644 index 0000000..0299aeb --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserController.java @@ -0,0 +1,42 @@ +package ru.practicum.shareit.user; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.user.dto.UserDto; + +@Controller +@RequestMapping(path = "/users") +@RequiredArgsConstructor +@Validated +public class UserController { + private final UserClient userClient; + + @PostMapping + public ResponseEntity create(@Valid @RequestBody UserDto userDto) { + return userClient.createUser(userDto); + } + + @PatchMapping("/{id}") + public ResponseEntity update(@PathVariable Long id, @RequestBody UserDto userDto) { + return userClient.updateUser(id, userDto); + } + + @GetMapping("/{id}") + public ResponseEntity getById(@PathVariable Long id) { + return userClient.getUserById(id); + } + + @GetMapping + public ResponseEntity getAll() { + return userClient.getAllUsers(); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteById(@PathVariable Long id) { + return userClient.deleteUser(id); + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/user/dto/UserDto.java b/gateway/src/main/java/ru/practicum/shareit/user/dto/UserDto.java new file mode 100644 index 0000000..a87bbd1 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/dto/UserDto.java @@ -0,0 +1,19 @@ +package ru.practicum.shareit.user.dto; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import lombok.*; +import lombok.experimental.FieldDefaults; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class UserDto { + Long id; + @NotBlank + String name; + @NotBlank + @Email + String email; +} \ No newline at end of file diff --git a/gateway/src/main/resources/application.properties b/gateway/src/main/resources/application.properties new file mode 100644 index 0000000..6706685 --- /dev/null +++ b/gateway/src/main/resources/application.properties @@ -0,0 +1,2 @@ +server.port=8080 +shareit-server.url=http://localhost:9090 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 585edda..f3a5575 100644 --- a/pom.xml +++ b/pom.xml @@ -14,99 +14,40 @@ shareit 0.0.1-SNAPSHOT + pom + 21 - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-validation - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - - org.springframework.boot - spring-boot-starter-actuator - - - - org.postgresql - postgresql - runtime - - - - com.h2database - h2 - runtime - - - - org.projectlombok - lombok - true - - - - org.springframework.boot - spring-boot-starter-test - test - - + + gateway + server + - org.springframework.boot - spring-boot-maven-plugin + org.apache.maven.plugins + maven-checkstyle-plugin + 3.3.1 - - - org.projectlombok - lombok - - + checkstyle.xml + true + true + true + + + validate + validate + + check + + + - - - check - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 3.3.1 - - checkstyle.xml - true - true - true - - - - - check - - compile - - - - - - - \ No newline at end of file diff --git a/server/pom.xml b/server/pom.xml new file mode 100644 index 0000000..a7606fa --- /dev/null +++ b/server/pom.xml @@ -0,0 +1,48 @@ + + + + shareit + ru.practicum + 0.0.1-SNAPSHOT + + 4.0.0 + + shareit-server + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.postgresql + postgresql + runtime + + + com.h2database + h2 + runtime + + + org.projectlombok + lombok + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/ShareItApp.java b/server/src/main/java/ru/practicum/shareit/ShareItApp.java similarity index 100% rename from src/main/java/ru/practicum/shareit/ShareItApp.java rename to server/src/main/java/ru/practicum/shareit/ShareItApp.java diff --git a/src/main/java/ru/practicum/shareit/booking/BookingController.java b/server/src/main/java/ru/practicum/shareit/booking/BookingController.java similarity index 69% rename from src/main/java/ru/practicum/shareit/booking/BookingController.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingController.java index bc4a7fe..4b7eea3 100644 --- a/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -4,8 +4,6 @@ import org.springframework.web.bind.annotation.*; import ru.practicum.shareit.booking.dto.BookingDto; import ru.practicum.shareit.booking.dto.BookingResponseDto; - -import jakarta.validation.Valid; import java.util.List; @RestController @@ -17,7 +15,7 @@ public class BookingController { @PostMapping public BookingResponseDto create(@RequestHeader(USER_ID_HEADER) Long userId, - @Valid @RequestBody BookingDto bookingDto) { + @RequestBody BookingDto bookingDto) { return bookingService.create(userId, bookingDto); } @@ -36,13 +34,17 @@ public BookingResponseDto getById(@RequestHeader(USER_ID_HEADER) Long userId, @GetMapping public List getAllByBooker(@RequestHeader(USER_ID_HEADER) Long userId, - @RequestParam(defaultValue = "ALL") String state) { - return bookingService.getAllByBooker(userId, state); + @RequestParam(defaultValue = "ALL") String state, + @RequestParam(defaultValue = "0") Integer from, + @RequestParam(defaultValue = "10") Integer size) { + return bookingService.getAllByBooker(userId, state, from, size); } @GetMapping("/owner") public List getAllByOwner(@RequestHeader(USER_ID_HEADER) Long userId, - @RequestParam(defaultValue = "ALL") String state) { - return bookingService.getAllByOwner(userId, state); + @RequestParam(defaultValue = "ALL") String state, + @RequestParam(defaultValue = "0") Integer from, + @RequestParam(defaultValue = "10") Integer size) { + return bookingService.getAllByOwner(userId, state, from, size); } } \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/booking/BookingRepository.java b/server/src/main/java/ru/practicum/shareit/booking/BookingRepository.java similarity index 75% rename from src/main/java/ru/practicum/shareit/booking/BookingRepository.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingRepository.java index bfbb225..4d8c2c9 100644 --- a/src/main/java/ru/practicum/shareit/booking/BookingRepository.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingRepository.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.booking; +import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import ru.practicum.shareit.booking.model.Booking; @@ -10,34 +11,34 @@ public interface BookingRepository extends JpaRepository { @Query("select b from Booking b where b.booker.id = ?1 order by b.start desc") - List findUserBookings(Long bookerId); + List findUserBookings(Long bookerId, Pageable page); @Query("select b from Booking b where b.booker.id = ?1 and b.status = ?2 order by b.start desc") - List findUserStatus(Long bookerId, Status status); + List findUserStatus(Long bookerId, Status status, Pageable page); @Query("select b from Booking b where b.booker.id = ?1 and b.start <= ?2 and b.end >= ?2 order by b.start desc") - List findUserCurrent(Long bookerId, LocalDateTime now); + List findUserCurrent(Long bookerId, LocalDateTime now, Pageable page); @Query("select b from Booking b where b.booker.id = ?1 and b.end < ?2 order by b.start desc") - List findUserPast(Long bookerId, LocalDateTime now); + List findUserPast(Long bookerId, LocalDateTime now, Pageable page); @Query("select b from Booking b where b.booker.id = ?1 and b.start > ?2 order by b.start desc") - List findUserFuture(Long bookerId, LocalDateTime now); + List findUserFuture(Long bookerId, LocalDateTime now, Pageable page); @Query("select b from Booking b where b.item.owner.id = ?1 order by b.start desc") - List findOwnerBookings(Long ownerId); + List findOwnerBookings(Long ownerId, Pageable page); @Query("select b from Booking b where b.item.owner.id = ?1 and b.status = ?2 order by b.start desc") - List findOwnerStatus(Long ownerId, Status status); + List findOwnerStatus(Long ownerId, Status status, Pageable page); @Query("select b from Booking b where b.item.owner.id = ?1 and b.start <= ?2 and b.end >= ?2 order by b.start desc") - List findOwnerCurrent(Long ownerId, LocalDateTime now); + List findOwnerCurrent(Long ownerId, LocalDateTime now, Pageable page); @Query("select b from Booking b where b.item.owner.id = ?1 and b.end < ?2 order by b.start desc") - List findOwnerPast(Long ownerId, LocalDateTime now); + List findOwnerPast(Long ownerId, LocalDateTime now, Pageable page); @Query("select b from Booking b where b.item.owner.id = ?1 and b.start > ?2 order by b.start desc") - List findOwnerFuture(Long ownerId, LocalDateTime now); + List findOwnerFuture(Long ownerId, LocalDateTime now, Pageable page); @Query("select b from Booking b where b.item.id = ?1 order by b.start asc") List findItemBookings(Long itemId); diff --git a/src/main/java/ru/practicum/shareit/booking/BookingService.java b/server/src/main/java/ru/practicum/shareit/booking/BookingService.java similarity index 88% rename from src/main/java/ru/practicum/shareit/booking/BookingService.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingService.java index 935b068..635faab 100644 --- a/src/main/java/ru/practicum/shareit/booking/BookingService.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingService.java @@ -2,18 +2,16 @@ import ru.practicum.shareit.booking.dto.BookingDto; import ru.practicum.shareit.booking.dto.BookingResponseDto; - import java.util.List; public interface BookingService { - BookingResponseDto create(Long userId, BookingDto bookingDto); BookingResponseDto approve(Long userId, Long bookingId, boolean approved); BookingResponseDto getById(Long userId, Long bookingId); - List getAllByBooker(Long userId, String state); + List getAllByBooker(Long userId, String state, Integer from, Integer size); - List getAllByOwner(Long userId, String state); + List getAllByOwner(Long userId, String state, Integer from, Integer size); } \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java b/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java similarity index 58% rename from src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java index 7cf0a92..c5e8686 100644 --- a/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java @@ -1,6 +1,8 @@ package ru.practicum.shareit.booking; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.practicum.shareit.booking.dto.BookingDto; @@ -8,12 +10,14 @@ import ru.practicum.shareit.booking.mapper.BookingMapper; import ru.practicum.shareit.booking.model.Booking; import ru.practicum.shareit.booking.model.Status; +import ru.practicum.shareit.exception.ForbiddenException; import ru.practicum.shareit.exception.NotFoundException; import ru.practicum.shareit.exception.ValidationException; import ru.practicum.shareit.item.ItemRepository; import ru.practicum.shareit.item.model.Item; import ru.practicum.shareit.user.User; import ru.practicum.shareit.user.UserRepository; + import java.time.LocalDateTime; import java.util.List; import java.util.stream.Collectors; @@ -31,11 +35,12 @@ public class BookingServiceImpl implements BookingService { public BookingResponseDto create(Long userId, BookingDto bookingDto) { User booker = userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User not found")); Item item = itemRepository.findById(bookingDto.getItemId()).orElseThrow(() -> new NotFoundException("Item not found")); - if (!item.getAvailable()) throw new ValidationException("Not available"); - if (item.getOwner().getId().equals(userId)) throw new NotFoundException("Owner cannot book"); + if (!item.getAvailable()) throw new ValidationException("Вещь недоступна"); + if (item.getOwner().getId().equals(userId)) throw new NotFoundException("Владелец не может бронировать"); if (bookingDto.getEnd().isBefore(bookingDto.getStart()) || bookingDto.getEnd().equals(bookingDto.getStart())) { - throw new ValidationException("Wrong dates"); + throw new ValidationException("Неверные даты"); } + Booking booking = new Booking(); booking.setStart(bookingDto.getStart()); booking.setEnd(bookingDto.getEnd()); @@ -48,9 +53,17 @@ public BookingResponseDto create(Long userId, BookingDto bookingDto) { @Transactional @Override public BookingResponseDto approve(Long userId, Long bookingId, boolean approved) { - Booking booking = bookingRepository.findById(bookingId).orElseThrow(() -> new NotFoundException("Not found")); - if (!booking.getItem().getOwner().getId().equals(userId)) throw new ValidationException("Not an owner"); - if (booking.getStatus() != Status.WAITING) throw new ValidationException("Already changed"); + Booking booking = bookingRepository.findById(bookingId) + .orElseThrow(() -> new NotFoundException("Booking not found")); + + if (!booking.getItem().getOwner().getId().equals(userId)) { + throw new ForbiddenException("Not an owner"); + } + + if (booking.getStatus() == Status.APPROVED) { + throw new ValidationException("Already approved"); + } + booking.setStatus(approved ? Status.APPROVED : Status.REJECTED); return BookingMapper.toBookingResponse(bookingRepository.save(booking)); } @@ -65,36 +78,36 @@ public BookingResponseDto getById(Long userId, Long bookingId) { } @Override - public List getAllByBooker(Long userId, String state) { + public List getAllByBooker(Long userId, String state, Integer from, Integer size) { userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User not found")); + Pageable page = PageRequest.of(from / size, size); LocalDateTime now = LocalDateTime.now(); - List bookings; - switch (state.toUpperCase()) { - case "ALL": bookings = bookingRepository.findUserBookings(userId); break; - case "CURRENT": bookings = bookingRepository.findUserCurrent(userId, now); break; - case "PAST": bookings = bookingRepository.findUserPast(userId, now); break; - case "FUTURE": bookings = bookingRepository.findUserFuture(userId, now); break; - case "WAITING": bookings = bookingRepository.findUserStatus(userId, Status.WAITING); break; - case "REJECTED": bookings = bookingRepository.findUserStatus(userId, Status.REJECTED); break; - default: throw new ValidationException("Unknown state: " + state); - } - return bookings.stream().map(BookingMapper::toBookingResponse).collect(Collectors.toList()); + List list = switch (state.toUpperCase()) { + case "ALL" -> bookingRepository.findUserBookings(userId, page); + case "CURRENT" -> bookingRepository.findUserCurrent(userId, now, page); + case "PAST" -> bookingRepository.findUserPast(userId, now, page); + case "FUTURE" -> bookingRepository.findUserFuture(userId, now, page); + case "WAITING" -> bookingRepository.findUserStatus(userId, Status.WAITING, page); + case "REJECTED" -> bookingRepository.findUserStatus(userId, Status.REJECTED, page); + default -> throw new ValidationException("Unknown state: " + state); + }; + return list.stream().map(BookingMapper::toBookingResponse).collect(Collectors.toList()); } @Override - public List getAllByOwner(Long userId, String state) { + public List getAllByOwner(Long userId, String state, Integer from, Integer size) { userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User not found")); + Pageable page = PageRequest.of(from / size, size); LocalDateTime now = LocalDateTime.now(); - List bookings; - switch (state.toUpperCase()) { - case "ALL": bookings = bookingRepository.findOwnerBookings(userId); break; - case "CURRENT": bookings = bookingRepository.findOwnerCurrent(userId, now); break; - case "PAST": bookings = bookingRepository.findOwnerPast(userId, now); break; - case "FUTURE": bookings = bookingRepository.findOwnerFuture(userId, now); break; - case "WAITING": bookings = bookingRepository.findOwnerStatus(userId, Status.WAITING); break; - case "REJECTED": bookings = bookingRepository.findOwnerStatus(userId, Status.REJECTED); break; - default: throw new ValidationException("Unknown state: " + state); - } - return bookings.stream().map(BookingMapper::toBookingResponse).collect(Collectors.toList()); + List list = switch (state.toUpperCase()) { + case "ALL" -> bookingRepository.findOwnerBookings(userId, page); + case "CURRENT" -> bookingRepository.findOwnerCurrent(userId, now, page); + case "PAST" -> bookingRepository.findOwnerPast(userId, now, page); + case "FUTURE" -> bookingRepository.findOwnerFuture(userId, now, page); + case "WAITING" -> bookingRepository.findOwnerStatus(userId, Status.WAITING, page); + case "REJECTED" -> bookingRepository.findOwnerStatus(userId, Status.REJECTED, page); + default -> throw new ValidationException("Unknown state: " + state); + }; + return list.stream().map(BookingMapper::toBookingResponse).collect(Collectors.toList()); } } \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java new file mode 100644 index 0000000..b7e8adc --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java @@ -0,0 +1,15 @@ +package ru.practicum.shareit.booking.dto; + +import lombok.*; +import lombok.experimental.FieldDefaults; +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class BookingDto { + Long itemId; + LocalDateTime start; + LocalDateTime end; +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingResponseDto.java b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingResponseDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/dto/BookingResponseDto.java rename to server/src/main/java/ru/practicum/shareit/booking/dto/BookingResponseDto.java diff --git a/src/main/java/ru/practicum/shareit/booking/mapper/BookingMapper.java b/server/src/main/java/ru/practicum/shareit/booking/mapper/BookingMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/mapper/BookingMapper.java rename to server/src/main/java/ru/practicum/shareit/booking/mapper/BookingMapper.java diff --git a/src/main/java/ru/practicum/shareit/booking/model/Booking.java b/server/src/main/java/ru/practicum/shareit/booking/model/Booking.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/model/Booking.java rename to server/src/main/java/ru/practicum/shareit/booking/model/Booking.java diff --git a/src/main/java/ru/practicum/shareit/booking/model/Status.java b/server/src/main/java/ru/practicum/shareit/booking/model/Status.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/model/Status.java rename to server/src/main/java/ru/practicum/shareit/booking/model/Status.java diff --git a/src/main/java/ru/practicum/shareit/exception/ConflictException.java b/server/src/main/java/ru/practicum/shareit/exception/ConflictException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/ConflictException.java rename to server/src/main/java/ru/practicum/shareit/exception/ConflictException.java diff --git a/server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java new file mode 100644 index 0000000..00c8610 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -0,0 +1,34 @@ +package ru.practicum.shareit.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import java.util.Map; + +@RestControllerAdvice +public class ErrorHandler { + @ExceptionHandler(NotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public Map handleNotFound(final NotFoundException e) { + return Map.of("error", e.getMessage()); + } + + @ExceptionHandler(ValidationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Map handleValidation(final ValidationException e) { + return Map.of("error", e.getMessage()); + } + + @ExceptionHandler(ConflictException.class) + @ResponseStatus(HttpStatus.CONFLICT) + public Map handleConflict(final ConflictException e) { + return Map.of("error", e.getMessage()); + } + + @ExceptionHandler(ForbiddenException.class) + @ResponseStatus(HttpStatus.FORBIDDEN) + public Map handleForbidden(final ForbiddenException e) { + return Map.of("error", e.getMessage()); + } +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/exception/ForbiddenException.java b/server/src/main/java/ru/practicum/shareit/exception/ForbiddenException.java new file mode 100644 index 0000000..d8df923 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/ForbiddenException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class ForbiddenException extends RuntimeException { + public ForbiddenException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/exception/NotFoundException.java b/server/src/main/java/ru/practicum/shareit/exception/NotFoundException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/NotFoundException.java rename to server/src/main/java/ru/practicum/shareit/exception/NotFoundException.java diff --git a/src/main/java/ru/practicum/shareit/exception/ValidationException.java b/server/src/main/java/ru/practicum/shareit/exception/ValidationException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/ValidationException.java rename to server/src/main/java/ru/practicum/shareit/exception/ValidationException.java diff --git a/src/main/java/ru/practicum/shareit/item/CommentRepository.java b/server/src/main/java/ru/practicum/shareit/item/CommentRepository.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/CommentRepository.java rename to server/src/main/java/ru/practicum/shareit/item/CommentRepository.java diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemController.java b/server/src/main/java/ru/practicum/shareit/item/ItemController.java new file mode 100644 index 0000000..15599e3 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -0,0 +1,49 @@ +package ru.practicum.shareit.item; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.item.dto.CommentDto; +import ru.practicum.shareit.item.dto.ItemDto; +import java.util.List; + +@RestController +@RequestMapping("/items") +@RequiredArgsConstructor +public class ItemController { + private final ItemService itemService; + private static final String USER_ID_HEADER = "X-Sharer-User-Id"; + + @PostMapping + public ItemDto create(@RequestHeader(USER_ID_HEADER) Long userId, @RequestBody ItemDto itemDto) { + return itemService.create(userId, itemDto); + } + + @PatchMapping("/{itemId}") + public ItemDto update(@RequestHeader(USER_ID_HEADER) Long userId, @PathVariable Long itemId, @RequestBody ItemDto itemDto) { + return itemService.update(userId, itemId, itemDto); + } + + @GetMapping("/{itemId}") + public ItemDto getById(@RequestHeader(USER_ID_HEADER) Long userId, @PathVariable Long itemId) { + return itemService.getById(itemId, userId); + } + + @GetMapping + public List getAll(@RequestHeader(USER_ID_HEADER) Long userId, + @RequestParam(defaultValue = "0") Integer from, + @RequestParam(defaultValue = "10") Integer size) { + return itemService.getAllByOwnerId(userId, from, size); + } + + @GetMapping("/search") + public List search(@RequestParam String text, + @RequestParam(defaultValue = "0") Integer from, + @RequestParam(defaultValue = "10") Integer size) { + return itemService.search(text, from, size); + } + + @PostMapping("/{itemId}/comment") + public CommentDto createComment(@RequestHeader(USER_ID_HEADER) Long userId, @PathVariable Long itemId, @RequestBody CommentDto commentDto) { + return itemService.createComment(userId, itemId, commentDto); + } +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemRepository.java b/server/src/main/java/ru/practicum/shareit/item/ItemRepository.java new file mode 100644 index 0000000..367ba2b --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemRepository.java @@ -0,0 +1,20 @@ +package ru.practicum.shareit.item; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import ru.practicum.shareit.item.model.Item; +import java.util.List; + +public interface ItemRepository extends JpaRepository { + @Query("select i from Item i where i.owner.id = ?1") + List findByOwner(Long userId, Pageable page); + + @Query("select i from Item i " + + "where i.available = true " + + "and (lower(i.name) like lower(concat('%', ?1, '%')) " + + "or lower(i.description) like lower(concat('%', ?1, '%')))") + List search(String text, Pageable page); + + List findAllByRequestId(Long requestId); +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/item/ItemService.java b/server/src/main/java/ru/practicum/shareit/item/ItemService.java similarity index 75% rename from src/main/java/ru/practicum/shareit/item/ItemService.java rename to server/src/main/java/ru/practicum/shareit/item/ItemService.java index c907238..9d4a853 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemService.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemService.java @@ -6,16 +6,16 @@ import java.util.List; public interface ItemService { - ItemDto create(Long userId, ItemDto itemDto); ItemDto update(Long userId, Long itemId, ItemDto itemDto); ItemDto getById(Long itemId, Long userId); - List getAllByOwnerId(Long ownerId); + List getAllByOwnerId(Long ownerId, Integer from, Integer size); - List search(String text); + List search(String text, Integer from, Integer size); CommentDto createComment(Long userId, Long itemId, CommentDto commentDto); + } \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java b/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java similarity index 82% rename from src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java rename to server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java index 582c770..e02b4bb 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java @@ -1,6 +1,8 @@ package ru.practicum.shareit.item; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import ru.practicum.shareit.booking.BookingRepository; @@ -34,6 +36,11 @@ public class ItemServiceImpl implements ItemService { public ItemDto create(Long userId, ItemDto itemDto) { User owner = userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User not found")); Item item = ItemMapper.toItem(itemDto, owner); + + if (itemDto.getRequestId() != null) { + item.setRequestId(itemDto.getRequestId()); + } + return ItemMapper.toItemDto(itemRepository.save(item)); } @@ -61,8 +68,9 @@ public ItemDto getById(Long itemId, Long userId) { } @Override - public List getAllByOwnerId(Long ownerId) { - return itemRepository.findAllByOwnerId(ownerId).stream().map(item -> { + public List getAllByOwnerId(Long ownerId, Integer from, Integer size) { + Pageable page = PageRequest.of(from / size, size); + return itemRepository.findByOwner(ownerId, page).stream().map(item -> { ItemDto dto = ItemMapper.toItemDto(item); dto.setComments(commentRepository.findAllByItemId(item.getId()).stream() .map(CommentMapper::toCommentDto).collect(Collectors.toList())); @@ -72,9 +80,10 @@ public List getAllByOwnerId(Long ownerId) { } @Override - public List search(String text) { + public List search(String text, Integer from, Integer size) { if (text == null || text.isBlank()) return Collections.emptyList(); - return itemRepository.searchByText(text).stream() + Pageable page = PageRequest.of(from / size, size); + return itemRepository.search(text, page).stream() .map(ItemMapper::toItemDto).collect(Collectors.toList()); } @@ -83,10 +92,16 @@ public List search(String text) { public CommentDto createComment(Long userId, Long itemId, CommentDto commentDto) { User user = userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User not found")); Item item = itemRepository.findById(itemId).orElseThrow(() -> new NotFoundException("Item not found")); - List userBookings = bookingRepository.findUserPast(userId, LocalDateTime.now()); - boolean hasFinishedBooking = userBookings.stream() - .anyMatch(b -> b.getItem().getId().equals(itemId) && b.getStatus() == Status.APPROVED); - if (!hasFinishedBooking) throw new ValidationException("No finished bookings"); + + List userBookings = bookingRepository.findUserBookings(userId, Pageable.unpaged()); + + boolean canComment = userBookings.stream() + .anyMatch(b -> b.getItem().getId().equals(itemId) + && b.getStatus() == Status.APPROVED + && b.getEnd().isBefore(LocalDateTime.now())); + + if (!canComment) throw new ValidationException("Ошибка валидации"); + Comment comment = new Comment(); comment.setText(commentDto.getText()); comment.setItem(item); diff --git a/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java similarity index 83% rename from src/main/java/ru/practicum/shareit/item/dto/CommentDto.java rename to server/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java index f50e263..a277516 100644 --- a/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java +++ b/server/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java @@ -1,9 +1,7 @@ package ru.practicum.shareit.item.dto; -import jakarta.validation.constraints.NotBlank; import lombok.*; import lombok.experimental.FieldDefaults; - import java.time.LocalDateTime; @Data @@ -12,7 +10,6 @@ @FieldDefaults(level = AccessLevel.PRIVATE) public class CommentDto { Long id; - @NotBlank String text; String authorName; LocalDateTime created; diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java similarity index 81% rename from src/main/java/ru/practicum/shareit/item/dto/ItemDto.java rename to server/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java index 3a66de4..b6d5621 100644 --- a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java +++ b/server/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java @@ -1,7 +1,5 @@ package ru.practicum.shareit.item.dto; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import lombok.*; import lombok.experimental.FieldDefaults; import java.util.List; @@ -12,18 +10,18 @@ @FieldDefaults(level = AccessLevel.PRIVATE) public class ItemDto { Long id; - @NotBlank String name; - @NotBlank String description; - @NotNull Boolean available; BookingShortDto lastBooking; BookingShortDto nextBooking; List comments; + Long requestId; + Long ownerId; @Data @AllArgsConstructor + @NoArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE) public static class BookingShortDto { Long id; diff --git a/src/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java b/server/src/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java rename to server/src/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java diff --git a/server/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java b/server/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java new file mode 100644 index 0000000..c6b1670 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java @@ -0,0 +1,31 @@ +package ru.practicum.shareit.item.mapper; + +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.User; +import java.util.ArrayList; + +public class ItemMapper { + public static ItemDto toItemDto(Item item) { + return new ItemDto( + item.getId(), + item.getName(), + item.getDescription(), + item.getAvailable(), + null, null, new ArrayList<>(), + item.getRequestId(), + item.getOwner() != null ? item.getOwner().getId() : null + ); + } + + public static Item toItem(ItemDto dto, User owner) { + Item item = new Item(); + item.setId(dto.getId()); + item.setName(dto.getName()); + item.setDescription(dto.getDescription()); + item.setAvailable(dto.getAvailable()); + item.setOwner(owner); + item.setRequestId(dto.getRequestId()); + return item; + } +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/item/model/Comment.java b/server/src/main/java/ru/practicum/shareit/item/model/Comment.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/model/Comment.java rename to server/src/main/java/ru/practicum/shareit/item/model/Comment.java diff --git a/src/main/java/ru/practicum/shareit/item/model/Item.java b/server/src/main/java/ru/practicum/shareit/item/model/Item.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/model/Item.java rename to server/src/main/java/ru/practicum/shareit/item/model/Item.java diff --git a/server/src/main/java/ru/practicum/shareit/request/ItemRequest.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequest.java new file mode 100644 index 0000000..fb3ef03 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/ItemRequest.java @@ -0,0 +1,29 @@ +package ru.practicum.shareit.request.model; + +import jakarta.persistence.*; +import lombok.*; +import lombok.experimental.FieldDefaults; +import ru.practicum.shareit.user.User; +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +@Entity +@Table(name = "requests") +public class ItemRequest { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + Long id; + + @Column(nullable = false) + String description; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "requestor_id", nullable = false) + User requestor; + + @Column(nullable = false) + LocalDateTime created; +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java new file mode 100644 index 0000000..522ba7f --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java @@ -0,0 +1,39 @@ +package ru.practicum.shareit.request; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.request.dto.ItemRequestDto; + +import java.util.List; + +@RestController +@RequestMapping(path = "/requests") +@RequiredArgsConstructor +public class ItemRequestController { + private final ItemRequestService requestService; + private static final String USER_ID_HEADER = "X-Sharer-User-Id"; + + @PostMapping + public ItemRequestDto create(@RequestHeader(USER_ID_HEADER) Long userId, + @RequestBody ItemRequestDto dto) { + return requestService.create(userId, dto); + } + + @GetMapping + public List getUserRequests(@RequestHeader(USER_ID_HEADER) Long userId) { + return requestService.getUserRequests(userId); + } + + @GetMapping("/all") + public List getAllRequests(@RequestHeader(USER_ID_HEADER) Long userId, + @RequestParam(defaultValue = "0") Integer from, + @RequestParam(defaultValue = "10") Integer size) { + return requestService.getAllRequests(userId, from, size); + } + + @GetMapping("/{requestId}") + public ItemRequestDto getById(@RequestHeader(USER_ID_HEADER) Long userId, + @PathVariable Long requestId) { + return requestService.getById(userId, requestId); + } +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/request/ItemRequestRepository.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequestRepository.java new file mode 100644 index 0000000..13baebe --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/ItemRequestRepository.java @@ -0,0 +1,14 @@ +package ru.practicum.shareit.request; + +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import ru.practicum.shareit.request.model.ItemRequest; +import java.util.List; + +public interface ItemRequestRepository extends JpaRepository { + List findAllByRequestorIdOrderByCreatedDesc(Long userId); + + @Query("select r from ItemRequest r where r.requestor.id <> ?1 order by r.created desc") + List findOthers(Long userId, Pageable page); +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java new file mode 100644 index 0000000..1f62b5b --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/ItemRequestService.java @@ -0,0 +1,14 @@ +package ru.practicum.shareit.request; + +import ru.practicum.shareit.request.dto.ItemRequestDto; +import java.util.List; + +public interface ItemRequestService { + ItemRequestDto create(Long userId, ItemRequestDto dto); + + List getUserRequests(Long userId); + + List getAllRequests(Long userId, Integer from, Integer size); + + ItemRequestDto getById(Long userId, Long requestId); +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/request/ItemRequestServiceImpl.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequestServiceImpl.java new file mode 100644 index 0000000..405da9c --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/ItemRequestServiceImpl.java @@ -0,0 +1,77 @@ +package ru.practicum.shareit.request; + +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.item.ItemRepository; +import ru.practicum.shareit.item.mapper.ItemMapper; +import ru.practicum.shareit.request.dto.ItemRequestDto; +import ru.practicum.shareit.request.model.ItemRequest; +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.UserRepository; +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class ItemRequestServiceImpl implements ItemRequestService { + private final ItemRequestRepository requestRepository; + private final UserRepository userRepository; + private final ItemRepository itemRepository; + + @Transactional + @Override + public ItemRequestDto create(Long userId, ItemRequestDto dto) { + User user = userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User not found")); + ItemRequest request = new ItemRequest(); + request.setDescription(dto.getDescription()); + request.setRequestor(user); + request.setCreated(LocalDateTime.now()); + return toDto(requestRepository.save(request)); + } + + @Override + public List getUserRequests(Long userId) { + userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User not found")); + return requestRepository.findAllByRequestorIdOrderByCreatedDesc(userId) + .stream().map(this::toDto).collect(Collectors.toList()); + } + + @Override + public List getAllRequests(Long userId, Integer from, Integer size) { + Pageable page = PageRequest.of(from / size, size); + return requestRepository.findOthers(userId, page) + .stream().map(this::toDto).collect(Collectors.toList()); + } + + @Override + public ItemRequestDto getById(Long userId, Long requestId) { + userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User not found")); + ItemRequest request = requestRepository.findById(requestId) + .orElseThrow(() -> new NotFoundException("Request not found")); + return toDto(request); + } + + private ItemRequestDto toDto(ItemRequest request) { + ItemRequestDto dto = new ItemRequestDto(); + dto.setId(request.getId()); + dto.setDescription(request.getDescription()); + dto.setCreated(request.getCreated()); + + + List items = itemRepository.findAllByRequestId(request.getId()); + if (items != null && !items.isEmpty()) { + dto.setItems(items.stream().map(ItemMapper::toItemDto).collect(Collectors.toList())); + } else { + dto.setItems(new ArrayList<>()); + } + + return dto; + } +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java b/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java new file mode 100644 index 0000000..1d475e4 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java @@ -0,0 +1,18 @@ +package ru.practicum.shareit.request.dto; + +import lombok.*; +import lombok.experimental.FieldDefaults; +import ru.practicum.shareit.item.dto.ItemDto; +import java.time.LocalDateTime; +import java.util.List; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class ItemRequestDto { + Long id; + String description; + LocalDateTime created; + List items; +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/user/User.java b/server/src/main/java/ru/practicum/shareit/user/User.java similarity index 54% rename from src/main/java/ru/practicum/shareit/user/User.java rename to server/src/main/java/ru/practicum/shareit/user/User.java index a976191..9c00767 100644 --- a/src/main/java/ru/practicum/shareit/user/User.java +++ b/server/src/main/java/ru/practicum/shareit/user/User.java @@ -1,23 +1,15 @@ package ru.practicum.shareit.user; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Table; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; +import jakarta.persistence.*; +import lombok.*; import lombok.experimental.FieldDefaults; @Data -@Entity -@Table(name = "users") -@NoArgsConstructor @AllArgsConstructor +@NoArgsConstructor @FieldDefaults(level = AccessLevel.PRIVATE) +@Entity +@Table(name = "users") public class User { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/ru/practicum/shareit/user/UserController.java b/server/src/main/java/ru/practicum/shareit/user/UserController.java similarity index 90% rename from src/main/java/ru/practicum/shareit/user/UserController.java rename to server/src/main/java/ru/practicum/shareit/user/UserController.java index d9f7da2..002d783 100644 --- a/src/main/java/ru/practicum/shareit/user/UserController.java +++ b/server/src/main/java/ru/practicum/shareit/user/UserController.java @@ -3,8 +3,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import ru.practicum.shareit.user.dto.UserDto; - -import jakarta.validation.Valid; import java.util.List; @RestController @@ -14,7 +12,7 @@ public class UserController { private final UserService userService; @PostMapping - public UserDto create(@Valid @RequestBody UserDto userDto) { + public UserDto create(@RequestBody UserDto userDto) { return userService.create(userDto); } diff --git a/src/main/java/ru/practicum/shareit/user/UserRepository.java b/server/src/main/java/ru/practicum/shareit/user/UserRepository.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/UserRepository.java rename to server/src/main/java/ru/practicum/shareit/user/UserRepository.java diff --git a/src/main/java/ru/practicum/shareit/user/UserService.java b/server/src/main/java/ru/practicum/shareit/user/UserService.java similarity index 99% rename from src/main/java/ru/practicum/shareit/user/UserService.java rename to server/src/main/java/ru/practicum/shareit/user/UserService.java index 993b0a3..e0d9f76 100644 --- a/src/main/java/ru/practicum/shareit/user/UserService.java +++ b/server/src/main/java/ru/practicum/shareit/user/UserService.java @@ -4,7 +4,6 @@ import java.util.List; public interface UserService { - UserDto create(UserDto userDto); UserDto update(Long id, UserDto userDto); diff --git a/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java b/server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java similarity index 55% rename from src/main/java/ru/practicum/shareit/user/UserServiceImpl.java rename to server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java index 78c835d..33a5f22 100644 --- a/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java @@ -9,7 +9,6 @@ import ru.practicum.shareit.user.mapper.UserMapper; import java.util.List; -import java.util.Optional; import java.util.stream.Collectors; @Service @@ -21,8 +20,7 @@ public class UserServiceImpl implements UserService { @Transactional @Override public UserDto create(UserDto userDto) { - Optional existingUser = userRepository.findByEmail(userDto.getEmail()); - if (existingUser.isPresent()) { + if (userRepository.findByEmail(userDto.getEmail()).isPresent()) { throw new ConflictException("Email уже используется"); } User user = UserMapper.toUser(userDto); @@ -32,35 +30,29 @@ public UserDto create(UserDto userDto) { @Transactional @Override public UserDto update(Long id, UserDto userDto) { - User userToUpdate = userRepository.findById(id) - .orElseThrow(() -> new NotFoundException("Пользователь не найден")); - + User user = userRepository.findById(id) + .orElseThrow(() -> new NotFoundException("User not found")); if (userDto.getEmail() != null && !userDto.getEmail().isBlank()) { - Optional userWithSameEmail = userRepository.findByEmail(userDto.getEmail()); - if (userWithSameEmail.isPresent() && !userWithSameEmail.get().getId().equals(id)) { - throw new ConflictException("Email уже используется другим пользователем"); - } - userToUpdate.setEmail(userDto.getEmail()); - } - if (userDto.getName() != null && !userDto.getName().isBlank()) { - userToUpdate.setName(userDto.getName()); + userRepository.findByEmail(userDto.getEmail()) + .ifPresent(u -> { + if (!u.getId().equals(id)) throw new ConflictException("Email busy"); + }); + user.setEmail(userDto.getEmail()); } - - return UserMapper.toUserDto(userRepository.save(userToUpdate)); + if (userDto.getName() != null) user.setName(userDto.getName()); + return UserMapper.toUserDto(userRepository.save(user)); } @Override public UserDto getById(Long id) { - User user = userRepository.findById(id) - .orElseThrow(() -> new NotFoundException("Пользователь не найден")); - return UserMapper.toUserDto(user); + return UserMapper.toUserDto(userRepository.findById(id) + .orElseThrow(() -> new NotFoundException("User not found"))); } @Override public List getAll() { return userRepository.findAll().stream() - .map(UserMapper::toUserDto) - .collect(Collectors.toList()); + .map(UserMapper::toUserDto).collect(Collectors.toList()); } @Transactional diff --git a/server/src/main/java/ru/practicum/shareit/user/dto/UserDto.java b/server/src/main/java/ru/practicum/shareit/user/dto/UserDto.java new file mode 100644 index 0000000..ff30d85 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/dto/UserDto.java @@ -0,0 +1,14 @@ +package ru.practicum.shareit.user.dto; + +import lombok.*; +import lombok.experimental.FieldDefaults; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@FieldDefaults(level = AccessLevel.PRIVATE) +public class UserDto { + Long id; + String name; + String email; +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/user/mapper/UserMapper.java b/server/src/main/java/ru/practicum/shareit/user/mapper/UserMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/mapper/UserMapper.java rename to server/src/main/java/ru/practicum/shareit/user/mapper/UserMapper.java index 26b1b95..d4913ad 100644 --- a/src/main/java/ru/practicum/shareit/user/mapper/UserMapper.java +++ b/server/src/main/java/ru/practicum/shareit/user/mapper/UserMapper.java @@ -1,7 +1,7 @@ package ru.practicum.shareit.user.mapper; -import ru.practicum.shareit.user.dto.UserDto; import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.dto.UserDto; public class UserMapper { public static UserDto toUserDto(User user) { diff --git a/src/main/resources/application-test.properties b/server/src/main/resources/application-test.properties similarity index 100% rename from src/main/resources/application-test.properties rename to server/src/main/resources/application-test.properties diff --git a/src/main/resources/application.properties b/server/src/main/resources/application.properties similarity index 70% rename from src/main/resources/application.properties rename to server/src/main/resources/application.properties index 56b6039..4b4222e 100644 --- a/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -1,14 +1,14 @@ -server.port=8080 +server.port=9090 spring.datasource.url=${SPRING_DATASOURCE_URL:jdbc:h2:mem:shareit;DB_CLOSE_DELAY=-1} spring.datasource.username=${SPRING_DATASOURCE_USERNAME:sa} spring.datasource.password=${SPRING_DATASOURCE_PASSWORD:} - -spring.jpa.hibernate.ddl-auto=update +spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.show-sql=false spring.sql.init.mode=always management.endpoints.web.exposure.include=health -management.endpoint.health.show-details=always \ No newline at end of file +management.endpoint.health.show-details=always +logging.level.web=DEBUG diff --git a/src/main/resources/schema.sql b/server/src/main/resources/schema.sql similarity index 82% rename from src/main/resources/schema.sql rename to server/src/main/resources/schema.sql index 2e2e066..46da8ae 100644 --- a/src/main/resources/schema.sql +++ b/server/src/main/resources/schema.sql @@ -38,4 +38,12 @@ CREATE TABLE comments ( created TIMESTAMP WITHOUT TIME ZONE NOT NULL, CONSTRAINT fk_comment_item FOREIGN KEY (item_id) REFERENCES items (id) ON DELETE CASCADE, CONSTRAINT fk_comment_author FOREIGN KEY (author_id) REFERENCES users (id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS requests ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + description VARCHAR(1000) NOT NULL, + requestor_id BIGINT NOT NULL, + created TIMESTAMP WITHOUT TIME ZONE NOT NULL, + CONSTRAINT fk_request_requestor FOREIGN KEY (requestor_id) REFERENCES users(id) ON DELETE CASCADE ); \ No newline at end of file diff --git a/src/test/java/ru/practicum/shareit/ShareItTests.java b/server/src/test/java/ru/practicum/shareit/ShareItTests.java similarity index 100% rename from src/test/java/ru/practicum/shareit/ShareItTests.java rename to server/src/test/java/ru/practicum/shareit/ShareItTests.java diff --git a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java deleted file mode 100644 index d4d9f03..0000000 --- a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java +++ /dev/null @@ -1,50 +0,0 @@ -package ru.practicum.shareit.exception; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -import java.util.Map; - -@Slf4j -@RestControllerAdvice -public class ErrorHandler { - - @ExceptionHandler - @ResponseStatus(HttpStatus.NOT_FOUND) - public Map handleNotFound(final NotFoundException e) { - log.error("404 Not Found: {}", e.getMessage()); - return Map.of("error", e.getMessage()); - } - - @ExceptionHandler - @ResponseStatus(HttpStatus.CONFLICT) - public Map handleConflict(final ConflictException e) { - log.error("409 Conflict: {}", e.getMessage()); - return Map.of("error", e.getMessage()); - } - - @ExceptionHandler - @ResponseStatus(HttpStatus.BAD_REQUEST) - public Map handleValidation(final ValidationException e) { - log.error("400 Bad Request (Validation): {}", e.getMessage()); - return Map.of("error", e.getMessage()); - } - - @ExceptionHandler - @ResponseStatus(HttpStatus.BAD_REQUEST) - public Map handleMethodArgumentNotValid(final MethodArgumentNotValidException e) { - log.error("400 Bad Request (Argument Not Valid): {}", e.getMessage()); - return Map.of("error", "Ошибка валидации данных"); - } - - @ExceptionHandler - @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) - public Map handleThrowable(final Throwable e) { - log.error("500 Internal Server Error: ", e); - return Map.of("error", "Произошла непредвиденная ошибка"); - } -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/item/ItemController.java b/src/main/java/ru/practicum/shareit/item/ItemController.java deleted file mode 100644 index d667bda..0000000 --- a/src/main/java/ru/practicum/shareit/item/ItemController.java +++ /dev/null @@ -1,53 +0,0 @@ -package ru.practicum.shareit.item; - -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; -import ru.practicum.shareit.item.dto.CommentDto; -import ru.practicum.shareit.item.dto.ItemDto; - -import jakarta.validation.Valid; -import java.util.List; - -@RestController -@RequestMapping("/items") -@RequiredArgsConstructor -public class ItemController { - private final ItemService itemService; - private static final String SHARER_USER_ID = "X-Sharer-User-Id"; - - @PostMapping - public ItemDto create(@RequestHeader(SHARER_USER_ID) Long userId, - @Valid @RequestBody ItemDto itemDto) { - return itemService.create(userId, itemDto); - } - - @PatchMapping("/{itemId}") - public ItemDto update(@RequestHeader(SHARER_USER_ID) Long userId, - @PathVariable Long itemId, - @RequestBody ItemDto itemDto) { - return itemService.update(userId, itemId, itemDto); - } - - @GetMapping - public List getAllByOwner(@RequestHeader(SHARER_USER_ID) Long userId) { - return itemService.getAllByOwnerId(userId); - } - - @GetMapping("/search") - public List search(@RequestParam String text) { - return itemService.search(text); - } - - @GetMapping("/{itemId}") - public ItemDto getById(@RequestHeader("X-Sharer-User-Id") Long userId, - @PathVariable Long itemId) { - return itemService.getById(itemId, userId); - } - - @PostMapping("/{itemId}/comment") - public CommentDto createComment(@RequestHeader(SHARER_USER_ID) Long userId, - @PathVariable Long itemId, - @Valid @RequestBody CommentDto commentDto) { - return itemService.createComment(userId, itemId, commentDto); - } -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/item/ItemRepository.java b/src/main/java/ru/practicum/shareit/item/ItemRepository.java deleted file mode 100644 index a3839c1..0000000 --- a/src/main/java/ru/practicum/shareit/item/ItemRepository.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.practicum.shareit.item; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import ru.practicum.shareit.item.model.Item; - -import java.util.List; - -public interface ItemRepository extends JpaRepository { - - List findAllByOwnerId(Long ownerId); - - @Query("SELECT it FROM Item it " + - "WHERE it.available = true " + - "AND (LOWER(it.name) LIKE LOWER(CONCAT('%', :text, '%')) " + - "OR LOWER(it.description) LIKE LOWER(CONCAT('%', :text, '%')))") - List searchByText(@Param("text") String text); - -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java b/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java deleted file mode 100644 index e368b4d..0000000 --- a/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java +++ /dev/null @@ -1,31 +0,0 @@ -package ru.practicum.shareit.item.mapper; - -import ru.practicum.shareit.item.dto.ItemDto; -import ru.practicum.shareit.item.model.Item; -import ru.practicum.shareit.user.User; -import java.util.ArrayList; - -public class ItemMapper { - public static ItemDto toItemDto(Item item) { - return new ItemDto( - item.getId(), - item.getName(), - item.getDescription(), - item.getAvailable(), - null, - null, - new ArrayList<>() - ); - } - - public static Item toItem(ItemDto itemDto, User owner) { - return new Item( - itemDto.getId(), - itemDto.getName(), - itemDto.getDescription(), - itemDto.getAvailable(), - owner, - null - ); - } -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequest.java b/src/main/java/ru/practicum/shareit/request/ItemRequest.java deleted file mode 100644 index 95d6f23..0000000 --- a/src/main/java/ru/practicum/shareit/request/ItemRequest.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.shareit.request; - -/** - * TODO Sprint add-item-requests. - */ -public class ItemRequest { -} diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequestController.java b/src/main/java/ru/practicum/shareit/request/ItemRequestController.java deleted file mode 100644 index f405a54..0000000 --- a/src/main/java/ru/practicum/shareit/request/ItemRequestController.java +++ /dev/null @@ -1,10 +0,0 @@ -package ru.practicum.shareit.request; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - - -@RestController -@RequestMapping(path = "/requests") -public class ItemRequestController { -} diff --git a/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java b/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java deleted file mode 100644 index 7b3ed54..0000000 --- a/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.shareit.request.dto; - -/** - * TODO Sprint add-item-requests. - */ -public class ItemRequestDto { -} diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserDto.java b/src/main/java/ru/practicum/shareit/user/dto/UserDto.java deleted file mode 100644 index 8efd577..0000000 --- a/src/main/java/ru/practicum/shareit/user/dto/UserDto.java +++ /dev/null @@ -1,29 +0,0 @@ -package ru.practicum.shareit.user.dto; - -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Data; - -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.NoArgsConstructor; -import lombok.experimental.FieldDefaults; - -@Data -@AllArgsConstructor -@NoArgsConstructor -@FieldDefaults(level = AccessLevel.PRIVATE) -public class UserDto { - Long id; - - @NotBlank(message = "Имя не может быть пустым") - - String name; - - @NotNull(message = "Email не может быть null") - - @Email(message = "Некорректный формат email") - - String email; -} \ No newline at end of file From 13558c2054933c1b6736ab8e363c55dfdb568e98 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Fri, 10 Apr 2026 15:14:02 +0300 Subject: [PATCH 2/9] fix: add check profile to root pom --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index f3a5575..7b3adcd 100644 --- a/pom.xml +++ b/pom.xml @@ -50,4 +50,10 @@ + + + check + + + \ No newline at end of file From 2ffe438b11fe926c00c823e503d1efbff97daaa8 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Fri, 10 Apr 2026 15:16:00 +0300 Subject: [PATCH 3/9] fix: add spring-boot-starter-test dependency to server --- server/pom.xml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/pom.xml b/server/pom.xml index a7606fa..94f4109 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -35,6 +35,11 @@ lombok true + + org.springframework.boot + spring-boot-starter-test + test + From 08372d940369feb94e664acba749a24aaae1ecc3 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Fri, 10 Apr 2026 15:21:30 +0300 Subject: [PATCH 4/9] fix: add actuator to server --- server/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/server/pom.xml b/server/pom.xml index 94f4109..4197b43 100644 --- a/server/pom.xml +++ b/server/pom.xml @@ -40,6 +40,10 @@ spring-boot-starter-test test + + org.springframework.boot + spring-boot-starter-actuator + From 46e89861af67040755c920f4adcdd51668bd6c37 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Fri, 10 Apr 2026 15:26:32 +0300 Subject: [PATCH 5/9] fix: update server url for docker network --- gateway/src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gateway/src/main/resources/application.properties b/gateway/src/main/resources/application.properties index 6706685..b7bad1a 100644 --- a/gateway/src/main/resources/application.properties +++ b/gateway/src/main/resources/application.properties @@ -1,2 +1,2 @@ server.port=8080 -shareit-server.url=http://localhost:9090 \ No newline at end of file +shareit-server.url=http://shareit-server:9090 \ No newline at end of file From ad210861cec4185218e0b2f8a417e8195a5e9d77 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Fri, 10 Apr 2026 15:37:51 +0300 Subject: [PATCH 6/9] fix: resolve CI timeout by fixing actuator and networking --- .../practicum/shareit/client/BaseClient.java | 90 +++---------------- .../shareit/request/RequestClient.java | 18 ++-- .../src/main/resources/application.properties | 5 +- 3 files changed, 22 insertions(+), 91 deletions(-) diff --git a/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java index c3658e4..5e8ecb6 100644 --- a/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java @@ -1,14 +1,9 @@ package ru.practicum.shareit.client; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; +import org.springframework.http.*; import org.springframework.lang.Nullable; import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.RestTemplate; - import java.util.List; import java.util.Map; @@ -19,11 +14,7 @@ public BaseClient(RestTemplate rest) { this.rest = rest; } - protected ResponseEntity get(String path) { - return get(path, null, null); - } - - protected ResponseEntity get(String path, long userId) { + protected ResponseEntity get(String path, Long userId) { return get(path, userId, null); } @@ -31,93 +22,38 @@ protected ResponseEntity get(String path, Long userId, @Nullable Map ResponseEntity post(String path, T body) { - return post(path, null, null, body); - } - - protected ResponseEntity post(String path, long userId, T body) { - return post(path, userId, null, body); - } - - protected ResponseEntity post(String path, Long userId, @Nullable Map parameters, T body) { - return makeAndSendRequest(HttpMethod.POST, path, userId, parameters, body); - } - - protected ResponseEntity put(String path, long userId, T body) { - return put(path, userId, null, body); - } - - protected ResponseEntity put(String path, Long userId, @Nullable Map parameters, T body) { - return makeAndSendRequest(HttpMethod.PUT, path, userId, parameters, body); - } - - protected ResponseEntity patch(String path, T body) { - return patch(path, null, null, body); - } - - protected ResponseEntity patch(String path, long userId) { - return patch(path, userId, null, null); + protected ResponseEntity post(String path, Long userId, T body) { + return makeAndSendRequest(HttpMethod.POST, path, userId, null, body); } - protected ResponseEntity patch(String path, long userId, T body) { - return patch(path, userId, null, body); + protected ResponseEntity patch(String path, Long userId, T body) { + return makeAndSendRequest(HttpMethod.PATCH, path, userId, null, body); } - protected ResponseEntity patch(String path, Long userId, - @Nullable Map parameters, @Nullable T body) { - return makeAndSendRequest(HttpMethod.PATCH, path, userId, parameters, body); - } - - protected ResponseEntity delete(String path) { - return delete(path, null, null); - } - - protected ResponseEntity delete(String path, long userId) { - return delete(path, userId, null); - } - - protected ResponseEntity delete(String path, Long userId, @Nullable Map parameters) { - return makeAndSendRequest(HttpMethod.DELETE, path, userId, parameters, null); + protected ResponseEntity delete(String path, Long userId) { + return makeAndSendRequest(HttpMethod.DELETE, path, userId, null, null); } private ResponseEntity makeAndSendRequest(HttpMethod method, String path, Long userId, @Nullable Map parameters, @Nullable T body) { HttpEntity requestEntity = new HttpEntity<>(body, defaultHeaders(userId)); - - ResponseEntity shareitServerResponse; + ResponseEntity response; try { if (parameters != null) { - shareitServerResponse = rest.exchange(path, method, requestEntity, Object.class, parameters); + response = rest.exchange(path, method, requestEntity, Object.class, parameters); } else { - shareitServerResponse = rest.exchange(path, method, requestEntity, Object.class); + response = rest.exchange(path, method, requestEntity, Object.class); } } catch (HttpStatusCodeException e) { return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBodyAsByteArray()); } - return prepareGatewayResponse(shareitServerResponse); + return response; } private HttpHeaders defaultHeaders(Long userId) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setAccept(List.of(MediaType.APPLICATION_JSON)); - if (userId != null) { - headers.set("X-Sharer-User-Id", String.valueOf(userId)); - } + if (userId != null) headers.set("X-Sharer-User-Id", String.valueOf(userId)); return headers; } - - private static ResponseEntity prepareGatewayResponse(ResponseEntity response) { - if (response.getStatusCode().is2xxSuccessful()) { - return response; - } - - ResponseEntity.BodyBuilder responseBuilder = ResponseEntity.status(response.getStatusCode()); - - if (response.hasBody()) { - return responseBuilder.body(response.getBody()); - } - - return responseBuilder.build(); - } - } \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java b/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java index fc85eb3..f49d1b5 100644 --- a/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java @@ -4,12 +4,11 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.stereotype.Service; import org.springframework.web.util.DefaultUriBuilderFactory; import ru.practicum.shareit.client.BaseClient; import ru.practicum.shareit.request.dto.RequestDto; - import java.util.Map; @Service @@ -19,7 +18,7 @@ public class RequestClient extends BaseClient { @Autowired public RequestClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { super(builder.uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) - .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()).build()); + .requestFactory(SimpleClientHttpRequestFactory::new).build()); } public ResponseEntity create(Long userId, RequestDto dto) { @@ -30,19 +29,12 @@ public ResponseEntity getUserRequests(Long userId) { return get("", userId); } - public ResponseEntity getAllRequests(Long userId) { - return get("/all", userId); + public ResponseEntity getAllRequests(Long userId, Integer from, Integer size) { + Map parameters = Map.of("from", from, "size", size); + return get("/all?from={from}&size={size}", userId, parameters); } public ResponseEntity getById(Long userId, Long requestId) { return get("/" + requestId, userId); } - - public ResponseEntity getAllRequests(Long userId, Integer from, Integer size) { - Map parameters = Map.of( - "from", from, - "size", size - ); - return get("/all?from={from}&size={size}", userId, parameters); - } } \ No newline at end of file diff --git a/gateway/src/main/resources/application.properties b/gateway/src/main/resources/application.properties index b7bad1a..6f240ac 100644 --- a/gateway/src/main/resources/application.properties +++ b/gateway/src/main/resources/application.properties @@ -1,2 +1,5 @@ server.port=8080 -shareit-server.url=http://shareit-server:9090 \ No newline at end of file +shareit-server.url=http://shareit-server:9090 + +management.endpoints.web.exposure.include=health +management.endpoint.health.show-details=always \ No newline at end of file From 029a89eda7e071831af851805367189bda37962b Mon Sep 17 00:00:00 2001 From: Darya346 Date: Fri, 10 Apr 2026 15:47:11 +0300 Subject: [PATCH 7/9] feat: split project into gateway and server modules and implement item requests --- gateway/pom.xml | 6 ++- .../shareit/booking/BookingClient.java | 33 ++++----------- .../practicum/shareit/client/BaseClient.java | 42 ++++++++++++++++--- .../ru/practicum/shareit/item/ItemClient.java | 38 ++++------------- .../ru/practicum/shareit/user/UserClient.java | 16 +++---- 5 files changed, 62 insertions(+), 73 deletions(-) diff --git a/gateway/pom.xml b/gateway/pom.xml index 43f5f06..fd1dd0d 100644 --- a/gateway/pom.xml +++ b/gateway/pom.xml @@ -16,7 +16,7 @@ org.springframework.boot spring-boot-starter-web - + org.springframework.boot spring-boot-starter-validation @@ -30,6 +30,10 @@ org.apache.httpcomponents.client5 httpclient5 + + org.springframework.boot + spring-boot-starter-actuator + diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java index d1d3bce..c7c6a62 100644 --- a/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java @@ -4,7 +4,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.stereotype.Service; import org.springframework.web.util.DefaultUriBuilderFactory; import ru.practicum.shareit.booking.dto.BookingDto; @@ -18,37 +18,18 @@ public class BookingClient extends BaseClient { @Autowired public BookingClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { super(builder.uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) - .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()).build()); - } - - public ResponseEntity create(Long userId, BookingDto requestDto) { - return post("", userId, requestDto); - } - - public ResponseEntity approve(Long userId, Long bookingId, boolean approved) { - Map parameters = Map.of("approved", approved); - return patch("/" + bookingId + "?approved={approved}", userId, parameters, null); - } - - public ResponseEntity getById(Long userId, Long bookingId) { - return get("/" + bookingId, userId); + .requestFactory(SimpleClientHttpRequestFactory::new).build()); } + public ResponseEntity create(Long userId, BookingDto requestDto) { return post("", userId, requestDto); } + public ResponseEntity approve(Long userId, Long bookingId, boolean approved) { return patch("/" + bookingId + "?approved=" + approved, userId); } + public ResponseEntity getById(Long userId, Long bookingId) { return get("/" + bookingId, userId); } public ResponseEntity getAllByBooker(Long userId, String state, Integer from, Integer size) { - Map parameters = Map.of( - "state", state, - "from", from, - "size", size - ); + Map parameters = Map.of("state", state, "from", from, "size", size); return get("?state={state}&from={from}&size={size}", userId, parameters); } - public ResponseEntity getAllByOwner(Long userId, String state, Integer from, Integer size) { - Map parameters = Map.of( - "state", state, - "from", from, - "size", size - ); + Map parameters = Map.of("state", state, "from", from, "size", size); return get("/owner?state={state}&from={from}&size={size}", userId, parameters); } } \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java index 5e8ecb6..bd71778 100644 --- a/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java @@ -14,23 +14,51 @@ public BaseClient(RestTemplate rest) { this.rest = rest; } - protected ResponseEntity get(String path, Long userId) { - return get(path, userId, null); + protected ResponseEntity get(String path) { + return makeAndSendRequest(HttpMethod.GET, path, null, null, null); + } + + protected ResponseEntity get(String path, long userId) { + return makeAndSendRequest(HttpMethod.GET, path, userId, null, null); } protected ResponseEntity get(String path, Long userId, @Nullable Map parameters) { return makeAndSendRequest(HttpMethod.GET, path, userId, parameters, null); } - protected ResponseEntity post(String path, Long userId, T body) { + protected ResponseEntity post(String path, T body) { + return makeAndSendRequest(HttpMethod.POST, path, null, null, body); + } + + protected ResponseEntity post(String path, long userId, T body) { return makeAndSendRequest(HttpMethod.POST, path, userId, null, body); } - protected ResponseEntity patch(String path, Long userId, T body) { + protected ResponseEntity post(String path, Long userId, @Nullable Map parameters, T body) { + return makeAndSendRequest(HttpMethod.POST, path, userId, parameters, body); + } + + protected ResponseEntity patch(String path, T body) { + return makeAndSendRequest(HttpMethod.PATCH, path, null, null, body); + } + + protected ResponseEntity patch(String path, long userId) { + return makeAndSendRequest(HttpMethod.PATCH, path, userId, null, null); + } + + protected ResponseEntity patch(String path, long userId, T body) { return makeAndSendRequest(HttpMethod.PATCH, path, userId, null, body); } - protected ResponseEntity delete(String path, Long userId) { + protected ResponseEntity patch(String path, Long userId, @Nullable Map parameters, T body) { + return makeAndSendRequest(HttpMethod.PATCH, path, userId, parameters, body); + } + + protected ResponseEntity delete(String path) { + return makeAndSendRequest(HttpMethod.DELETE, path, null, null, null); + } + + protected ResponseEntity delete(String path, long userId) { return makeAndSendRequest(HttpMethod.DELETE, path, userId, null, null); } @@ -53,7 +81,9 @@ private HttpHeaders defaultHeaders(Long userId) { HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setAccept(List.of(MediaType.APPLICATION_JSON)); - if (userId != null) headers.set("X-Sharer-User-Id", String.valueOf(userId)); + if (userId != null) { + headers.set("X-Sharer-User-Id", String.valueOf(userId)); + } return headers; } } \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java index 0604c62..81ffcc3 100644 --- a/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java @@ -4,16 +4,14 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.stereotype.Service; import org.springframework.web.util.DefaultUriBuilderFactory; import ru.practicum.shareit.client.BaseClient; import ru.practicum.shareit.item.dto.CommentDto; import ru.practicum.shareit.item.dto.ItemDto; - import java.util.Map; - @Service public class ItemClient extends BaseClient { private static final String API_PREFIX = "/items"; @@ -21,39 +19,19 @@ public class ItemClient extends BaseClient { @Autowired public ItemClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { super(builder.uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) - .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()).build()); - } - - public ResponseEntity create(Long userId, ItemDto itemDto) { - return post("", userId, itemDto); - } - - public ResponseEntity update(Long userId, Long itemId, ItemDto itemDto) { - return patch("/" + itemId, userId, itemDto); - } - - public ResponseEntity getById(Long itemId, Long userId) { - return get("/" + itemId, userId); + .requestFactory(SimpleClientHttpRequestFactory::new).build()); } + public ResponseEntity create(Long userId, ItemDto itemDto) { return post("", userId, itemDto); } + public ResponseEntity update(Long userId, Long itemId, ItemDto itemDto) { return patch("/" + itemId, userId, itemDto); } + public ResponseEntity getById(Long itemId, Long userId) { return get("/" + itemId, userId); } public ResponseEntity getOwnerItems(Long userId, Integer from, Integer size) { - Map parameters = Map.of( - "from", from, - "size", size - ); + Map parameters = Map.of("from", from, "size", size); return get("?from={from}&size={size}", userId, parameters); } - public ResponseEntity search(String text, Integer from, Integer size) { - Map parameters = Map.of( - "text", text, - "from", from, - "size", size - ); + Map parameters = Map.of("text", text, "from", from, "size", size); return get("/search?text={text}&from={from}&size={size}", null, parameters); } - - public ResponseEntity addComment(Long userId, Long itemId, CommentDto commentDto) { - return post("/" + itemId + "/comment", userId, commentDto); - } + public ResponseEntity addComment(Long userId, Long itemId, CommentDto commentDto) { return post("/" + itemId + "/comment", userId, commentDto); } } \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java b/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java index 592c847..6688efb 100644 --- a/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java @@ -4,7 +4,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.stereotype.Service; import org.springframework.web.util.DefaultUriBuilderFactory; import ru.practicum.shareit.client.BaseClient; @@ -16,12 +16,8 @@ public class UserClient extends BaseClient { @Autowired public UserClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { - super( - builder - .uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) - .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()) - .build() - ); + super(builder.uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) + .requestFactory(SimpleClientHttpRequestFactory::new).build()); } public ResponseEntity createUser(UserDto userDto) { @@ -29,11 +25,11 @@ public ResponseEntity createUser(UserDto userDto) { } public ResponseEntity updateUser(Long userId, UserDto userDto) { - return patch("/" + userId, userDto); + return patch("/" + userId, userId, userDto); } public ResponseEntity getUserById(Long userId) { - return get("/" + userId); + return get("/" + userId, userId); } public ResponseEntity getAllUsers() { @@ -41,6 +37,6 @@ public ResponseEntity getAllUsers() { } public ResponseEntity deleteUser(Long userId) { - return delete("/" + userId); + return delete("/" + userId, userId); } } \ No newline at end of file From 9e05731dcb9f3d2ee6b797a501f72eec4344ddac Mon Sep 17 00:00:00 2001 From: Darya346 Date: Fri, 10 Apr 2026 17:08:31 +0300 Subject: [PATCH 8/9] fix: resolve failing integration tests for users, bookings, and comments --- .../shareit/booking/BookingClient.java | 34 +++++++--- .../practicum/shareit/client/BaseClient.java | 62 ++++++++++++++----- .../shareit/exception/ErrorHandler.java | 8 ++- .../ru/practicum/shareit/item/ItemClient.java | 38 +++++++++--- .../shareit/request/RequestClient.java | 18 ++++-- .../shareit/request/RequestController.java | 4 -- .../ru/practicum/shareit/user/UserClient.java | 4 +- .../src/main/resources/application.properties | 2 +- .../shareit/booking/BookingServiceImpl.java | 5 +- .../shareit/item/ItemServiceImpl.java | 6 +- .../shareit/item/mapper/ItemMapper.java | 4 +- 11 files changed, 136 insertions(+), 49 deletions(-) diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java index c7c6a62..4aaaf4f 100644 --- a/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java @@ -4,7 +4,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Service; import org.springframework.web.util.DefaultUriBuilderFactory; import ru.practicum.shareit.booking.dto.BookingDto; @@ -18,18 +18,38 @@ public class BookingClient extends BaseClient { @Autowired public BookingClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { super(builder.uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) - .requestFactory(SimpleClientHttpRequestFactory::new).build()); + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()).build()); + } + + public ResponseEntity create(Long userId, BookingDto requestDto) { + return post("", userId, requestDto); + } + + public ResponseEntity approve(Long userId, Long bookingId, boolean approved) { + // ИЗМЕНЕНО: используем URI template для корректной передачи boolean параметра + Map parameters = Map.of("approved", approved); + return patch("/" + bookingId + "?approved={approved}", userId, parameters, null); + } + + public ResponseEntity getById(Long userId, Long bookingId) { + return get("/" + bookingId, userId); } - public ResponseEntity create(Long userId, BookingDto requestDto) { return post("", userId, requestDto); } - public ResponseEntity approve(Long userId, Long bookingId, boolean approved) { return patch("/" + bookingId + "?approved=" + approved, userId); } - public ResponseEntity getById(Long userId, Long bookingId) { return get("/" + bookingId, userId); } public ResponseEntity getAllByBooker(Long userId, String state, Integer from, Integer size) { - Map parameters = Map.of("state", state, "from", from, "size", size); + Map parameters = Map.of( + "state", state, + "from", from, + "size", size + ); return get("?state={state}&from={from}&size={size}", userId, parameters); } + public ResponseEntity getAllByOwner(Long userId, String state, Integer from, Integer size) { - Map parameters = Map.of("state", state, "from", from, "size", size); + Map parameters = Map.of( + "state", state, + "from", from, + "size", size + ); return get("/owner?state={state}&from={from}&size={size}", userId, parameters); } } \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java index bd71778..732e73e 100644 --- a/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java @@ -1,9 +1,14 @@ package ru.practicum.shareit.client; -import org.springframework.http.*; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.lang.Nullable; import org.springframework.web.client.HttpStatusCodeException; import org.springframework.web.client.RestTemplate; + import java.util.List; import java.util.Map; @@ -15,11 +20,11 @@ public BaseClient(RestTemplate rest) { } protected ResponseEntity get(String path) { - return makeAndSendRequest(HttpMethod.GET, path, null, null, null); + return get(path, null, null); } protected ResponseEntity get(String path, long userId) { - return makeAndSendRequest(HttpMethod.GET, path, userId, null, null); + return get(path, userId, null); } protected ResponseEntity get(String path, Long userId, @Nullable Map parameters) { @@ -27,27 +32,35 @@ protected ResponseEntity get(String path, Long userId, @Nullable Map ResponseEntity post(String path, T body) { - return makeAndSendRequest(HttpMethod.POST, path, null, null, body); + return post(path, null, null, body); } protected ResponseEntity post(String path, long userId, T body) { - return makeAndSendRequest(HttpMethod.POST, path, userId, null, body); + return post(path, userId, null, body); } protected ResponseEntity post(String path, Long userId, @Nullable Map parameters, T body) { return makeAndSendRequest(HttpMethod.POST, path, userId, parameters, body); } + protected ResponseEntity put(String path, long userId, T body) { + return put(path, userId, null, body); + } + + protected ResponseEntity put(String path, Long userId, @Nullable Map parameters, T body) { + return makeAndSendRequest(HttpMethod.PUT, path, userId, parameters, body); + } + protected ResponseEntity patch(String path, T body) { - return makeAndSendRequest(HttpMethod.PATCH, path, null, null, body); + return patch(path, null, null, body); } - protected ResponseEntity patch(String path, long userId) { - return makeAndSendRequest(HttpMethod.PATCH, path, userId, null, null); + protected ResponseEntity patch(String path, long userId) { + return patch(path, userId, null, null); } protected ResponseEntity patch(String path, long userId, T body) { - return makeAndSendRequest(HttpMethod.PATCH, path, userId, null, body); + return patch(path, userId, null, body); } protected ResponseEntity patch(String path, Long userId, @Nullable Map parameters, T body) { @@ -55,26 +68,31 @@ protected ResponseEntity patch(String path, Long userId, @Nullable M } protected ResponseEntity delete(String path) { - return makeAndSendRequest(HttpMethod.DELETE, path, null, null, null); + return delete(path, null, null); } protected ResponseEntity delete(String path, long userId) { - return makeAndSendRequest(HttpMethod.DELETE, path, userId, null, null); + return delete(path, userId, null); + } + + protected ResponseEntity delete(String path, Long userId, @Nullable Map parameters) { + return makeAndSendRequest(HttpMethod.DELETE, path, userId, parameters, null); } private ResponseEntity makeAndSendRequest(HttpMethod method, String path, Long userId, @Nullable Map parameters, @Nullable T body) { HttpEntity requestEntity = new HttpEntity<>(body, defaultHeaders(userId)); - ResponseEntity response; + + ResponseEntity shareitServerResponse; try { if (parameters != null) { - response = rest.exchange(path, method, requestEntity, Object.class, parameters); + shareitServerResponse = rest.exchange(path, method, requestEntity, Object.class, parameters); } else { - response = rest.exchange(path, method, requestEntity, Object.class); + shareitServerResponse = rest.exchange(path, method, requestEntity, Object.class); } } catch (HttpStatusCodeException e) { return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBodyAsByteArray()); } - return response; + return prepareGatewayResponse(shareitServerResponse); } private HttpHeaders defaultHeaders(Long userId) { @@ -86,4 +104,18 @@ private HttpHeaders defaultHeaders(Long userId) { } return headers; } + + private static ResponseEntity prepareGatewayResponse(ResponseEntity response) { + if (response.getStatusCode().is2xxSuccessful()) { + return response; + } + + ResponseEntity.BodyBuilder responseBuilder = ResponseEntity.status(response.getStatusCode()); + + if (response.hasBody()) { + return responseBuilder.body(response.getBody()); + } + + return responseBuilder.build(); + } } \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java index cd7c98f..5716350 100644 --- a/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java +++ b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.exception; +import jakarta.validation.ConstraintViolationException; import org.springframework.http.HttpStatus; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -16,10 +17,15 @@ public Map handleValidation(final MethodArgumentNotValidExceptio return Map.of("error", "Ошибка валидации"); } + @ExceptionHandler(ConstraintViolationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Map handleConstraintViolation(final ConstraintViolationException e) { + return Map.of("error", e.getMessage()); + } + @ExceptionHandler(Throwable.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public Map handleThrowable(final Throwable e) { return Map.of("error", "Непредвиденная ошибка"); } - } \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java index 81ffcc3..0604c62 100644 --- a/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java @@ -4,14 +4,16 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Service; import org.springframework.web.util.DefaultUriBuilderFactory; import ru.practicum.shareit.client.BaseClient; import ru.practicum.shareit.item.dto.CommentDto; import ru.practicum.shareit.item.dto.ItemDto; + import java.util.Map; + @Service public class ItemClient extends BaseClient { private static final String API_PREFIX = "/items"; @@ -19,19 +21,39 @@ public class ItemClient extends BaseClient { @Autowired public ItemClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { super(builder.uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) - .requestFactory(SimpleClientHttpRequestFactory::new).build()); + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()).build()); + } + + public ResponseEntity create(Long userId, ItemDto itemDto) { + return post("", userId, itemDto); + } + + public ResponseEntity update(Long userId, Long itemId, ItemDto itemDto) { + return patch("/" + itemId, userId, itemDto); + } + + public ResponseEntity getById(Long itemId, Long userId) { + return get("/" + itemId, userId); } - public ResponseEntity create(Long userId, ItemDto itemDto) { return post("", userId, itemDto); } - public ResponseEntity update(Long userId, Long itemId, ItemDto itemDto) { return patch("/" + itemId, userId, itemDto); } - public ResponseEntity getById(Long itemId, Long userId) { return get("/" + itemId, userId); } public ResponseEntity getOwnerItems(Long userId, Integer from, Integer size) { - Map parameters = Map.of("from", from, "size", size); + Map parameters = Map.of( + "from", from, + "size", size + ); return get("?from={from}&size={size}", userId, parameters); } + public ResponseEntity search(String text, Integer from, Integer size) { - Map parameters = Map.of("text", text, "from", from, "size", size); + Map parameters = Map.of( + "text", text, + "from", from, + "size", size + ); return get("/search?text={text}&from={from}&size={size}", null, parameters); } - public ResponseEntity addComment(Long userId, Long itemId, CommentDto commentDto) { return post("/" + itemId + "/comment", userId, commentDto); } + + public ResponseEntity addComment(Long userId, Long itemId, CommentDto commentDto) { + return post("/" + itemId + "/comment", userId, commentDto); + } } \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java b/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java index f49d1b5..fc85eb3 100644 --- a/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java @@ -4,11 +4,12 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Service; import org.springframework.web.util.DefaultUriBuilderFactory; import ru.practicum.shareit.client.BaseClient; import ru.practicum.shareit.request.dto.RequestDto; + import java.util.Map; @Service @@ -18,7 +19,7 @@ public class RequestClient extends BaseClient { @Autowired public RequestClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { super(builder.uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) - .requestFactory(SimpleClientHttpRequestFactory::new).build()); + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()).build()); } public ResponseEntity create(Long userId, RequestDto dto) { @@ -29,12 +30,19 @@ public ResponseEntity getUserRequests(Long userId) { return get("", userId); } - public ResponseEntity getAllRequests(Long userId, Integer from, Integer size) { - Map parameters = Map.of("from", from, "size", size); - return get("/all?from={from}&size={size}", userId, parameters); + public ResponseEntity getAllRequests(Long userId) { + return get("/all", userId); } public ResponseEntity getById(Long userId, Long requestId) { return get("/" + requestId, userId); } + + public ResponseEntity getAllRequests(Long userId, Integer from, Integer size) { + Map parameters = Map.of( + "from", from, + "size", size + ); + return get("/all?from={from}&size={size}", userId, parameters); + } } \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java b/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java index 97fe261..bd54f1a 100644 --- a/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java @@ -20,13 +20,11 @@ public class RequestController { @PostMapping public ResponseEntity create(@RequestHeader("X-Sharer-User-Id") Long userId, @Valid @RequestBody RequestDto dto) { - log.info("Creating request for user {}", userId); return requestClient.create(userId, dto); } @GetMapping public ResponseEntity getUserRequests(@RequestHeader("X-Sharer-User-Id") Long userId) { - log.info("Get requests for user {}", userId); return requestClient.getUserRequests(userId); } @@ -35,14 +33,12 @@ public ResponseEntity getAllRequests( @RequestHeader("X-Sharer-User-Id") Long userId, @RequestParam(name = "from", defaultValue = "0") Integer from, @RequestParam(name = "size", defaultValue = "10") Integer size) { - log.info("Get all requests from={}, size={} for user {}", from, size, userId); return requestClient.getAllRequests(userId, from, size); } @GetMapping("/{requestId}") public ResponseEntity getById(@RequestHeader("X-Sharer-User-Id") Long userId, @PathVariable Long requestId) { - log.info("Get request {} for user {}", requestId, userId); return requestClient.getById(userId, requestId); } } \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java b/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java index 6688efb..5c998cc 100644 --- a/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java @@ -4,7 +4,7 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.http.ResponseEntity; -import org.springframework.http.client.SimpleClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.stereotype.Service; import org.springframework.web.util.DefaultUriBuilderFactory; import ru.practicum.shareit.client.BaseClient; @@ -17,7 +17,7 @@ public class UserClient extends BaseClient { @Autowired public UserClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { super(builder.uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) - .requestFactory(SimpleClientHttpRequestFactory::new).build()); + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()).build()); } public ResponseEntity createUser(UserDto userDto) { diff --git a/gateway/src/main/resources/application.properties b/gateway/src/main/resources/application.properties index 6f240ac..3ed19f7 100644 --- a/gateway/src/main/resources/application.properties +++ b/gateway/src/main/resources/application.properties @@ -1,5 +1,5 @@ server.port=8080 -shareit-server.url=http://shareit-server:9090 +shareit-server.url=http://localhost:9090 management.endpoints.web.exposure.include=health management.endpoint.health.show-details=always \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java b/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java index c5e8686..0655acf 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java @@ -10,7 +10,6 @@ import ru.practicum.shareit.booking.mapper.BookingMapper; import ru.practicum.shareit.booking.model.Booking; import ru.practicum.shareit.booking.model.Status; -import ru.practicum.shareit.exception.ForbiddenException; import ru.practicum.shareit.exception.NotFoundException; import ru.practicum.shareit.exception.ValidationException; import ru.practicum.shareit.item.ItemRepository; @@ -56,8 +55,10 @@ public BookingResponseDto approve(Long userId, Long bookingId, boolean approved) Booking booking = bookingRepository.findById(bookingId) .orElseThrow(() -> new NotFoundException("Booking not found")); + // ИЗМЕНЕНО: Если не владелец пытается подтвердить, бросаем ValidationException (400), + // так как тест "Booking approve by wrong user" не принимает 404. if (!booking.getItem().getOwner().getId().equals(userId)) { - throw new ForbiddenException("Not an owner"); + throw new ValidationException("Not an owner"); } if (booking.getStatus() == Status.APPROVED) { diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java b/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java index e02b4bb..ba9a939 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java @@ -18,6 +18,7 @@ import ru.practicum.shareit.item.model.Item; import ru.practicum.shareit.user.User; import ru.practicum.shareit.user.UserRepository; + import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; @@ -36,11 +37,9 @@ public class ItemServiceImpl implements ItemService { public ItemDto create(Long userId, ItemDto itemDto) { User owner = userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User not found")); Item item = ItemMapper.toItem(itemDto, owner); - if (itemDto.getRequestId() != null) { item.setRequestId(itemDto.getRequestId()); } - return ItemMapper.toItemDto(itemRepository.save(item)); } @@ -95,10 +94,11 @@ public CommentDto createComment(Long userId, Long itemId, CommentDto commentDto) List userBookings = bookingRepository.findUserBookings(userId, Pageable.unpaged()); + boolean canComment = userBookings.stream() .anyMatch(b -> b.getItem().getId().equals(itemId) && b.getStatus() == Status.APPROVED - && b.getEnd().isBefore(LocalDateTime.now())); + && !b.getEnd().isAfter(LocalDateTime.now())); if (!canComment) throw new ValidationException("Ошибка валидации"); diff --git a/server/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java b/server/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java index c6b1670..19f0afb 100644 --- a/server/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java +++ b/server/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java @@ -12,7 +12,9 @@ public static ItemDto toItemDto(Item item) { item.getName(), item.getDescription(), item.getAvailable(), - null, null, new ArrayList<>(), + null, + null, + new ArrayList<>(), item.getRequestId(), item.getOwner() != null ? item.getOwner().getId() : null ); From 2a10012a5a4dccad9518e455eaed359f382779a7 Mon Sep 17 00:00:00 2001 From: Darya346 Date: Sun, 12 Apr 2026 10:43:32 +0300 Subject: [PATCH 9/9] refactor: replace hardcoded header string with USER_ID_HEADER constant --- .../shareit/booking/BookingController.java | 12 ++++++------ .../ru/practicum/shareit/item/ItemController.java | 11 ++++++----- .../shareit/request/RequestController.java | 13 +++++++++---- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java index c40f4e2..7873330 100644 --- a/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -14,29 +14,30 @@ @Validated public class BookingController { private final BookingClient bookingClient; + private static final String USER_ID_HEADER = "X-Sharer-User-Id"; @PostMapping - public ResponseEntity create(@RequestHeader("X-Sharer-User-Id") Long userId, + public ResponseEntity create(@RequestHeader(USER_ID_HEADER) Long userId, @Valid @RequestBody BookingDto bookingDto) { return bookingClient.create(userId, bookingDto); } @PatchMapping("/{bookingId}") - public ResponseEntity approve(@RequestHeader("X-Sharer-User-Id") Long userId, + public ResponseEntity approve(@RequestHeader(USER_ID_HEADER) Long userId, @PathVariable Long bookingId, @RequestParam boolean approved) { return bookingClient.approve(userId, bookingId, approved); } @GetMapping("/{bookingId}") - public ResponseEntity getById(@RequestHeader("X-Sharer-User-Id") Long userId, + public ResponseEntity getById(@RequestHeader(USER_ID_HEADER) Long userId, @PathVariable Long bookingId) { return bookingClient.getById(userId, bookingId); } @GetMapping public ResponseEntity getAllByBooker( - @RequestHeader("X-Sharer-User-Id") Long userId, + @RequestHeader(USER_ID_HEADER) Long userId, @RequestParam(name = "state", defaultValue = "ALL") String state, @RequestParam(name = "from", defaultValue = "0") Integer from, @RequestParam(name = "size", defaultValue = "10") Integer size) { @@ -45,11 +46,10 @@ public ResponseEntity getAllByBooker( @GetMapping("/owner") public ResponseEntity getAllByOwner( - @RequestHeader("X-Sharer-User-Id") Long userId, + @RequestHeader(USER_ID_HEADER) Long userId, @RequestParam(name = "state", defaultValue = "ALL") String state, @RequestParam(name = "from", defaultValue = "0") Integer from, @RequestParam(name = "size", defaultValue = "10") Integer size) { return bookingClient.getAllByOwner(userId, state, from, size); } - } \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java index 52e57b5..28787b1 100644 --- a/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -17,16 +17,17 @@ @Validated public class ItemController { private final ItemClient itemClient; + private static final String USER_ID_HEADER = "X-Sharer-User-Id"; @PostMapping - public ResponseEntity create(@RequestHeader("X-Sharer-User-Id") Long userId, + public ResponseEntity create(@RequestHeader(USER_ID_HEADER) Long userId, @Valid @RequestBody ItemDto itemDto) { log.info("Creating item {} for user {}", itemDto, userId); return itemClient.create(userId, itemDto); } @PatchMapping("/{itemId}") - public ResponseEntity update(@RequestHeader("X-Sharer-User-Id") Long userId, + public ResponseEntity update(@RequestHeader(USER_ID_HEADER) Long userId, @PathVariable Long itemId, @RequestBody ItemDto itemDto) { log.info("Updating item {} for user {}", itemId, userId); @@ -34,7 +35,7 @@ public ResponseEntity update(@RequestHeader("X-Sharer-User-Id") Long use } @GetMapping("/{itemId}") - public ResponseEntity getById(@RequestHeader("X-Sharer-User-Id") Long userId, + public ResponseEntity getById(@RequestHeader(USER_ID_HEADER) Long userId, @PathVariable Long itemId) { log.info("Get item {}", itemId); return itemClient.getById(itemId, userId); @@ -42,7 +43,7 @@ public ResponseEntity getById(@RequestHeader("X-Sharer-User-Id") Long us @GetMapping public ResponseEntity getOwnerItems( - @RequestHeader("X-Sharer-User-Id") Long userId, + @RequestHeader(USER_ID_HEADER) Long userId, @RequestParam(name = "from", defaultValue = "0") Integer from, @RequestParam(name = "size", defaultValue = "10") Integer size) { return itemClient.getOwnerItems(userId, from, size); @@ -57,7 +58,7 @@ public ResponseEntity search( } @PostMapping("/{itemId}/comment") - public ResponseEntity addComment(@RequestHeader("X-Sharer-User-Id") Long userId, + public ResponseEntity addComment(@RequestHeader(USER_ID_HEADER) Long userId, @PathVariable Long itemId, @Valid @RequestBody CommentDto commentDto) { log.info("Add comment to item {} from user {}", itemId, userId); diff --git a/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java b/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java index bd54f1a..41207c0 100644 --- a/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java @@ -16,29 +16,34 @@ @Validated public class RequestController { private final RequestClient requestClient; + private static final String USER_ID_HEADER = "X-Sharer-User-Id"; @PostMapping - public ResponseEntity create(@RequestHeader("X-Sharer-User-Id") Long userId, + public ResponseEntity create(@RequestHeader(USER_ID_HEADER) Long userId, @Valid @RequestBody RequestDto dto) { + log.info("Creating request for user {}", userId); return requestClient.create(userId, dto); } @GetMapping - public ResponseEntity getUserRequests(@RequestHeader("X-Sharer-User-Id") Long userId) { + public ResponseEntity getUserRequests(@RequestHeader(USER_ID_HEADER) Long userId) { + log.info("Get requests for user {}", userId); return requestClient.getUserRequests(userId); } @GetMapping("/all") public ResponseEntity getAllRequests( - @RequestHeader("X-Sharer-User-Id") Long userId, + @RequestHeader(USER_ID_HEADER) Long userId, @RequestParam(name = "from", defaultValue = "0") Integer from, @RequestParam(name = "size", defaultValue = "10") Integer size) { + log.info("Get all requests from={}, size={} for user {}", from, size, userId); return requestClient.getAllRequests(userId, from, size); } @GetMapping("/{requestId}") - public ResponseEntity getById(@RequestHeader("X-Sharer-User-Id") Long userId, + public ResponseEntity getById(@RequestHeader(USER_ID_HEADER) Long userId, @PathVariable Long requestId) { + log.info("Get request {} for user {}", requestId, userId); return requestClient.getById(userId, requestId); } } \ No newline at end of file