From 38dd6791ba496e42323d1e633501a812b90e4449 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 20:48:03 +0300 Subject: [PATCH 01/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9F=D0=B5=D1=80=D0=B2=D0=B8=D1=87=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +- checkstyle.xml | 2 +- docker-compose.yml | 39 + gateway/Dockerfile | 5 + gateway/pom.xml | 70 + .../ru/practicum/shareit/ShareItGateway.java | 12 + .../shareit/booking/BookingClient.java | 58 + .../shareit/booking/BookingController.java | 100 + .../shareit/booking/BookingDtoInput.java | 28 + .../shareit/booking/BookingState.java | 16 + .../practicum/shareit/client/BaseClient.java | 121 + .../shareit/exception/ErrorHandler.java | 65 + .../exception/ValidationException.java | 0 .../shareit/item/CommentDtoInput.java | 18 + .../ru/practicum/shareit/item/ItemClient.java | 54 + .../shareit/item/ItemController.java | 122 + .../practicum/shareit/item/ItemDtoInput.java | 21 +- .../shareit/request/RequestClient.java | 41 + .../shareit/request/RequestController.java | 73 + .../shareit/request/RequestDtoInput.java | 18 + .../ru/practicum/shareit/user/UserClient.java | 46 + .../shareit/user/UserController.java | 81 + .../practicum/shareit/user/UserDtoInput.java | 18 +- .../shareit/validation/CreateObject.java | 0 .../shareit/validation/UpdateObject.java | 0 .../src/main/resources/application.properties | 7 + pom.xml | 97 +- postman_for_shareit_14.json | 2725 ----------------- ...eit_15.json => postman_for_shareit_16.json | 1213 ++------ server/Dockerfile | 5 + server/pom.xml | 86 + .../ru/practicum/shareit/ShareItServer.java | 8 +- .../ru/practicum/shareit/booking/Booking.java | 0 .../shareit/booking/BookingController.java | 3 +- .../practicum/shareit/booking/BookingDto.java | 0 .../shareit/booking/BookingDtoInput.java | 20 + .../shareit/booking/BookingDtoOutput.java | 0 .../shareit/booking/BookingDtoShort.java | 0 .../shareit/booking/BookingMapper.java | 0 .../shareit/booking/BookingRepository.java | 0 .../shareit/booking/BookingService.java | 0 .../shareit/booking/BookingServiceImpl.java | 13 - .../shareit/booking/BookingState.java | 2 +- .../shareit/booking/BookingStatus.java | 0 .../exception/DataConflictException.java | 0 .../shareit/exception/ErrorHandler.java | 0 .../shareit/exception/NotFoundException.java | 0 .../exception/RestrictedAccessException.java | 0 .../exception/ValidationException.java | 7 + .../ru/practicum/shareit/item/Comment.java | 0 .../ru/practicum/shareit/item/CommentDto.java | 5 - .../shareit/item/CommentDtoOutput.java | 0 .../shareit/item/CommentDtoShort.java | 0 .../practicum/shareit/item/CommentMapper.java | 0 .../shareit/item/CommentRepository.java | 0 .../java/ru/practicum/shareit/item/Item.java | 2 +- .../shareit/item/ItemController.java | 11 +- .../ru/practicum/shareit/item/ItemDto.java | 37 + .../practicum/shareit/item/ItemDtoInput.java | 20 + .../practicum/shareit/item/ItemDtoOutput.java | 4 +- .../shareit/item/ItemDtoRequest.java | 20 + .../practicum/shareit/item/ItemDtoShort.java | 0 .../ru/practicum/shareit/item/ItemMapper.java | 21 +- .../shareit/item/ItemRepository.java | 0 .../practicum/shareit/item/ItemService.java | 4 +- .../shareit/item/ItemServiceImpl.java | 48 +- .../ru/practicum/shareit/request/Request.java | 7 +- .../shareit/request/RequestController.java | 39 + .../practicum/shareit/request/RequestDto.java | 5 +- .../shareit/request/RequestDtoInput.java | 14 + .../shareit/request/RequestDtoItems.java | 26 + .../shareit/request/RequestMapper.java | 62 + .../shareit/request/RequestRepository.java | 17 + .../shareit/request/RequestService.java | 15 + .../shareit/request/RequestServiceImpl.java | 107 + .../java/ru/practicum/shareit/user/User.java | 0 .../shareit/user/UserController.java | 9 +- .../ru/practicum/shareit/user/UserDto.java | 29 + .../practicum/shareit/user/UserDtoOutput.java | 0 .../practicum/shareit/user/UserDtoShort.java | 0 .../ru/practicum/shareit/user/UserMapper.java | 0 .../shareit/user/UserRepository.java | 0 .../practicum/shareit/user/UserService.java | 0 .../shareit/user/UserServiceImpl.java | 0 .../src/main/resources/application.properties | 17 + {src => server/src}/main/resources/schema.sql | 0 .../shareit/booking/BookingDtoInput.java | 28 - .../request/ItemRequestController.java | 12 - src/main/resources/application.yaml | 20 - 89 files changed, 1828 insertions(+), 3951 deletions(-) create mode 100644 docker-compose.yml create mode 100644 gateway/Dockerfile 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 create mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/BookingState.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java rename {src => gateway/src}/main/java/ru/practicum/shareit/exception/ValidationException.java (100%) create mode 100644 gateway/src/main/java/ru/practicum/shareit/item/CommentDtoInput.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 rename src/main/java/ru/practicum/shareit/item/ItemDto.java => gateway/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java (72%) 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/RequestDtoInput.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 rename src/main/java/ru/practicum/shareit/user/UserDto.java => gateway/src/main/java/ru/practicum/shareit/user/UserDtoInput.java (59%) rename {src => gateway/src}/main/java/ru/practicum/shareit/validation/CreateObject.java (100%) rename {src => gateway/src}/main/java/ru/practicum/shareit/validation/UpdateObject.java (100%) create mode 100644 gateway/src/main/resources/application.properties delete mode 100644 postman_for_shareit_14.json rename postman_for_shareit_15.json => postman_for_shareit_16.json (81%) create mode 100644 server/Dockerfile create mode 100644 server/pom.xml rename src/main/java/ru/practicum/shareit/ShareItApp.java => server/src/main/java/ru/practicum/shareit/ShareItServer.java (57%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/Booking.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingController.java (97%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingDto.java (100%) create mode 100644 server/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingDtoShort.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingMapper.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingRepository.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingService.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java (95%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingState.java (57%) rename {src => server/src}/main/java/ru/practicum/shareit/booking/BookingStatus.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/DataConflictException.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/ErrorHandler.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/NotFoundException.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java (100%) create mode 100644 server/src/main/java/ru/practicum/shareit/exception/ValidationException.java rename {src => server/src}/main/java/ru/practicum/shareit/item/Comment.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/CommentDto.java (51%) rename {src => server/src}/main/java/ru/practicum/shareit/item/CommentDtoOutput.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/CommentDtoShort.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/CommentMapper.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/CommentRepository.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/Item.java (98%) rename {src => server/src}/main/java/ru/practicum/shareit/item/ItemController.java (85%) create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemDto.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java rename {src => server/src}/main/java/ru/practicum/shareit/item/ItemDtoOutput.java (88%) create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java rename {src => server/src}/main/java/ru/practicum/shareit/item/ItemDtoShort.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/ItemMapper.java (89%) rename {src => server/src}/main/java/ru/practicum/shareit/item/ItemRepository.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/item/ItemService.java (73%) rename {src => server/src}/main/java/ru/practicum/shareit/item/ItemServiceImpl.java (86%) rename {src => server/src}/main/java/ru/practicum/shareit/request/Request.java (88%) create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestController.java rename src/main/java/ru/practicum/shareit/request/ItemRequestDto.java => server/src/main/java/ru/practicum/shareit/request/RequestDto.java (87%) create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestDtoItems.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestMapper.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestRepository.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestService.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java rename {src => server/src}/main/java/ru/practicum/shareit/user/User.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/UserController.java (73%) create mode 100644 server/src/main/java/ru/practicum/shareit/user/UserDto.java rename {src => server/src}/main/java/ru/practicum/shareit/user/UserDtoOutput.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/UserDtoShort.java (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/UserMapper.java (100%) 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 (100%) rename {src => server/src}/main/java/ru/practicum/shareit/user/UserServiceImpl.java (100%) create mode 100644 server/src/main/resources/application.properties rename {src => server/src}/main/resources/schema.sql (100%) delete mode 100644 src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java delete mode 100644 src/main/java/ru/practicum/shareit/request/ItemRequestController.java delete mode 100644 src/main/resources/application.yaml diff --git a/README.md b/README.md index d4b18b6..bd0da80 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ _java-shareit_ # Шеринг вещей -### Согласно задания спринта № 14 (add-controllers) +### Согласно задания спринта № 16 (add-item-requests-and-gateway) ## выполнено Филипповских Сергеем _**Когорта-53**_ -_**Для проверки выполнения 14 спринта использовался postman: -[postman_for_shareit_14.json](/postman_for_shareit_14.json)**_ +_**Для проверки выполнения 16 спринта использовался postman: +[postman_for_shareit_16.json](/postman_for_shareit_16.json)**_ ![](/er_diagram_shareit.png) diff --git a/checkstyle.xml b/checkstyle.xml index c28a0d3..05d5086 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -161,7 +161,7 @@ - + diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..abe6570 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,39 @@ +services: + gateway: + build: gateway + image: shareit-gateway + container_name: shareit-gateway + ports: + - "8080:8080" + depends_on: + - server + environment: + - SHAREIT_SERVER_URL=http://server:9090 + + server: + build: server + image: shareit-server + container_name: shareit-server + ports: + - "9090:9090" + depends_on: + - db + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/shareit + - SPRING_DATASOURCE_USERNAME=shareit + - SPRING_DATASOURCE_PASSWORD=shareit + + db: + image: postgres:16.1 + container_name: postgres + ports: + - "6541:5432" + environment: + - POSTGRES_PASSWORD=shareit + - POSTGRES_USER=shareit + - POSTGRES_DB=shareit + healthcheck: + test: pg_isready -q -d $$POSTGRES_DB -U $$POSTGRES_USER + timeout: 5s + interval: 5s + retries: 10 \ No newline at end of file diff --git a/gateway/Dockerfile b/gateway/Dockerfile new file mode 100644 index 0000000..0ff1817 --- /dev/null +++ b/gateway/Dockerfile @@ -0,0 +1,5 @@ +FROM eclipse-temurin:21-jre-jammy +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/gateway/pom.xml b/gateway/pom.xml new file mode 100644 index 0000000..f3394c1 --- /dev/null +++ b/gateway/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + ru.practicum + shareit + 0.0.1-SNAPSHOT + + + shareit-gateway + 0.0.1-SNAPSHOT + + ShareIt Gateway + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.hibernate.validator + hibernate-validator + + + + org.apache.httpcomponents.client5 + httpclient5 + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + 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..0aa75c3 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java @@ -0,0 +1,12 @@ +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); + } + +} 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..7975693 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java @@ -0,0 +1,58 @@ +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.client.BaseClient; +import ru.practicum.shareit.exception.ValidationException; + +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 addBooking(Long userId, BookingDtoInput requestDto) { + if (requestDto.getStart().equals(requestDto.getEnd())) { + throw new ValidationException("Начало и окончание бронирования не может быть одним и тем же моментом."); + } + if (requestDto.getEnd().isBefore(requestDto.getStart())) { + throw new ValidationException("Окончание бронирования не может быть раньше его начала."); + } + return post("", userId, requestDto); + } + + public ResponseEntity updateByOwner(Long userId, Long bookingId, Boolean approved) { + Map parameters = Map.of( + "approved", approved + ); + return patch("/" + bookingId + "?approved={approved}", userId, parameters, null); + } + + public ResponseEntity getWithStatusById(Long bookingId, Long userId) { + return get("/" + bookingId, userId); + } + + public ResponseEntity getByUserId(Long userId, BookingState state) { + return get("?state=" + state, userId); + } + + public ResponseEntity getByOwnerId(Long userId, BookingState state) { + return get("/owner?state=" + state, userId); + } +} 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..6d368fb --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -0,0 +1,100 @@ +package ru.practicum.shareit.booking; + +import jakarta.validation.Valid; +import jakarta.validation.ValidationException; +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.*; + +@Controller +@RequestMapping(path = "/bookings") +@RequiredArgsConstructor +@Slf4j +@Validated +public class BookingController { + + private final BookingClient bookingClient; + + /** + * Создает бронирование. + * + * @param bookerId идентификатор пользователя, создающего бронирование + * @param bookingDto данные бронирования + * @return ResponseEntity с объектом, представляющим результат создания бронирования + */ + @PostMapping + public ResponseEntity addBooking(@RequestHeader("X-Sharer-User-Id") Long bookerId, + @Valid @RequestBody BookingDtoInput bookingDto) { + log.info("Создано бронирование {}, пользователем {}", bookingDto, bookerId); + return bookingClient.addBooking(bookerId, bookingDto); + } + + /** + * Обновляет бронирование владельцем. + * + * @param ownerId идентификатор владельца бронирования + * @param bookingId идентификатор бронирования + * @param approved флаг, указывающий, одобрено ли бронирование + * @return ResponseEntity с объектом, представляющим результат обновления бронирования + */ + @PatchMapping("/{bookingId}") + public ResponseEntity updateByOwner(@RequestHeader("X-Sharer-User-Id") Long ownerId, + @PathVariable Long bookingId, + @RequestParam Boolean approved) { + log.info("Обновление бронирования {}, владельцем {}, в состояние {}", bookingId, ownerId, approved); + return bookingClient.updateByOwner(ownerId, bookingId, approved); + } + + /** + * Получает данные о бронировании по его идентификатору. + * + * @param userId идентификатор пользователя, запрашивающего данные о бронировании + * @param bookingId идентификатор бронирования + * @return ResponseEntity с объектом, представляющим результат получения данных о бронировании + */ + @GetMapping("/{bookingId}") + public ResponseEntity getWithStatusById(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long bookingId) { + log.info("Получение данных о бронировании {}, пользователем {}", bookingId, userId); + return bookingClient.getWithStatusById( bookingId, userId); + } + + /** + * Получает все бронирования пользователя по его идентификатору и состоянию. + * + * @param userId идентификатор пользователя, запрашивающего данные о бронировании + * @param stateParam состояние бронирования + * @return ResponseEntity с объектом, представляющим результат получения данных о бронировании + */ + @GetMapping + public ResponseEntity getByUserId(@RequestHeader("X-Sharer-User-Id") Long userId, + @RequestParam(value = "state", + defaultValue = "ALL", required = false) String stateParam) { + BookingState state = BookingState.from(stateParam) + .orElseThrow(() -> + new ValidationException("Неизвестное состояние бронирования: " + stateParam)); + log.info("Получение всех бронирований пользователя{}, в состоянии {}", userId, state); + return bookingClient.getByUserId(userId, state); + } + + /** + * Получает все забронированные вещи пользователя по его идентификатору и состоянию. + * + * @param userId идентификатор пользователя, запрашивающего данные о бронировании + * @param stateParam состояние бронирования + * @return ResponseEntity с объектом, представляющим результат получения данных о бронировании + */ + @GetMapping("/owner") + public ResponseEntity getByOwnerId(@RequestHeader("X-Sharer-User-Id") Long userId, + @RequestParam(value = "state", defaultValue = "ALL", + required = false) String stateParam) { + BookingState state = BookingState.from(stateParam) + .orElseThrow(() -> + new ValidationException("Неизвестное состояние бронирования: " + stateParam)); + log.info("Получение всех забронированных вещей пользователя{}, в состоянии {}", userId, state); + return bookingClient.getByOwnerId(userId, state); + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java new file mode 100644 index 0000000..0441ea2 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java @@ -0,0 +1,28 @@ +package ru.practicum.shareit.booking; + +import jakarta.validation.constraints.Future; +import jakarta.validation.constraints.FutureOrPresent; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BookingDtoInput { + + @NotNull(message = "При создании брони должна быть информация о вещи.") + private Long itemId; + + @FutureOrPresent(message = "Дата начала бронирования не должна быть в прошлом") + @NotNull(message = "Дата бронирования не должна быть пустой") + private LocalDateTime start; + + @Future(message = "Дата завершения бронирования должна быть в будущем") + @NotNull(message = "Дата бронирования не должна быть пустой") + private LocalDateTime end; + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingState.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingState.java new file mode 100644 index 0000000..e633731 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingState.java @@ -0,0 +1,16 @@ +package ru.practicum.shareit.booking; + +import java.util.Optional; + +public enum BookingState { + ALL, CURRENT, PAST, FUTURE, WAITING, REJECTED; + + public static Optional from(String stringState) { + for (ru.practicum.shareit.booking.BookingState state : values()) { + if (state.name().equalsIgnoreCase(stringState)) { + return Optional.of(state); + } + } + return Optional.empty(); + } +} 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..1a2d33a --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java @@ -0,0 +1,121 @@ +package ru.practicum.shareit.client; + +import java.util.List; +import java.util.Map; + +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; + +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, 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(); + } +} 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..6e40c90 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -0,0 +1,65 @@ +package ru.practicum.shareit.exception; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.validation.BindingResult; +import org.springframework.validation.ObjectError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingRequestHeaderException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Slf4j +@RestControllerAdvice +public class ErrorHandler { + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleValidationException(final ValidationException e) { + log.error("{} - {}", HttpStatus.BAD_REQUEST, e.getMessage()); + return new ErrorResponse(e.getMessage()); + } + + + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleMethodArgumentNotValidException(final MethodArgumentNotValidException e) { + BindingResult bindingResult = e.getBindingResult(); + List allErrors = bindingResult.getAllErrors(); + String defaultMessage = allErrors.stream() + .map(error -> Objects.requireNonNull(error.getDefaultMessage())) + .collect(Collectors.joining(", ")); + log.error("{} - {}", HttpStatus.BAD_REQUEST, defaultMessage); + return new ErrorResponse(defaultMessage); + } + + + @ExceptionHandler + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ErrorResponse handleRunTimeException(final RuntimeException e) { + log.error("{} - {}", HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(e.getMessage()); + } + + @ExceptionHandler(MissingRequestHeaderException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleMissingRequestHeaderException(MissingRequestHeaderException e) { + log.error("{} - {}", HttpStatus.BAD_REQUEST, e.getMessage()); + return new ErrorResponse(e.getMessage()); + } + + @Getter + public static class ErrorResponse { + private final String error; + + public ErrorResponse(String error) { + this.error = error; + } + + } +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/exception/ValidationException.java b/gateway/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 gateway/src/main/java/ru/practicum/shareit/exception/ValidationException.java diff --git a/gateway/src/main/java/ru/practicum/shareit/item/CommentDtoInput.java b/gateway/src/main/java/ru/practicum/shareit/item/CommentDtoInput.java new file mode 100644 index 0000000..d15069d --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/CommentDtoInput.java @@ -0,0 +1,18 @@ +package ru.practicum.shareit.item; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CommentDtoInput { + + @NotNull(message = "Комментарий должен быть указан.") + @NotBlank(message = "Комментарий не может быть пустым.") + private String text; + +} 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..ddf344c --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java @@ -0,0 +1,54 @@ +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; + +@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 addItem(Long userId, ItemDtoInput itemDto) { + return post("", userId, itemDto); + } + + public ResponseEntity updateItem(Long userId, Long idItem, ItemDtoInput itemDto) { + return patch("/" + idItem, userId, itemDto); + } + + public ResponseEntity getItemById(Long idItem, Long userId) { + return get("/" + idItem, userId); + } + + public ResponseEntity getAllItems(Long userId) { + return get("", userId); + } + + public ResponseEntity removeItem(Long userId, Long idItem) { + return delete("/" + idItem, userId); + } + + public ResponseEntity searchItems(String text) { + return get("/search?text=" + text); + } + + public ResponseEntity saveComment(Long userId, Long itemId, CommentDtoInput commentDto) { + return post("/" + itemId + "/comment", userId, commentDto); + } + +} 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..6de8007 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -0,0 +1,122 @@ +package ru.practicum.shareit.item; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +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.validation.CreateObject; +import ru.practicum.shareit.validation.UpdateObject; + +@Controller +@RequestMapping(path = "/items") +@RequiredArgsConstructor +@Slf4j +@Validated +public class ItemController { + private final ItemClient itemClient; + + /** + * Добавляет новую вещь. + * + * @param itemDto Данные вещи. + * @param idUser Идентификатор пользователя, добавляющего вещь. + * @return ResponseEntity с объектом, представляющим результат добавления вещи. + */ + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity addItem(@Validated(CreateObject.class) @RequestBody ItemDtoInput itemDto, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + log.info("Получен запрос пользователем {} на добавление вещи {}", idUser, itemDto); + return itemClient.addItem(idUser, itemDto); + } + + /** + * Обновляет вещь. + * + * @param idItem Идентификатор вещи, которую нужно обновить. + * @param itemDto Данные вещи. + * @param idUser Идентификатор пользователя, обновляющего вещь. + * @return ResponseEntity с объектом, представляющим результат обновления вещи. + */ + @PatchMapping("/{idItem}") + public ResponseEntity updateItem(@PathVariable Long idItem, + @Validated(UpdateObject.class) @RequestBody ItemDtoInput itemDto, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + log.info("Получен запрос пользователем {} на обновление вещи {}", idUser, itemDto); + return itemClient.updateItem(idUser, idItem, itemDto); + } + + /** + * Возвращает вещь по ее идентификатору. + * + * @param idItem Идентификатор вещи, которую нужно вернуть. + * @param idUser Идентификатор пользователя, запрашивающего вещь. + * @return ResponseEntity с объектом, представляющим вещь по ее идентификатору. + */ + @GetMapping("/{idItem}") + public ResponseEntity getItemById(@PathVariable Long idItem, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + log.info("Получен запрос пользователем {} на получение вещи с id={}", idUser, idItem); + return itemClient.getItemById(idItem, idUser); + } + + /** + * Возвращает все вещи пользователя. + * + * @param idUser Идентификатор пользователя, вещи которого нужно вернуть. + * @return ResponseEntity с объектом, представляющим все вещи пользователя. + */ + @GetMapping + public ResponseEntity getAllItemsByUser(@RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + log.info("Получен запрос пользователем {} на получение всех своих вещей", idUser); + return itemClient.getAllItems(idUser); + } + + /** + * Удаляет вещь по ее идентификатору. + * + * @param idItem Идентификатор вещи, которую нужно удалить. + * @param idUser Идентификатор пользователя, удаляющего вещь. + * @return ResponseEntity с объектом, представляющим результат удаления вещи. + */ + @DeleteMapping("/{idItem}") + public ResponseEntity removeItem(@PathVariable Long idItem, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + log.info("Получен запрос пользователем {} на удаление вещи с id={}", idUser, idItem); + return itemClient.removeItem(idUser, idItem); + } + + /** + * Ищет вещи по тексту. + * + * @param text Текст, по которому нужно искать вещи. + * @param idUser Идентификатор пользователя, инициирующего поиск. + * @return ResponseEntity с объектом, представляющим результат поиска вещей по тексту. + */ + @GetMapping("/search") + public ResponseEntity searchItemsByText(@RequestParam String text, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + log.info("Получен запрос пользователем {} на поиск вещей по тексту: {}", idUser, text); + return itemClient.searchItems(text); + } + + /** + * Добавляет комментарий к вещи. + * + * @param userId Идентификатор пользователя, добавляющего комментарий. + * @param itemId Идентификатор вещи, к которой добавляется комментарий. + * @param commentDto Данные комментария. + * @return ResponseEntity с объектом, представляющим результат добавления комментария. + */ + @PostMapping("/{itemId}/comment") + public ResponseEntity addCommentToItem(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long itemId, + @RequestBody CommentDtoInput commentDto) { + log.info("Получен запрос пользователем {} на добавление отзыва на вещь с id={} с текстом: {}", + userId, itemId, commentDto.getText()); + return itemClient.saveComment(userId, itemId, commentDto); + } +} diff --git a/src/main/java/ru/practicum/shareit/item/ItemDto.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java similarity index 72% rename from src/main/java/ru/practicum/shareit/item/ItemDto.java rename to gateway/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java index 2fb9d3e..3173be9 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemDto.java +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java @@ -5,19 +5,12 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import ru.practicum.shareit.booking.Booking; -import ru.practicum.shareit.request.Request; -import ru.practicum.shareit.user.User; import ru.practicum.shareit.validation.CreateObject; -import java.util.List; - @Data @AllArgsConstructor @NoArgsConstructor -public class ItemDto { - - private Long id; +public class ItemDtoInput { @NotNull(groups = {CreateObject.class}, message = "Название вещи должно быть указано.") @NotBlank(groups = {CreateObject.class}, message = "Название вещи не может быть пустым.") @@ -30,16 +23,6 @@ public class ItemDto { @NotNull(groups = {CreateObject.class}, message = "Доступность вещи должна быть указана.") private Boolean available; - private User owner; - - private Request request; - - private List comments; - - private List bookings; - - private Booking lastBooking; - - private Booking nextBooking; + private Long requestId; } 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..ba2c019 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java @@ -0,0 +1,41 @@ +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; + +@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 addRequest(Long userId, RequestDtoInput requestDto) { + return post("", userId, requestDto); + } + + public ResponseEntity getRequests(Long userId) { + return get("", userId); + } + + public ResponseEntity getAllRequests(Long userId) { + return get("/all", userId); + } + + public ResponseEntity getRequestById(Long requestId) { + return get("/" + requestId); + } +} 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..6f1d1b4 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java @@ -0,0 +1,73 @@ +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.*; + +@Controller +@RequestMapping(path = "/requests") +@RequiredArgsConstructor +@Slf4j +@Validated +public class RequestController { + + private final RequestClient requestClient; + + /** + * Добавляет новый запрос. + * + * @param requestorId Идентификатор пользователя, добавляющего запрос. + * @param requestDto Данные запроса. + * @return ResponseEntity с объектом, представляющим результат добавления запроса. + */ + @PostMapping + public ResponseEntity addRequest(@RequestHeader("X-Sharer-User-Id") Long requestorId, + @Valid @RequestBody RequestDtoInput requestDto) { + log.info("Получен запрос на добавление запроса с параметрами: {}", requestDto); + return requestClient.addRequest(requestorId, requestDto); + } + + /** + * Возвращает расширенный список запросов конкретного пользователя. + * + * @param requestorId Идентификатор пользователя, запросы которого нужно вернуть. + * @return ResponseEntity с объектом, представляющим список запросов пользователя. + */ + @GetMapping + public ResponseEntity getRequests(@RequestHeader("X-Sharer-User-Id") Long requestorId) { + log.info("Получен запрос на получение расширенного списка запросов пользователя с id: {}", requestorId); + return requestClient.getRequests(requestorId); + } + + /** + * Возвращает все запросы (за исключением запросов самого пользователя). + * + * @param requestorId Идентификатор пользователя. + * @return ResponseEntity с объектом, представляющим все запросы. + */ + @GetMapping("/all") + public ResponseEntity getAllRequests(@RequestHeader("X-Sharer-User-Id") Long requestorId) { + log.info("Получен запрос на получение всех запросов, кроме запросов самого пользователя с id: {}", requestorId); + return requestClient.getAllRequests(requestorId); + } + + /** + * Возвращает запрос по его идентификатору. + * + * @param requestorId Идентификатор пользователя, который делает запрос. + * @param requestId Идентификатор запроса, который нужно вернуть. + * @return ResponseEntity с объектом, представляющим запрос по его идентификатору. + */ + @GetMapping("/{requestId}") + public ResponseEntity getRequestsById(@RequestHeader("X-Sharer-User-Id") Long requestorId, + @PathVariable Long requestId) { + log.info("Получен запрос на получение запроса с id: {} по идентификатору пользователя с id: {}", + requestId, requestorId); + return requestClient.getRequestById(requestId); + } + +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java b/gateway/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java new file mode 100644 index 0000000..90542a2 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java @@ -0,0 +1,18 @@ +package ru.practicum.shareit.request; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RequestDtoInput { + + @NotNull(message = "Запрос не может быть пустым.") + @NotBlank(message = "Запрос не может быть пустым.") + private String description; + +} 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..fe6a014 --- /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; + +@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 addUser( UserDtoInput userDto) { + return post("", userDto); + } + + public ResponseEntity updateUser(Long userId, UserDtoInput userDto) { + return patch("/" + userId, userDto); + } + + public ResponseEntity getUserById(Long userId) { + return get("/" + userId); + } + + public ResponseEntity getAllUsers() { + return get(API_PREFIX); + } + + public ResponseEntity removeUser(Long userId) { + return delete("/" + userId); + } + +} 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..5c5f256 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserController.java @@ -0,0 +1,81 @@ +package ru.practicum.shareit.user; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +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.validation.CreateObject; +import ru.practicum.shareit.validation.UpdateObject; + +@Controller +@RequestMapping("/users") +@RequiredArgsConstructor +@Slf4j +@Validated +public class UserController { + private final UserClient userClient; + + /** + * Добавляет нового пользователя. + * + * @param userDto данные пользователя + * @return ResponseEntity с объектом, представляющим результат добавления пользователя + */ + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity addUser(@Validated(CreateObject.class) @RequestBody UserDtoInput userDto) { + log.info("Добавление нового пользователя {}", userDto); + return userClient.addUser(userDto); + } + + /** + * Обновляет данные пользователя. + * + * @param idUser идентификатор пользователя, данные которого нужно обновить + * @param userDto новые данные пользователя + * @return ResponseEntity с объектом, представляющим результат обновления данных пользователя + */ + @PatchMapping("/{idUser}") + public ResponseEntity updateUser(@PathVariable Long idUser, + @Validated(UpdateObject.class) @RequestBody UserDtoInput userDto) { + log.info("Обновление данных пользователя с id {} на основе данных {}", idUser, userDto); + return userClient.updateUser(idUser, userDto); + } + + /** + * Получает данные пользователя по его идентификатору. + * + * @param idUser идентификатор пользователя, данные которого нужно получить + * @return ResponseEntity с объектом, представляющим результат получения данных пользователя + */ + @GetMapping("/{idUser}") + public ResponseEntity getUserById(@PathVariable Long idUser) { + log.info("Получение данных пользователя с id {}", idUser); + return userClient.getUserById(idUser); + } + + /** + * Получает всех пользователей. + * + * @return ResponseEntity с объектом, представляющим результат получения всех пользователей + */ + @GetMapping + public ResponseEntity getAllUsers() { + log.info("Получение всех пользователей"); + return userClient.getAllUsers(); + } + + /** + * Удаляет пользователя по его идентификатору. + * + * @param idUser идентификатор пользователя, которого нужно удалить + */ + @DeleteMapping("/{idUser}") + public ResponseEntity removeUser(@PathVariable Long idUser) { + log.info("Удаление пользователя с id {}", idUser); + return userClient.removeUser(idUser); + } +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/user/UserDto.java b/gateway/src/main/java/ru/practicum/shareit/user/UserDtoInput.java similarity index 59% rename from src/main/java/ru/practicum/shareit/user/UserDto.java rename to gateway/src/main/java/ru/practicum/shareit/user/UserDtoInput.java index 122a434..702908a 100644 --- a/src/main/java/ru/practicum/shareit/user/UserDto.java +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserDtoInput.java @@ -6,33 +6,19 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import ru.practicum.shareit.booking.Booking; -import ru.practicum.shareit.item.Comment; -import ru.practicum.shareit.item.Item; import ru.practicum.shareit.validation.CreateObject; -import ru.practicum.shareit.validation.UpdateObject; - -import java.util.List; @Data @AllArgsConstructor @NoArgsConstructor -public class UserDto { - - private Long id; +public class UserDtoInput { @NotNull(groups = {CreateObject.class}, message = "Имя должно быть указано.") @NotBlank(groups = {CreateObject.class}, message = "Имя не может быть пустым.") private String name; @NotNull(groups = {CreateObject.class}, message = "Email должен быть указан.") - @Email(groups = {CreateObject.class, UpdateObject.class}, message = "Email должен быть указан корректно.") + @Email(groups = {CreateObject.class}, message = "Email должен быть указан корректно.") private String email; - private List items; - - private List bookings; - - private List comments; - } diff --git a/src/main/java/ru/practicum/shareit/validation/CreateObject.java b/gateway/src/main/java/ru/practicum/shareit/validation/CreateObject.java similarity index 100% rename from src/main/java/ru/practicum/shareit/validation/CreateObject.java rename to gateway/src/main/java/ru/practicum/shareit/validation/CreateObject.java diff --git a/src/main/java/ru/practicum/shareit/validation/UpdateObject.java b/gateway/src/main/java/ru/practicum/shareit/validation/UpdateObject.java similarity index 100% rename from src/main/java/ru/practicum/shareit/validation/UpdateObject.java rename to gateway/src/main/java/ru/practicum/shareit/validation/UpdateObject.java diff --git a/gateway/src/main/resources/application.properties b/gateway/src/main/resources/application.properties new file mode 100644 index 0000000..2ee0851 --- /dev/null +++ b/gateway/src/main/resources/application.properties @@ -0,0 +1,7 @@ +logging.level.org.springframework.web.client.RestTemplate=DEBUG +#logging.level.org.apache.http=DEBUG +#logging.level.httpclient.wire=DEBUG + +server.port=8080 + +shareit-server.url=http://localhost:9090 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2185679..79cba37 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,7 @@ ru.practicum shareit + pom 0.0.1-SNAPSHOT ShareIt @@ -19,73 +20,29 @@ 21 - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - org.postgresql - postgresql - - - - org.projectlombok - lombok - true - - - - org.springframework.boot - spring-boot-starter-test - test - - - - org.springframework.boot - spring-boot-starter-validation - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - + + gateway + server + - - - src/main/resources - true - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - + + org.springframework.boot + spring-boot-maven-plugin + + + true + + + + org.projectlombok + lombok + + + + org.apache.maven.plugins maven-surefire-plugin @@ -232,17 +189,5 @@ - - coverage - - - - org.jacoco - jacoco-maven-plugin - - - - - - + \ No newline at end of file diff --git a/postman_for_shareit_14.json b/postman_for_shareit_14.json deleted file mode 100644 index d04cf55..0000000 --- a/postman_for_shareit_14.json +++ /dev/null @@ -1,2725 +0,0 @@ -{ - "info": { - "_postman_id": "f4a82602-e802-4363-87b5-c1814ccd49db", - "name": "Sprint 14 ShareIt (add-controllers)", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "23073145", - "_collection_link": "https://universal-shadow-295426.postman.co/workspace/My-Workspace~4200f6aa-0504-44b1-8a1d-707d0dcbd5ce/collection/13708500-f4a82602-e802-4363-87b5-c1814ccd49db?action=share&source=collection_link&creator=23073145" - }, - "item": [ - { - "name": "users", - "item": [ - { - "name": "Create user", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " pm.collectionVariables.set(\"userName\", user.name);\r", - " pm.collectionVariables.set(\"userEmail\", user.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200 or 201\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([200,201]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"{{userEmail}}\"\n}" - }, - "url": { - "raw": "localhost:8080/users", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "users" - ] - } - }, - "response": [] - }, - { - "name": "Create user without email", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user2 = rnd.getUser();\r", - " pm.collectionVariables.set(\"userName\", user2.name);\r", - " pm.collectionVariables.set(\"userEmail\", user2.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\"\n}" - }, - "url": { - "raw": "localhost:8080/users", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "users" - ] - } - }, - "response": [] - }, - { - "name": "Create 2 users with same email", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user1 = rnd.getUser();\r", - " us = await api.addUser(user1);\r", - " user2 = rnd.getUser();\r", - " user2.email = user1.email;\r", - " pm.collectionVariables.set(\"userName\", user2.name);\r", - " pm.collectionVariables.set(\"userEmail\", user2.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 409\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([409, 500]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"{{userEmail}}\"\n}" - }, - "url": { - "raw": "localhost:8080/users", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "users" - ] - } - }, - "response": [] - }, - { - "name": "Create user with invalid email", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " pm.collectionVariables.set(\"userName\", user.name);\r", - " pm.collectionVariables.set(\"userEmail\", user.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"user.com\"\n}" - }, - "url": { - "raw": "localhost:8080/users", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "users" - ] - } - }, - "response": [] - }, - { - "name": "User update", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " us = await api.addUser(user);\r", - " pm.collectionVariables.set(\"userId\", us.id);\r", - " us = rnd.getUser()\r", - " pm.collectionVariables.set(\"userName\", us.name);\r", - " pm.collectionVariables.set(\"userEmail\", us.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {\r", - " pm.response.to.be.ok;\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "pm.test(\"Test user 'id' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('id');\r", - " var id = pm.collectionVariables.get(\"userId\");\r", - " pm.expect(jsonData.id, '\"id\" must be ' + id).to.eql(Number(id));\r", - "});\r", - "pm.test(\"Test user 'email' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('email');\r", - " var email = pm.collectionVariables.get(\"userEmail\");\r", - " pm.expect(jsonData.email, '\"email\" must be ' + email).to.eql(email);\r", - "});\r", - "pm.test(\"Test user 'name' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('name');\r", - " var name = pm.collectionVariables.get(\"userName\");\r", - " pm.expect(jsonData.name, '\"name\" must be ' + name).to.eql(name);\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"{{userEmail}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/users/{{userId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } - }, - "response": [] - }, - { - "name": "User update name", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " us = await api.addUser(user);\r", - " pm.collectionVariables.set(\"userId\", us.id);\r", - " us = rnd.getUser()\r", - " pm.collectionVariables.set(\"userName\", us.name);\r", - " pm.collectionVariables.set(\"userEmail\", us.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {\r", - " pm.response.to.be.ok;\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "pm.test(\"Test user 'id' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('id');\r", - " var id = pm.collectionVariables.get(\"userId\");\r", - " pm.expect(jsonData.id, '\"id\" must be ' + id).to.eql(Number(id));\r", - "});\r", - "pm.test(\"Test user 'name' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('name');\r", - " var name = pm.collectionVariables.get(\"userName\");\r", - " pm.expect(jsonData.name, '\"name\" must be ' + name).to.eql(name);\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/users/{{userId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } - }, - "response": [] - }, - { - "name": "User update email", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " us = await api.addUser(user);\r", - " pm.collectionVariables.set(\"userId\", us.id);\r", - " us = rnd.getUser()\r", - " pm.collectionVariables.set(\"userName\", us.name);\r", - " pm.collectionVariables.set(\"userEmail\", us.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {\r", - " pm.response.to.be.ok;\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "pm.test(\"Test user 'id' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('id');\r", - " var id = pm.collectionVariables.get(\"userId\");\r", - " pm.expect(jsonData.id, '\"id\" must be ' + id).to.eql(Number(id));\r", - "});\r", - "pm.test(\"Test user 'email' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('email');\r", - " var email = pm.collectionVariables.get(\"userEmail\");\r", - " pm.expect(jsonData.email, '\"email\" must be ' + email).to.eql(email);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"email\": \"{{userEmail}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/users/{{userId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } - }, - "response": [] - }, - { - "name": "User update with existing email", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " us = await api.addUser(user);\r", - " user2 = rnd.getUser();\r", - " us2 = await api.addUser(user2)\r", - " pm.collectionVariables.set(\"userId\", us2.id);\r", - " usa = rnd.getUser()\r", - " pm.collectionVariables.set(\"userName\", usa.name);\r", - " pm.collectionVariables.set(\"userEmail\", user.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 409\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([409, 500]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"{{userEmail}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/users/{{userId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } - }, - "response": [] - }, - { - "name": "Get user", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " us = await api.addUser(user);\r", - " pm.collectionVariables.set(\"userId\", us.id);\r", - " pm.collectionVariables.set(\"userName\", us.name);\r", - " pm.collectionVariables.set(\"userEmail\", us.email);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {\r", - " pm.response.to.be.ok;\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "pm.test(\"Test user 'id' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('id');\r", - " var id = pm.collectionVariables.get(\"userId\");\r", - " pm.expect(jsonData.id, '\"id\" must be ' + id).to.eql(Number(id));\r", - "});\r", - "pm.test(\"Test user 'email' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('email');\r", - " var email = pm.collectionVariables.get(\"userEmail\");\r", - " pm.expect(jsonData.email, '\"email\" must be ' + email).to.eql(email);\r", - "});\r", - "pm.test(\"Test user 'name' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('name');\r", - " var name = pm.collectionVariables.get(\"userName\");\r", - " pm.expect(jsonData.name, '\"name\" must be ' + name).to.eql(name);\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{baseUrl}}/users/{{userId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } - }, - "response": [] - }, - { - "name": "User delete", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,204]);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " us = await api.addUser(user);\r", - " pm.collectionVariables.set(\"userId\", us.id);\r", - " pm.collectionVariables.set(\"userName\", us.name);\r", - " pm.collectionVariables.set(\"userEmail\", us.email);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "DELETE", - "header": [ - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/users/{{userId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "items", - "item": [ - { - "name": "Item create", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200 or 201\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([200,201]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "var item = pm.collectionVariables.get(\"item\");\r", - "\r", - "pm.test(\"Response data equal to request\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('id');\r", - " pm.expect(jsonData).to.have.property('name');\r", - " pm.expect(jsonData).to.have.property('description');\r", - " pm.expect(jsonData).to.have.property('available');\r", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);\r", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);\r", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "localhost:8080/items", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item create without X-Sharer-User-Id", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400 or 500\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([500, 400]);\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "localhost:8080/items", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item create with non-existent user", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id + '1');\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 404\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([404]);\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "localhost:8080/items", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item create without available field", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\"\n}" - }, - "url": { - "raw": "localhost:8080/items", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item create with empty name field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"\",\n \"description\": \"Аккумуляторная отвертка\",\n \"available\": true\n}" - }, - "url": { - "raw": "{{baseUrl}}/items", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item create with empty description field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"Отвертка\",\n \"available\": true\n}" - }, - "url": { - "raw": "{{baseUrl}}/items", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item update", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update without X-Sharer-User-Id", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 500\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([500, 400]);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text", - "disabled": true - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update with other user", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 404\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([404, 403]);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id + 1);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update available field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update description field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"description\": \"{{itemDescription}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update name field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item get", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = await api.addItem(rnd.getItem(), user.id)\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemId\", item.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Get all items from user", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test list item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 2').to.eql(2);", - "});", - "", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " await api.addItem(rnd.getItem(), user.id)\r", - " await api.addItem(rnd.getItem(), user.id)\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/items", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item search", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test list item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 1').to.eql(1);", - " pm.expect(jsonData[0].name.toUpperCase(), 'Name should include ' + pm.collectionVariables.get(\"searchString\")).to.eql(pm.collectionVariables.get(\"searchString\"))", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " it = rnd.getItem()\r", - " it.available = true\r", - " item = await api.addItem(it, user.id)\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemId\", item.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"searchString\", item.name.toUpperCase());\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/items/search?text={{searchString}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "search" - ], - "query": [ - { - "key": "text", - "value": "{{searchString}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Item search unavailable", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test list item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 0').to.eql(0);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " it = rnd.getItem()\r", - " it.available = false\r", - " item = await api.addItem(it, user.id)\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemId\", item.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"searchString\", item.name.toUpperCase());\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/items/search?text={{searchString}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "search" - ], - "query": [ - { - "key": "text", - "value": "{{searchString}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Item search empty", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test search item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 0').to.eql(0);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/items/search?text=", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "search" - ], - "query": [ - { - "key": "text", - "value": "" - } - ] - } - }, - "response": [] - } - ] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "packages": {}, - "exec": [ - "API = class {\r", - " constructor(postman, verbose = false, baseUrl = \"http://localhost:8080\") {\r", - " this.baseUrl = baseUrl;\r", - " this.pm = postman;\r", - " this._verbose = verbose;\r", - " }\r", - "\r", - " async addUser(user, id=0, verbose=null) {\r", - " return this.post(\"/users\", user, id, \"Ошибка при добавлении нового пользователя: \", verbose);\r", - " }\r", - "\r", - " async addItem(item, id=0, verbose=null) {\r", - " return this.post(\"/items\", item, id, \"Ошибка при добавлении новой вещи: \", verbose);\r", - " }\r", - "\r", - " async addRequest(request, id=0, verbose=null) {\r", - " return this.post(\"/requests\", request, id, \"Ошибка при добавлении нового запроса: \", verbose);\r", - " }\r", - " \r", - " async post(path, body, id=0, errorText = \"Ошибка при выполнении post-запроса: \", verbose=null) {\r", - " return this.sendRequest(\"POST\", path, body, id, errorText, verbose);\r", - " }\r", - "\r", - " async patch(path, body = null, id=0, errorText = \"Ошибка при выполнении patch-запроса: \", verbose=null) {\r", - " return this.sendRequest(\"PATCH\", path, body, id, errorText, verbose);\r", - " }\r", - "\r", - " async get(path, body = null, id=0, errorText = \"Ошибка при выполнении get-запроса: \", verbose=null) {\r", - " return this.sendRequest(\"GET\", path, body, id, errorText, verbose);\r", - " }\r", - "\r", - " async put(path, body = null, id=0, errorText = \"Ошибка при выполнении put-запроса: \", verbose=null) {\r", - " return this.sendRequest(\"PUT\", path, body, id, errorText, verbose);\r", - " }\r", - "\r", - " async delete(path, body = null, id=0, errorText = \"Ошибка при выполнении delte-запроса: \", verbose=null) {\r", - " return this.sendRequest(\"DELETE\", path, body, id, errorText, verbose);\r", - " }\r", - "\r", - " async sendRequest(method, path, body=null, id=0, errorText = \"Ошибка при выполнении запроса: \", verbose=null) {\r", - " return new Promise((resolve, reject) => {\r", - " verbose = verbose == null ? this._verbose : verbose;\r", - " var req = {};\r", - " if (id == 0){\r", - " req = {\r", - " url: this.baseUrl + path,\r", - " method: method,\r", - " body: body == null ? \"\" : JSON.stringify(body),\r", - " header: { \"Content-Type\": \"application/json\"},\r", - " };\r", - " }else{\r", - " req = {\r", - " url: this.baseUrl + path,\r", - " method: method,\r", - " body: body == null ? \"\" : JSON.stringify(body),\r", - " header: [{\r", - " \"key\": \"X-Sharer-User-Id\",\r", - " \"value\": id,\r", - " \"type\": \"text\",\r", - " },\r", - " {\r", - " \"key\": \"Content-Type\",\r", - " \"name\": \"Content-Type\",\r", - " \"value\": \"application/json\",\r", - " \"type\": \"text\"\r", - " }]\r", - " };\r", - " }\r", - " if(verbose) {\r", - " console.log(\"Отправляю запрос: \", req);\r", - " }\r", - "\r", - " try {\r", - " this.pm.sendRequest(req, (error, response) => {\r", - " if(error || (response.code >= 400 && response.code <= 599)) {\r", - " let err = error ? error : JSON.stringify(response.json());\r", - " console.error(\"При выполнении запроса к серверу возникла ошибка.\\n\", err,\r", - " \"\\nДля отладки проблемы повторите такой же запрос к вашей программе \" + \r", - " \"на локальном компьютере. Данные запроса:\\n\", JSON.stringify(request));\r", - "\r", - " reject(new Error(errorText + err));\r", - " }\r", - " if(verbose) {\r", - " console.log(\"Результат обработки запроса: код состояния - \", response.code, \", тело: \", response.json());\r", - " }\r", - " if (response.stream.length === 0){\r", - " resolve(null);\r", - " }else{\r", - " resolve(response.json());\r", - " }\r", - " });\r", - " \r", - " } catch(err) {\r", - " if(verbose) {\r", - " console.error(errorText, err);\r", - " }\r", - " return Promise.reject(err);\r", - " }\r", - " });\r", - " }\r", - "};\r", - "\r", - "RandomUtils = class {\r", - " constructor() {}\r", - "\r", - " getUser() {\r", - " return {\r", - " name: pm.variables.replaceIn('{{$randomFullName}}'),\r", - " email: pm.variables.replaceIn('{{$randomEmail}}'),\r", - " };\r", - " }\r", - "\r", - " getRequest() {\r", - " return {\r", - " description: this.getWord(50)\r", - " };\r", - " }\r", - "\r", - " getItem() {\r", - " return {\r", - " name: this.getWord(10),\r", - " description: this.getWord(50),\r", - " available: pm.variables.replaceIn('{{$randomBoolean}}')\t\r", - " };\r", - " }\r", - "\r", - " getItemForRequest(id) {\r", - " return {\r", - " name: this.getWord(10),\r", - " description: this.getWord(50),\r", - " available: pm.variables.replaceIn('{{$randomBoolean}}'),\r", - " requestId: id\r", - " };\r", - " }\r", - "\r", - " getFilm(director=null) {\r", - " let date = new Date(new Date(1960, 0, 1).getTime() + Math.random() * (new Date(2010, 0, 1).getTime() - new Date(1960, 0, 1).getTime()));\r", - " var toReturn = {\r", - " name: this.getWord(15),\r", - " description: this.getWord(50),\r", - " releaseDate: date.toISOString().slice(0,10),\r", - " duration: Math.floor(Math.random() * (180 - 60 + 1) + 60),\r", - " mpa: { id: Math.floor(Math.random() * (5 - 1 + 1) + 1)},\r", - " genres: [{ id: Math.floor(Math.random() * (6 - 1 + 1) + 1)}]\r", - " };\r", - " if (director!==null)\r", - " toReturn.directors = [{ id: director.id}];\r", - " return toReturn;\r", - " }\r", - "\r", - "\r", - " getWord(length = 1) {\r", - " let result = '';\r", - " const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\r", - " const charactersLength = characters.length;\r", - " let counter = 0;\r", - " while (counter < length) {\r", - " result += characters.charAt(Math.floor(Math.random() * charactersLength));\r", - " counter += 1;\r", - " }\r", - " return result;\r", - " }\r", - "\r", - " getName(length = 1) {\r", - " let result = '';\r", - " const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\r", - " const charactersLength = characters.length;\r", - " let counter = 0;\r", - " while (counter < length) {\r", - " result += characters.charAt(Math.floor(Math.random() * charactersLength));\r", - " counter += 1;\r", - " }\r", - " return result;\r", - " }\r", - "\r", - "}" - ] - } - }, - { - "listen": "test", - "script": { - "type": "text/javascript", - "packages": {}, - "exec": [ - "" - ] - } - } - ], - "variable": [ - { - "key": "baseUrl", - "value": "http://localhost:8080" - }, - { - "key": "userName", - "value": "" - }, - { - "key": "userEmail", - "value": "" - }, - { - "key": "userId", - "value": "1", - "type": "string" - }, - { - "key": "item", - "value": "" - }, - { - "key": "itemName", - "value": "" - }, - { - "key": "itemAvailable", - "value": "" - }, - { - "key": "itemDescription", - "value": "" - }, - { - "key": "itemId", - "value": "" - }, - { - "key": "searchString", - "value": "" - } - ] -} diff --git a/postman_for_shareit_15.json b/postman_for_shareit_16.json similarity index 81% rename from postman_for_shareit_15.json rename to postman_for_shareit_16.json index 26845fe..6e87b6b 100644 --- a/postman_for_shareit_15.json +++ b/postman_for_shareit_16.json @@ -1,10 +1,10 @@ { "info": { - "_postman_id": "069d256e-037e-4bd9-a5de-be6910726adc", - "name": "Sprint 15 ShareIt (add-bookings)", + "_postman_id": "7fc26681-0775-4307-885f-fd53560f0317", + "name": "Sprint 16 ShareIt (add-item-requests-and-gateway)", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "23073145", - "_collection_link": "https://universal-shadow-295426.postman.co/workspace/My-Workspace~4200f6aa-0504-44b1-8a1d-707d0dcbd5ce/collection/13708500-069d256e-037e-4bd9-a5de-be6910726adc?action=share&source=collection_link&creator=23073145" + "_collection_link": "https://universal-shadow-295426.postman.co/workspace/My-Workspace~4200f6aa-0504-44b1-8a1d-707d0dcbd5ce/collection/13708500-7fc26681-0775-4307-885f-fd53560f0317?action=share&source=collection_link&creator=23073145" }, "item": [ { @@ -921,10 +921,10 @@ ] }, { - "name": "items", + "name": "Item", "item": [ { - "name": "Item create", + "name": "Create Item", "event": [ { "listen": "prerequest", @@ -1027,7 +1027,7 @@ "response": [] }, { - "name": "Item create without X-Sharer-User-Id", + "name": "Create Item on request", "event": [ { "listen": "prerequest", @@ -1038,13 +1038,18 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", + " user1 = await api.addUser(rnd.getUser());\r", + " var request = await api.addRequest(rnd.getRequest(), user1.id);\r", + " pm.collectionVariables.set(\"requestId\", request.id);\r", + " user2 = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user2.id);\r", + "\r", " item = rnd.getItem();\r", " pm.collectionVariables.set(\"item\", item);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", + "\r", "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", @@ -1075,10 +1080,25 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Status code is 400 or 500\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([500, 400]);\r", + "pm.test(\"Status code is 200 or 201\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([200,201]);\r", "});\r", - "" + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "var item = pm.collectionVariables.get(\"item\");\r", + "\r", + "pm.test(\"Response data equal to request\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('id');\r", + " pm.expect(jsonData).to.have.property('name');\r", + " pm.expect(jsonData).to.have.property('description');\r", + " pm.expect(jsonData).to.have.property('available');\r", + " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);\r", + " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);\r", + " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());\r", + "});" ], "type": "text/javascript", "packages": {} @@ -1094,13 +1114,12 @@ }, { "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "disabled": true + "value": "{{userId}}" } ], "body": { "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}},\n \"requestId\": {{requestId}}\n}" }, "url": { "raw": "localhost:8080/items", @@ -1116,7 +1135,7 @@ "response": [] }, { - "name": "Item create with non-existent user", + "name": "Create Item without name on request", "event": [ { "listen": "prerequest", @@ -1127,13 +1146,18 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", + " user1 = await api.addUser(rnd.getUser());\r", + " var request = await api.addRequest(rnd.getRequest(), user1.id);\r", + " pm.collectionVariables.set(\"requestId\", request.id);\r", + " user2 = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user2.id);\r", + "\r", " item = rnd.getItem();\r", " pm.collectionVariables.set(\"item\", item);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id + '1');\r", + "\r", "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", @@ -1164,10 +1188,17 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Status code is 404\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([404]);\r", + "pm.test(\"Status code is 400\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", "});\r", - "" + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "pm.test(\"Response data have error\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('error');\r", + "});" ], "type": "text/javascript", "packages": {} @@ -1188,7 +1219,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" + "raw": "{\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}},\n \"requestId\": {{requestId}}\n}" }, "url": { "raw": "localhost:8080/items", @@ -1204,7 +1235,7 @@ "response": [] }, { - "name": "Item create without available field", + "name": "Create Item without description on request", "event": [ { "listen": "prerequest", @@ -1215,13 +1246,18 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", + " user1 = await api.addUser(rnd.getUser());\r", + " var request = await api.addRequest(rnd.getRequest(), user1.id);\r", + " pm.collectionVariables.set(\"requestId\", request.id);\r", + " user2 = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user2.id);\r", + "\r", " item = rnd.getItem();\r", " pm.collectionVariables.set(\"item\", item);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", + "\r", "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", @@ -1254,6 +1290,14 @@ "exec": [ "pm.test(\"Status code is 400\", function () {\r", " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "pm.test(\"Response data have error\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('error');\r", "});" ], "type": "text/javascript", @@ -1275,7 +1319,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\"\n}" + "raw": "{\n \"name\": \"{{itemName}}\",\n \"available\": {{itemAvailable}},\n \"requestId\": {{requestId}}\n}" }, "url": { "raw": "localhost:8080/items", @@ -1291,140 +1335,8 @@ "response": [] }, { - "name": "Item create with empty name field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"\",\n \"description\": \"Аккумуляторная отвертка\",\n \"available\": true\n}" - }, - "url": { - "raw": "{{baseUrl}}/items", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item create with empty description field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"Отвертка\",\n \"available\": true\n}" - }, - "url": { - "raw": "{{baseUrl}}/items", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item update", + "name": "Create Item without available on request", "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, { "listen": "prerequest", "script": { @@ -1434,17 +1346,19 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", + " user1 = await api.addUser(rnd.getUser());\r", + " var request = await api.addRequest(rnd.getRequest(), user1.id);\r", + " pm.collectionVariables.set(\"requestId\", request.id);\r", + " user2 = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user2.id);\r", + "\r", " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", + " pm.collectionVariables.set(\"item\", item);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", " pm.collectionVariables.set(\"itemDescription\", item.description);\r", "\r", + "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -1469,694 +1383,65 @@ "type": "text/javascript", "packages": {} } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update without X-Sharer-User-Id", - "event": [ { "listen": "test", "script": { "exec": [ - "pm.test(\"Status code is 500\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([500, 400]);", + "pm.test(\"Status code is 400\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "pm.test(\"Response data have error\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('error');\r", "});" ], "type": "text/javascript", "packages": {} } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text", - "disabled": true - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update with other user", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 404\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([404, 403]);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id + 1);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update available field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update description field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"description\": \"{{itemDescription}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update name field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } } ], "request": { - "method": "PATCH", + "method": "POST", "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, { "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item get", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = await api.addItem(rnd.getItem(), user.id)\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemId\", item.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" + "value": "application/json" }, - { - "key": "Accept", - "value": "*/*", - "type": "text" + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}" } ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"requestId\": {{requestId}}\n}" + }, "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", + "raw": "localhost:8080/items", "host": [ - "{{baseUrl}}" + "localhost" ], + "port": "8080", "path": [ - "items", - "{{itemId}}" + "items" ] } }, "response": [] - }, + } + ] + }, + { + "name": "requests", + "item": [ { - "name": "Get all items from user", + "name": "Create request", "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test list item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 2').to.eql(2);", - "});", - "", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, { "listen": "prerequest", "script": { @@ -2166,10 +1451,11 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", + " request1 = rnd.getRequest();\r", + " pm.collectionVariables.set(\"requestDescription\", request1.description);\r", " user = await api.addUser(rnd.getUser());\r", " pm.collectionVariables.set(\"userId\", user.id);\r", - " await api.addItem(rnd.getItem(), user.id)\r", - " await api.addItem(rnd.getItem(), user.id)\r", + "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -2194,57 +1480,65 @@ "type": "text/javascript", "packages": {} } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200 or 201\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([200,201]);\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "pm.test(\"Response data equal to request\", function () {\r", + " var description = pm.collectionVariables.get(\"requestDescription\");\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('id');\r", + " pm.expect(jsonData).to.have.property('description');\r", + " pm.expect(jsonData).to.have.property('created');\r", + " pm.expect(jsonData.description, `\"description\" must be ${description}`).to.eql(description);\r", + "});" + ], + "type": "text/javascript", + "packages": {} + } } ], "request": { - "method": "GET", + "method": "POST", "header": [ { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" }, { - "key": "Accept", - "value": "*/*", - "type": "text" + "key": "X-Sharer-User-Id", + "value": "{{userId}}" } ], + "body": { + "mode": "raw", + "raw": "{ \n \"description\": \"{{requestDescription}}\"\n}" + }, "url": { - "raw": "{{baseUrl}}/items", + "raw": "localhost:8080/requests", "host": [ - "{{baseUrl}}" + "localhost" ], + "port": "8080", "path": [ - "items" + "requests" ] } }, "response": [] }, { - "name": "Item search", + "name": "Get user requests", "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test list item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 1').to.eql(1);", - " pm.expect(jsonData[0].name.toUpperCase(), 'Name should include ' + pm.collectionVariables.get(\"searchString\")).to.eql(pm.collectionVariables.get(\"searchString\"))", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, { "listen": "prerequest", "script": { @@ -2256,15 +1550,9 @@ " try {\r", " user = await api.addUser(rnd.getUser());\r", " pm.collectionVariables.set(\"userId\", user.id);\r", - " it = rnd.getItem()\r", - " it.available = true\r", - " item = await api.addItem(it, user.id)\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemId\", item.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"searchString\", item.name.toUpperCase());\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + " request1 = await api.addRequest(rnd.getRequest(), user.id);\r", + " request2 = await api.addRequest(rnd.getRequest(), user.id);\r", + "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -2289,63 +1577,54 @@ "type": "text/javascript", "packages": {} } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {\r", + " pm.response.to.be.ok;\r", + "});\r", + "pm.test(\"User requests amount\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData.length, 'List length must be 2').to.eql(2);\r", + "});" + ], + "type": "text/javascript", + "packages": {} + } } ], "request": { "method": "GET", "header": [ { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" }, { - "key": "Accept", - "value": "*/*", - "type": "text" + "key": "X-Sharer-User-Id", + "value": "{{userId}}" } ], "url": { - "raw": "{{baseUrl}}/items/search?text={{searchString}}", + "raw": "localhost:8080/requests", "host": [ - "{{baseUrl}}" + "localhost" ], + "port": "8080", "path": [ - "items", - "search" - ], - "query": [ - { - "key": "text", - "value": "{{searchString}}" - } + "requests" ] } }, "response": [] }, { - "name": "Item search unavailable", + "name": "Get user request by id", "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test list item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 0').to.eql(0);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, { "listen": "prerequest", "script": { @@ -2355,17 +1634,13 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " it = rnd.getItem()\r", - " it.available = false\r", - " item = await api.addItem(it, user.id)\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemId\", item.id);\r", + " user1 = await api.addUser(rnd.getUser());\r", + " user2 = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user2.id);\r", + " req = await api.addRequest(rnd.getRequest(), user1.id);\r", + " pm.collectionVariables.set(\"requestId\", req.id);\r", + " item = await api.addItem(rnd.getItemForRequest(req.id), user2.id);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"searchString\", item.name.toUpperCase());\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -2390,59 +1665,28 @@ "type": "text/javascript", "packages": {} } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/items/search?text={{searchString}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "search" - ], - "query": [ - { - "key": "text", - "value": "{{searchString}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Item search empty", - "event": [ + }, { "listen": "test", "script": { "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test search item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 0').to.eql(0);", + "pm.test(\"Status code is 200\", function () {\r", + " pm.response.to.be.ok;\r", + "});\r", + "pm.test(\"User requests amount\", function () {\r", + " var name = pm.collectionVariables.get(\"itemName\");\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('id');\r", + " pm.expect(jsonData).to.have.property('description');\r", + " pm.expect(jsonData).to.have.property('created');\r", + " pm.expect(jsonData).to.have.property('items');\r", + " pm.expect(jsonData.items[0].name, `\"item name\" must be ${name}`).to.eql(name);\r", "});" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -2450,30 +1694,23 @@ "method": "GET", "header": [ { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" }, { - "key": "Accept", - "value": "*/*", - "type": "text" + "key": "X-Sharer-User-Id", + "value": "{{userId}}" } ], "url": { - "raw": "{{baseUrl}}/items/search?text=", + "raw": "localhost:8080/requests/{{requestId}}", "host": [ - "{{baseUrl}}" + "localhost" ], + "port": "8080", "path": [ - "items", - "search" - ], - "query": [ - { - "key": "text", - "value": "" - } + "requests", + "{{requestId}}" ] } }, @@ -4739,10 +3976,6 @@ } ], "variable": [ - { - "key": "baseUrl", - "value": "http://localhost:8080" - }, { "key": "userName", "value": "" @@ -4753,11 +3986,7 @@ }, { "key": "userId", - "value": "" - }, - { - "key": "item", - "value": "" + "value": "1" }, { "key": "itemName", @@ -4772,13 +4001,21 @@ "value": "" }, { - "key": "itemId", + "key": "item", + "value": "" + }, + { + "key": "requestId", "value": "" }, { - "key": "searchString", + "key": "requestDescription", "value": "" }, + { + "key": "baseUrl", + "value": "localhost:8080" + }, { "key": "start", "value": "" @@ -4787,6 +4024,10 @@ "key": "end", "value": "" }, + { + "key": "itemId", + "value": "" + }, { "key": "user1", "value": "" @@ -4797,7 +4038,7 @@ }, { "key": "bookingId", - "value": "" + "value": "1" }, { "key": "booking", diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000..0ff1817 --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,5 @@ +FROM eclipse-temurin:21-jre-jammy +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/server/pom.xml b/server/pom.xml new file mode 100644 index 0000000..566db3e --- /dev/null +++ b/server/pom.xml @@ -0,0 +1,86 @@ + + + 4.0.0 + + ru.practicum + shareit + 0.0.1-SNAPSHOT + + + shareit-server + 0.0.1-SNAPSHOT + + ShareIt Server + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.postgresql + postgresql + runtime + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + coverage + + + + org.jacoco + jacoco-maven-plugin + + + + + + + diff --git a/src/main/java/ru/practicum/shareit/ShareItApp.java b/server/src/main/java/ru/practicum/shareit/ShareItServer.java similarity index 57% rename from src/main/java/ru/practicum/shareit/ShareItApp.java rename to server/src/main/java/ru/practicum/shareit/ShareItServer.java index a10a87d..303541d 100644 --- a/src/main/java/ru/practicum/shareit/ShareItApp.java +++ b/server/src/main/java/ru/practicum/shareit/ShareItServer.java @@ -4,10 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class ShareItApp { +public class ShareItServer { - public static void main(String[] args) { - SpringApplication.run(ShareItApp.class, args); - } + public static void main(String[] args) { + SpringApplication.run(ShareItServer.class, args); + } } diff --git a/src/main/java/ru/practicum/shareit/booking/Booking.java b/server/src/main/java/ru/practicum/shareit/booking/Booking.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/Booking.java rename to server/src/main/java/ru/practicum/shareit/booking/Booking.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 97% rename from src/main/java/ru/practicum/shareit/booking/BookingController.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingController.java index 40df0bc..fd62891 100644 --- a/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -1,6 +1,5 @@ package ru.practicum.shareit.booking; -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -20,7 +19,7 @@ public class BookingController { */ @PostMapping public BookingDtoOutput addBooking(@RequestHeader("X-Sharer-User-Id") Long bookerId, - @Valid @RequestBody BookingDtoInput bookingDto) { + @RequestBody BookingDtoInput bookingDto) { return BookingMapper.toBookingDtoOutput(bookingService.addBooking(bookerId, bookingDto)); } diff --git a/src/main/java/ru/practicum/shareit/booking/BookingDto.java b/server/src/main/java/ru/practicum/shareit/booking/BookingDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/BookingDto.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingDto.java diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java b/server/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java new file mode 100644 index 0000000..382f6a2 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java @@ -0,0 +1,20 @@ +package ru.practicum.shareit.booking; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BookingDtoInput { + + private Long itemId; + + private LocalDateTime start; + + private LocalDateTime end; + +} diff --git a/src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java b/server/src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java diff --git a/src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java b/server/src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java diff --git a/src/main/java/ru/practicum/shareit/booking/BookingMapper.java b/server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/BookingMapper.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java 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 100% rename from src/main/java/ru/practicum/shareit/booking/BookingRepository.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingRepository.java 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 100% rename from src/main/java/ru/practicum/shareit/booking/BookingService.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingService.java 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 95% rename from src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java index 991eba2..d0f22f3 100644 --- a/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java @@ -203,7 +203,6 @@ public List getByOwnerId(Long userId, String state) { switch (bookingState) { case ALL: { result = bookingRepository.findAllByItem_OwnerOrderByStartDesc(bookerFromDb); - System.out.println(result); break; } case CURRENT: { @@ -253,18 +252,6 @@ private void validateBooking(BookingDto bookingDto, Item item, User booker) { throw new ValidationException(message); } - if (bookingDto.getStart().equals(bookingDto.getEnd())) { - String message = "Начало и окончание бронирования не может быть одним и тем же временем."; - log.info(message); - throw new ValidationException(message); - } - - if (bookingDto.getEnd().isBefore(bookingDto.getStart())) { - String message = "Окончание бронирования не может быть раньше его начала."; - log.info(message); - throw new ValidationException(message); - } - List bookings = item.getBookings(); if (!bookings.isEmpty()) { for (Booking b : bookings) { diff --git a/src/main/java/ru/practicum/shareit/booking/BookingState.java b/server/src/main/java/ru/practicum/shareit/booking/BookingState.java similarity index 57% rename from src/main/java/ru/practicum/shareit/booking/BookingState.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingState.java index 75be8b4..76499ce 100644 --- a/src/main/java/ru/practicum/shareit/booking/BookingState.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingState.java @@ -1,5 +1,5 @@ package ru.practicum.shareit.booking; public enum BookingState { - ALL, CURRENT, PAST, FUTURE, WAITING, REJECTED; + ALL, CURRENT, PAST, FUTURE, WAITING, REJECTED } diff --git a/src/main/java/ru/practicum/shareit/booking/BookingStatus.java b/server/src/main/java/ru/practicum/shareit/booking/BookingStatus.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/BookingStatus.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingStatus.java diff --git a/src/main/java/ru/practicum/shareit/exception/DataConflictException.java b/server/src/main/java/ru/practicum/shareit/exception/DataConflictException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/DataConflictException.java rename to server/src/main/java/ru/practicum/shareit/exception/DataConflictException.java diff --git a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/ErrorHandler.java rename to server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java 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/RestrictedAccessException.java b/server/src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java rename to server/src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java diff --git a/server/src/main/java/ru/practicum/shareit/exception/ValidationException.java b/server/src/main/java/ru/practicum/shareit/exception/ValidationException.java new file mode 100644 index 0000000..59043da --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/ValidationException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class ValidationException extends RuntimeException { + public ValidationException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/practicum/shareit/item/Comment.java b/server/src/main/java/ru/practicum/shareit/item/Comment.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/Comment.java rename to server/src/main/java/ru/practicum/shareit/item/Comment.java diff --git a/src/main/java/ru/practicum/shareit/item/CommentDto.java b/server/src/main/java/ru/practicum/shareit/item/CommentDto.java similarity index 51% rename from src/main/java/ru/practicum/shareit/item/CommentDto.java rename to server/src/main/java/ru/practicum/shareit/item/CommentDto.java index bd2daa4..fc9956b 100644 --- a/src/main/java/ru/practicum/shareit/item/CommentDto.java +++ b/server/src/main/java/ru/practicum/shareit/item/CommentDto.java @@ -1,12 +1,9 @@ package ru.practicum.shareit.item; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import ru.practicum.shareit.user.User; -import ru.practicum.shareit.validation.CreateObject; import java.time.LocalDateTime; @@ -17,8 +14,6 @@ public class CommentDto { private Long id; - @NotNull(groups = {CreateObject.class}, message = "комментарий должен быть указан.") - @NotBlank(groups = {CreateObject.class}, message = "Комментарий не может быть пустым.") private String text; private Item item; diff --git a/src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java b/server/src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java rename to server/src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java diff --git a/src/main/java/ru/practicum/shareit/item/CommentDtoShort.java b/server/src/main/java/ru/practicum/shareit/item/CommentDtoShort.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/CommentDtoShort.java rename to server/src/main/java/ru/practicum/shareit/item/CommentDtoShort.java diff --git a/src/main/java/ru/practicum/shareit/item/CommentMapper.java b/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/CommentMapper.java rename to server/src/main/java/ru/practicum/shareit/item/CommentMapper.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/src/main/java/ru/practicum/shareit/item/Item.java b/server/src/main/java/ru/practicum/shareit/item/Item.java similarity index 98% rename from src/main/java/ru/practicum/shareit/item/Item.java rename to server/src/main/java/ru/practicum/shareit/item/Item.java index 99e8100..bd80d79 100644 --- a/src/main/java/ru/practicum/shareit/item/Item.java +++ b/server/src/main/java/ru/practicum/shareit/item/Item.java @@ -39,7 +39,7 @@ public class Item { @JoinColumn(name = "owner_id", nullable = false) private User owner; - @OneToOne + @ManyToOne @JoinColumn(name = "request_id") private Request request; diff --git a/src/main/java/ru/practicum/shareit/item/ItemController.java b/server/src/main/java/ru/practicum/shareit/item/ItemController.java similarity index 85% rename from src/main/java/ru/practicum/shareit/item/ItemController.java rename to server/src/main/java/ru/practicum/shareit/item/ItemController.java index affabdd..c95d869 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemController.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -2,11 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import ru.practicum.shareit.validation.CreateObject; -import ru.practicum.shareit.validation.UpdateObject; - import java.util.List; @RestController @@ -17,14 +13,14 @@ public class ItemController { @PostMapping @ResponseStatus(HttpStatus.CREATED) - public ItemDtoOutput addItem(@Validated(CreateObject.class) @RequestBody ItemDto itemDto, + public ItemDtoOutput addItem(@RequestBody ItemDtoInput itemDto, @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { return ItemMapper.toItemDtoOutput(itemService.addItem(idUser, itemDto), idUser); } @PatchMapping("/{idItem}") public ItemDtoOutput updateItem(@PathVariable Long idItem, - @Validated(UpdateObject.class) @RequestBody ItemDto itemDto, + @RequestBody ItemDtoInput itemDto, @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { return ItemMapper.toItemDtoOutput(itemService.updateItem(idUser, idItem, itemDto), idUser); } @@ -56,7 +52,8 @@ public List searchItemsByText(@RequestParam String text, @PostMapping("/{itemId}/comment") public CommentDtoOutput addCommentToItem(@RequestHeader("X-Sharer-User-Id") Long userId, - @PathVariable Long itemId, @RequestBody CommentDto commentDto) { + @PathVariable Long itemId, + @RequestBody CommentDto commentDto) { return CommentMapper.toCommentDtoOutput((itemService.saveComment(userId, itemId, commentDto))); } } diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDto.java b/server/src/main/java/ru/practicum/shareit/item/ItemDto.java new file mode 100644 index 0000000..4f5f206 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemDto.java @@ -0,0 +1,37 @@ +package ru.practicum.shareit.item; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.request.RequestDto; +import ru.practicum.shareit.user.User; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemDto { + + private Long id; + + private String name; + + private String description; + + private Boolean available; + + private User owner; + + private RequestDto request; + + private List comments; + + private List bookings; + + private Booking lastBooking; + + private Booking nextBooking; + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java b/server/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java new file mode 100644 index 0000000..97209f5 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java @@ -0,0 +1,20 @@ +package ru.practicum.shareit.item; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemDtoInput { + + private String name; + + private String description; + + private Boolean available; + + private Long requestId; + +} diff --git a/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java b/server/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java similarity index 88% rename from src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java rename to server/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java index 36ff82c..c68c82f 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java @@ -4,7 +4,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import ru.practicum.shareit.booking.BookingDtoShort; -import ru.practicum.shareit.request.Request; +import ru.practicum.shareit.request.RequestDto; import ru.practicum.shareit.user.UserDtoShort; import java.util.List; @@ -24,7 +24,7 @@ public class ItemDtoOutput { private UserDtoShort owner; - private Request request; + private RequestDto request; private List comments; diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java b/server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java new file mode 100644 index 0000000..95ffbee --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java @@ -0,0 +1,20 @@ +package ru.practicum.shareit.item; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemDtoRequest { + + private Long idItem; + + private String name; + + private Long idOwner; + + private String Owner; + +} diff --git a/src/main/java/ru/practicum/shareit/item/ItemDtoShort.java b/server/src/main/java/ru/practicum/shareit/item/ItemDtoShort.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/ItemDtoShort.java rename to server/src/main/java/ru/practicum/shareit/item/ItemDtoShort.java diff --git a/src/main/java/ru/practicum/shareit/item/ItemMapper.java b/server/src/main/java/ru/practicum/shareit/item/ItemMapper.java similarity index 89% rename from src/main/java/ru/practicum/shareit/item/ItemMapper.java rename to server/src/main/java/ru/practicum/shareit/item/ItemMapper.java index d076fa1..9f81b4e 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemMapper.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemMapper.java @@ -3,6 +3,7 @@ import ru.practicum.shareit.booking.Booking; import ru.practicum.shareit.booking.BookingMapper; import ru.practicum.shareit.booking.BookingStatus; +import ru.practicum.shareit.request.RequestMapper; import ru.practicum.shareit.user.UserMapper; import java.time.LocalDateTime; @@ -24,7 +25,7 @@ public static ItemDto toItemDto(Item item) { itemDto.setDescription(item.getDescription()); itemDto.setAvailable(item.getAvailable()); itemDto.setOwner(item.getOwner()); - itemDto.setRequest(item.getRequest()); + itemDto.setRequest(RequestMapper.toRequestDto(item.getRequest())); itemDto.setComments(item.getComments()); itemDto.setBookings(item.getBookings()); @@ -42,7 +43,7 @@ public static ItemDtoOutput toItemDtoOutput(Item item, Long userId) { itemDto.setDescription(item.getDescription()); itemDto.setAvailable(item.getAvailable()); itemDto.setOwner(UserMapper.toUserDtoShort(item.getOwner())); - itemDto.setRequest(item.getRequest()); + itemDto.setRequest(RequestMapper.toRequestDto(item.getRequest())); List comments = Optional.ofNullable(item.getComments()).orElse(Collections.emptyList()); if (!comments.isEmpty()) { itemDto.setComments(comments.stream().map(CommentMapper::toCommentDtoShort).toList()); @@ -83,6 +84,20 @@ public static ItemDtoShort toItemDtoShort(Item item) { return itemDto; } + public static ItemDtoRequest toItemDtoRequest(Item item) { + if (item == null) { + return null; + } + ItemDtoRequest itemDto = new ItemDtoRequest(); + + itemDto.setIdItem(item.getId()); + itemDto.setName(item.getName()); + itemDto.setIdOwner(item.getOwner().getId()); + itemDto.setOwner(item.getOwner().getName()); + + return itemDto; + } + public static Item toItem(ItemDto itemDto) { Item item = new Item(); @@ -107,7 +122,7 @@ public static Item toItem(ItemDto itemDto) { } if (itemDto.getRequest() != null) { if (itemDto.getRequest().getId() > 0) { - item.setRequest(itemDto.getRequest()); + item.setRequest(RequestMapper.toRequest(itemDto.getRequest())); } } if (itemDto.getComments() != null) { diff --git a/src/main/java/ru/practicum/shareit/item/ItemRepository.java b/server/src/main/java/ru/practicum/shareit/item/ItemRepository.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/ItemRepository.java rename to server/src/main/java/ru/practicum/shareit/item/ItemRepository.java 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 73% rename from src/main/java/ru/practicum/shareit/item/ItemService.java rename to server/src/main/java/ru/practicum/shareit/item/ItemService.java index ba5bf8a..c72f72e 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemService.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemService.java @@ -4,9 +4,9 @@ public interface ItemService { - Item addItem(Long idUser, ItemDto itemDto); + Item addItem(Long idUser, ItemDtoInput itemDto); - Item updateItem(Long idUser, Long idItem, ItemDto itemDto); + Item updateItem(Long idUser, Long idItem, ItemDtoInput itemDto); Item getItemById(Long idItem); 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 86% rename from src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java rename to server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java index edaf54d..e939b0e 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.item; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; @@ -7,6 +8,8 @@ import ru.practicum.shareit.exception.NotFoundException; import ru.practicum.shareit.exception.RestrictedAccessException; import ru.practicum.shareit.exception.ValidationException; +import ru.practicum.shareit.request.RequestMapper; +import ru.practicum.shareit.request.RequestRepository; import ru.practicum.shareit.user.User; import ru.practicum.shareit.user.UserMapper; import ru.practicum.shareit.user.UserRepository; @@ -17,35 +20,41 @@ @Slf4j @Service +@RequiredArgsConstructor public class ItemServiceImpl implements ItemService { private final ItemRepository itemRepository; private final UserRepository userRepository; private final CommentRepository commentRepository; - - public ItemServiceImpl(ItemRepository itemRepository, UserRepository userRepository, CommentRepository commentRepository) { - this.itemRepository = itemRepository; - this.userRepository = userRepository; - this.commentRepository = commentRepository; - } + private final RequestRepository requestRepository; /** * Метод для добавления новой вещи. * * @param idUser идентификатор пользователя - * @param itemDto объект ItemDto, содержащий данные новой вещи + * @param inputItemDto объект ItemDtoInput, содержащий данные новой вещи * @return объект Item, содержащий данные добавленной вещи * @throws ValidationException если идентификатор пользователя не указан * @throws NotFoundException если пользователь с указанным id не найден в БД */ @Override - public Item addItem(Long idUser, ItemDto itemDto) { - if (userRepository.findById(idUser).isEmpty()) { + public Item addItem(Long idUser, ItemDtoInput inputItemDto) { + ItemDto itemDto = new ItemDto(); + User user = userRepository.findById(idUser).orElse(null); + if (user == null) { String error = "Пользователь с id [ " + idUser + " ] не найден в БД при добавлении вещи."; log.info(error); throw new NotFoundException(error); } - itemDto.setOwner(userRepository.findById(idUser).get()); + itemDto.setOwner(user); + itemDto.setName(inputItemDto.getName()); + itemDto.setDescription(inputItemDto.getDescription()); + itemDto.setAvailable(inputItemDto.getAvailable()); + if (inputItemDto.getRequestId() == null) { + itemDto.setRequest(null); + } else { + itemDto.setRequest(RequestMapper.toRequestDto(requestRepository.findById(inputItemDto.getRequestId()).orElse(null))); + } Item result = ItemMapper.toItem(itemDto); itemRepository.save(result); log.info("Добавлена вещь [ {} ] пользователем [ {} ]", result.getId(), idUser); @@ -57,14 +66,15 @@ public Item addItem(Long idUser, ItemDto itemDto) { * * @param idUser идентификатор пользователя * @param idItem идентификатор вещи - * @param itemDto объект ItemDto, содержащий новые данные вещи + * @param itemDtoInput объект ItemDtoInput, содержащий новые данные вещи * @return объект Item, содержащий обновленные данные вещи * @throws ValidationException если идентификатор пользователя не указан * @throws NotFoundException если пользователь или вещь с указанным id не найдены в БД * @throws RestrictedAccessException если пользователь не является владельцем вещи */ @Override - public Item updateItem(Long idUser, Long idItem, ItemDto itemDto) { + public Item updateItem(Long idUser, Long idItem, ItemDtoInput itemDtoInput) { + ItemDto itemDto = new ItemDto(); if (userRepository.findById(idUser).isEmpty()) { String error = "Пользователь с id [ " + idUser + " ] не найден в БД при изменение данных вещи."; log.info(error); @@ -83,15 +93,21 @@ public Item updateItem(Long idUser, Long idItem, ItemDto itemDto) { } itemDto.setId(idItem); itemDto.setOwner(oldItem.get().getOwner()); - itemDto.setRequest(oldItem.get().getRequest()); - if (itemDto.getName() == null) { + itemDto.setRequest(RequestMapper.toRequestDto(oldItem.get().getRequest())); + if (itemDtoInput.getName() == null) { itemDto.setName(oldItem.get().getName()); + } else { + itemDto.setName(itemDtoInput.getName()); } - if (itemDto.getDescription() == null) { + if (itemDtoInput.getDescription() == null) { itemDto.setDescription(oldItem.get().getDescription()); + } else { + itemDto.setDescription(itemDtoInput.getDescription()); } - if (itemDto.getAvailable() == null) { + if (itemDtoInput.getAvailable() == null) { itemDto.setAvailable(oldItem.get().getAvailable()); + } else { + itemDto.setAvailable(itemDtoInput.getAvailable()); } Item newItem = ItemMapper.toItem(itemDto); itemRepository.save(newItem); diff --git a/src/main/java/ru/practicum/shareit/request/Request.java b/server/src/main/java/ru/practicum/shareit/request/Request.java similarity index 88% rename from src/main/java/ru/practicum/shareit/request/Request.java rename to server/src/main/java/ru/practicum/shareit/request/Request.java index 2e67520..5947b30 100644 --- a/src/main/java/ru/practicum/shareit/request/Request.java +++ b/server/src/main/java/ru/practicum/shareit/request/Request.java @@ -6,6 +6,7 @@ import ru.practicum.shareit.user.User; import java.time.LocalDateTime; +import java.util.List; @Getter @Setter @@ -27,9 +28,9 @@ public class Request { @JoinColumn(name = "requestor_id", nullable = false) private User requestor; - @OneToOne(mappedBy = "request") - private Item requestItem; - @Column(nullable = false) private LocalDateTime created; + + @OneToMany(mappedBy = "request") + private List items; } diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestController.java b/server/src/main/java/ru/practicum/shareit/request/RequestController.java new file mode 100644 index 0000000..d3c49ec --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestController.java @@ -0,0 +1,39 @@ +package ru.practicum.shareit.request; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * TODO Sprint add-item-requests. + */ +@RestController +@RequestMapping(path = "/requests") +@RequiredArgsConstructor +public class RequestController { + + private final RequestService requestService; + + @PostMapping + public RequestDto addRequest(@RequestHeader("X-Sharer-User-Id") Long requestorId, + @RequestBody RequestDtoInput requestDto) { + return RequestMapper.toRequestDto(requestService.addRequest(requestorId, requestDto)); + } + + @GetMapping + public List getRequests(@RequestHeader("X-Sharer-User-Id") Long requestorId) { + return requestService.getRequests(requestorId).stream().map(RequestMapper::toRequestDtoItems).toList(); + } + + @GetMapping("/all") + public List getAllRequests(@RequestHeader("X-Sharer-User-Id") Long requestorId) { + return requestService.getAllRequests(requestorId).stream().map(RequestMapper::toRequestDto).toList(); + } + + @GetMapping("/{requestId}") + public RequestDtoItems getRequestsById(@PathVariable Long requestId) { + return RequestMapper.toRequestDtoItems(requestService.getRequestById(requestId)); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequestDto.java b/server/src/main/java/ru/practicum/shareit/request/RequestDto.java similarity index 87% rename from src/main/java/ru/practicum/shareit/request/ItemRequestDto.java rename to server/src/main/java/ru/practicum/shareit/request/RequestDto.java index 16d55ee..a49428d 100644 --- a/src/main/java/ru/practicum/shareit/request/ItemRequestDto.java +++ b/server/src/main/java/ru/practicum/shareit/request/RequestDto.java @@ -10,11 +10,14 @@ @Data @AllArgsConstructor @NoArgsConstructor -public class ItemRequestDto { +public class RequestDto { + + private Long id; private String description; private User requestor; private LocalDateTime created; + } diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java b/server/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java new file mode 100644 index 0000000..8d44bc6 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java @@ -0,0 +1,14 @@ +package ru.practicum.shareit.request; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RequestDtoInput { + + private String description; + +} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestDtoItems.java b/server/src/main/java/ru/practicum/shareit/request/RequestDtoItems.java new file mode 100644 index 0000000..178918b --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestDtoItems.java @@ -0,0 +1,26 @@ +package ru.practicum.shareit.request; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.item.ItemDtoRequest; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RequestDtoItems { + + private Long id; + + private String description; + + private User requestor; + + private LocalDateTime created; + + List items; +} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestMapper.java b/server/src/main/java/ru/practicum/shareit/request/RequestMapper.java new file mode 100644 index 0000000..9608ce2 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestMapper.java @@ -0,0 +1,62 @@ +package ru.practicum.shareit.request; + +import ru.practicum.shareit.item.ItemMapper; + +public class RequestMapper { + + public static RequestDto toRequestDto(Request request) { + if (request == null) { + return null; + } + RequestDto requestDto = new RequestDto(); + + requestDto.setId(request.getId()); + requestDto.setDescription(request.getDescription()); + requestDto.setRequestor(request.getRequestor()); + requestDto.setCreated(request.getCreated()); + + return requestDto; + } + + public static RequestDtoItems toRequestDtoItems(Request request) { + if (request == null) { + return null; + } + RequestDtoItems requestDto = new RequestDtoItems(); + + requestDto.setId(request.getId()); + requestDto.setDescription(request.getDescription()); + requestDto.setRequestor(request.getRequestor()); + requestDto.setCreated(request.getCreated()); + requestDto.setItems(request.getItems().stream().map(ItemMapper::toItemDtoRequest).toList()); + + return requestDto; + } + + + public static Request toRequest(RequestDto requestDto) { + Request request = new Request(); + + if (requestDto.getId() != null) { + if (requestDto.getId() > 0) { + request.setId(requestDto.getId()); + } + } + if (requestDto.getDescription() != null) { + if (!requestDto.getDescription().trim().isEmpty()) { + request.setDescription(requestDto.getDescription().trim()); + } + } + if (requestDto.getRequestor().getId() != null) { + if (requestDto.getRequestor().getId() > 0) { + request.setRequestor(requestDto.getRequestor()); + } + } + if (requestDto.getCreated() != null) { + request.setCreated(requestDto.getCreated()); + } + + return request; + } + +} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestRepository.java b/server/src/main/java/ru/practicum/shareit/request/RequestRepository.java new file mode 100644 index 0000000..db723d3 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestRepository.java @@ -0,0 +1,17 @@ +package ru.practicum.shareit.request; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.practicum.shareit.user.User; + +import java.util.List; + +@Repository +public interface RequestRepository extends JpaRepository { + + List findAllByRequestorOrderByCreatedDesc(User requestor); + + List findAllByOrderByCreatedDesc(); + + List findAllByRequestorNotOrderByCreatedDesc(User requestor); +} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestService.java b/server/src/main/java/ru/practicum/shareit/request/RequestService.java new file mode 100644 index 0000000..6e3bba3 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestService.java @@ -0,0 +1,15 @@ +package ru.practicum.shareit.request; + +import java.util.List; + +public interface RequestService { + + Request addRequest(Long requestorId, RequestDtoInput requestDto); + + List getRequests(Long requestorId); + + List getAllRequests(Long requestorId); + + Request getRequestById(Long requestId); + +} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java b/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java new file mode 100644 index 0000000..60dae61 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java @@ -0,0 +1,107 @@ +package ru.practicum.shareit.request; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.item.ItemRepository; +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.UserRepository; + +import java.time.LocalDateTime; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class RequestServiceImpl implements RequestService { + + private final RequestRepository requestRepository; + private final UserRepository userRepository; + + /** + * Добавляет новый запрос в базу данных. + * + * @param requestorId идентификатор пользователя, который создает запрос + * @param requestDto объект, содержащий данные для создания запроса + * @return созданный запрос + * @throws NotFoundException если пользователь с указанным идентификатором не найден в базе данных + */ + @Override + public Request addRequest(Long requestorId, RequestDtoInput requestDto) { + User requestor = new User(); + Request request = new Request(); + requestor = userRepository.findById(requestorId).orElse(null); + if (requestor == null) { + String error = "Пользователь с id [ " + requestorId + " ] не найден в БД при добавлении запроса."; + log.info(error); + throw new NotFoundException(error); + } + request.setDescription(requestDto.getDescription()); + request.setRequestor(requestor); + request.setCreated(LocalDateTime.now()); + request = requestRepository.save(request); + log.info("Запрос [ {} ] добавлен в БД.",request); + return request; + } + + /** + * Возвращает список запросов, созданных определенным пользователем, отсортированный по дате создания в обратном порядке. + * + * @param requestorId идентификатор пользователя, чьи запросы нужно получить + * @return список запросов, созданных пользователем + */ + @Override + public List getRequests(Long requestorId) { + User requestor = new User(); + requestor = userRepository.findById(requestorId).orElse(null); + if (requestor == null) { + log.info("Пользователь с id [ {} ] не найден в БД при получении своего списка запросов - вернулся пустой список.", requestorId); + return List.of(); + } + List requests = requestRepository.findAllByRequestorOrderByCreatedDesc(requestor); + log.info("Список запросов пользователя [ {} ] получен из БД в количестве [ {} ].", requestorId, requests.size()); + return requests; + } + + /** + * Возвращает список всех запросов, отсортированных по дате создания в обратном порядке. + * Если передан идентификатор пользователя, то возвращаются все запросы, + * кроме тех, которые были созданы этим пользователем. + * + * @param requestorId идентификатор пользователя, чьи запросы нужно исключить из списка (может быть null) + * @return список всех запросов, отсортированных по дате создания в обратном порядке + */ + @Override + public List getAllRequests(Long requestorId) { + User requestor = new User(); + requestor = userRepository.findById(requestorId).orElse(null); + if (requestor == null) { + List requests = requestRepository.findAllByOrderByCreatedDesc(); + log.info("Список доступных запросов получен из БД в количестве [ {} ].", requests.size()); + return requests; + } + List requests = requestRepository.findAllByRequestorNotOrderByCreatedDesc(requestor); + log.info("Список доступных запросов ( кроме запросов пользователя [ {} ] ) получен из БД в количестве [ {} ].", + requestorId, requests.size()); + return requests; + } + + /** + * Возвращает запрос по его идентификатору. + * + * @param requestId идентификатор запроса + * @return запрос с указанным идентификатором + * @throws NotFoundException если запрос с указанным идентификатором не найден в базе данных + */ + @Override + public Request getRequestById(Long requestId) { + Request request = requestRepository.findById(requestId).orElse(null); + if (request == null) { + String error = "Запрос с id [ " + requestId + " ] не найден в БД при получении."; + log.info(error); + throw new NotFoundException(error); + } + return request; + } +} 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 100% rename from src/main/java/ru/practicum/shareit/user/User.java rename to server/src/main/java/ru/practicum/shareit/user/User.java 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 73% rename from src/main/java/ru/practicum/shareit/user/UserController.java rename to server/src/main/java/ru/practicum/shareit/user/UserController.java index 38dff38..98c0885 100644 --- a/src/main/java/ru/practicum/shareit/user/UserController.java +++ b/server/src/main/java/ru/practicum/shareit/user/UserController.java @@ -2,10 +2,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -import ru.practicum.shareit.validation.CreateObject; -import ru.practicum.shareit.validation.UpdateObject; + import java.util.List; @@ -17,12 +15,13 @@ public class UserController { @PostMapping @ResponseStatus(HttpStatus.CREATED) - public UserDtoOutput addUser(@Validated(CreateObject.class) @RequestBody UserDto userDto) { + public UserDtoOutput addUser(@RequestBody UserDto userDto) { return UserMapper.toUserDtoOutput(userService.addUser(userDto)); } @PatchMapping("/{idUser}") - public UserDtoOutput updateUser(@PathVariable Long idUser, @Validated(UpdateObject.class) @RequestBody UserDto userDto) { + public UserDtoOutput updateUser(@PathVariable Long idUser, + @RequestBody UserDto userDto) { return UserMapper.toUserDtoOutput(userService.updateUser(idUser, userDto)); } diff --git a/server/src/main/java/ru/practicum/shareit/user/UserDto.java b/server/src/main/java/ru/practicum/shareit/user/UserDto.java new file mode 100644 index 0000000..924c595 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/UserDto.java @@ -0,0 +1,29 @@ +package ru.practicum.shareit.user; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.item.Comment; +import ru.practicum.shareit.item.Item; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserDto { + + private Long id; + + private String name; + + private String email; + + private List items; + + private List bookings; + + private List comments; + +} diff --git a/src/main/java/ru/practicum/shareit/user/UserDtoOutput.java b/server/src/main/java/ru/practicum/shareit/user/UserDtoOutput.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/UserDtoOutput.java rename to server/src/main/java/ru/practicum/shareit/user/UserDtoOutput.java diff --git a/src/main/java/ru/practicum/shareit/user/UserDtoShort.java b/server/src/main/java/ru/practicum/shareit/user/UserDtoShort.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/UserDtoShort.java rename to server/src/main/java/ru/practicum/shareit/user/UserDtoShort.java diff --git a/src/main/java/ru/practicum/shareit/user/UserMapper.java b/server/src/main/java/ru/practicum/shareit/user/UserMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/UserMapper.java rename to server/src/main/java/ru/practicum/shareit/user/UserMapper.java 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 100% rename from src/main/java/ru/practicum/shareit/user/UserService.java rename to server/src/main/java/ru/practicum/shareit/user/UserService.java 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 100% rename from src/main/java/ru/practicum/shareit/user/UserServiceImpl.java rename to server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties new file mode 100644 index 0000000..0f5ef57 --- /dev/null +++ b/server/src/main/resources/application.properties @@ -0,0 +1,17 @@ +server.port=9090 + +spring.jpa.hibernate.ddl-auto=none +spring.jpa.properties.hibernate.format_sql=true +spring.sql.init.mode=always + +#--- +spring.datasource.driverClassName=org.postgresql.Driver +spring.datasource.url=jdbc:postgresql://localhost:5432/shareit +spring.datasource.username=shareituser +spring.datasource.password=12345678 +#--- +spring.config.activate.on-profile=test +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.url=jdbc:h2:mem:shareit +spring.datasource.username=shareit +spring.datasource.password=shareit \ No newline at end of file diff --git a/src/main/resources/schema.sql b/server/src/main/resources/schema.sql similarity index 100% rename from src/main/resources/schema.sql rename to server/src/main/resources/schema.sql diff --git a/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java b/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java deleted file mode 100644 index b39e330..0000000 --- a/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.practicum.shareit.booking; - -import jakarta.validation.constraints.FutureOrPresent; -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.shareit.validation.CreateObject; - -import java.time.LocalDateTime; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class BookingDtoInput { - - @NotNull(groups = {CreateObject.class}, message = "При создании брони должна быть информация о вещи.") - private Long itemId; - - @FutureOrPresent(groups = {CreateObject.class}, message = "Дата не должна быть в прошлом") - @NotNull(groups = {CreateObject.class}, message = "Дата не должна быть пустой") - private LocalDateTime start; - - @FutureOrPresent(groups = {CreateObject.class}, message = "Дата не должна быть в прошлом") - @NotNull(groups = {CreateObject.class}, message = "Дата не должна быть пустой") - private LocalDateTime end; - -} 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 064e2e9..0000000 --- a/src/main/java/ru/practicum/shareit/request/ItemRequestController.java +++ /dev/null @@ -1,12 +0,0 @@ -package ru.practicum.shareit.request; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * TODO Sprint add-item-requests. - */ -@RestController -@RequestMapping(path = "/requests") -public class ItemRequestController { -} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml deleted file mode 100644 index faf6257..0000000 --- a/src/main/resources/application.yaml +++ /dev/null @@ -1,20 +0,0 @@ -spring: - application.name: shareit - main.banner-mode: off - datasource: - url: jdbc:postgresql://localhost:5432/shareit - driver-class-name: org.postgresql.Driver - username: shareituser - password: 12345678 - jpa: - show-sql: true - hibernate: - ddl-auto: update - properties: - hibernate.jdbc.time_zone: UTC - dialect: org.hibernate.dialect.PostgreSQL95Dialect - format_sql: true - sql: - init: - mode: always - From fe2cbe3dc5b7124c5a01c5d5c2b6450b2af54120 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 20:50:29 +0300 Subject: [PATCH 02/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9F=D0=B5=D1=80=D0=B2=D0=B8=D1=87=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- checkstyle.xml | 257 ----------------------------------------------- suppressions.xml | 7 -- 2 files changed, 264 deletions(-) delete mode 100644 checkstyle.xml delete mode 100644 suppressions.xml diff --git a/checkstyle.xml b/checkstyle.xml deleted file mode 100644 index 05d5086..0000000 --- a/checkstyle.xml +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/suppressions.xml b/suppressions.xml deleted file mode 100644 index b2c6822..0000000 --- a/suppressions.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - \ No newline at end of file From 50aeb407e5fe954709dd18576bafd71e040c6b09 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 20:59:01 +0300 Subject: [PATCH 03/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9F=D0=B5=D1=80=D0=B2=D0=B8=D1=87=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index 79cba37..0095bb0 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,6 @@ com.puppycrawl.tools checkstyle - 10.3 From 9d8d0ed4ba1dae2293aea62644a24fd9a1ecbfc3 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 21:10:27 +0300 Subject: [PATCH 04/24] =?UTF-8?q?Revert=20"=D0=92=D1=8B=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D0=BD=D1=82=D0=B0."?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 50aeb407e5fe954709dd18576bafd71e040c6b09. --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 0095bb0..79cba37 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,7 @@ com.puppycrawl.tools checkstyle + 10.3 From 7289d4d5f60f32b055f895bfd225899130d1954d Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 21:11:49 +0300 Subject: [PATCH 05/24] =?UTF-8?q?Revert=20"=D0=92=D1=8B=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D0=BD=D1=82=D0=B0."?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit fe2cbe3dc5b7124c5a01c5d5c2b6450b2af54120. --- checkstyle.xml | 257 +++++++++++++++++++++++++++++++++++++++++++++++ suppressions.xml | 7 ++ 2 files changed, 264 insertions(+) create mode 100644 checkstyle.xml create mode 100644 suppressions.xml diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 0000000..05d5086 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/suppressions.xml b/suppressions.xml new file mode 100644 index 0000000..b2c6822 --- /dev/null +++ b/suppressions.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file From 209dd47ed128c1e3f355da4884b92d94a5db420d Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 21:12:00 +0300 Subject: [PATCH 06/24] =?UTF-8?q?Revert=20"=D0=92=D1=8B=D0=BF=D0=BE=D0=BB?= =?UTF-8?q?=D0=BD=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8?= =?UTF-8?q?=D0=BD=D1=82=D0=B0."?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 38dd6791ba496e42323d1e633501a812b90e4449. --- README.md | 6 +- checkstyle.xml | 2 +- docker-compose.yml | 39 - gateway/Dockerfile | 5 - gateway/pom.xml | 70 - .../ru/practicum/shareit/ShareItGateway.java | 12 - .../shareit/booking/BookingClient.java | 58 - .../shareit/booking/BookingController.java | 100 - .../shareit/booking/BookingDtoInput.java | 28 - .../shareit/booking/BookingState.java | 16 - .../practicum/shareit/client/BaseClient.java | 121 - .../shareit/exception/ErrorHandler.java | 65 - .../shareit/item/CommentDtoInput.java | 18 - .../ru/practicum/shareit/item/ItemClient.java | 54 - .../shareit/item/ItemController.java | 122 - .../shareit/request/RequestClient.java | 41 - .../shareit/request/RequestController.java | 73 - .../shareit/request/RequestDtoInput.java | 18 - .../ru/practicum/shareit/user/UserClient.java | 46 - .../shareit/user/UserController.java | 81 - .../src/main/resources/application.properties | 7 - pom.xml | 97 +- postman_for_shareit_14.json | 2725 +++++++++++++++++ ...eit_16.json => postman_for_shareit_15.json | 1227 ++++++-- server/Dockerfile | 5 - server/pom.xml | 86 - .../shareit/booking/BookingDtoInput.java | 20 - .../exception/ValidationException.java | 7 - .../ru/practicum/shareit/item/ItemDto.java | 37 - .../practicum/shareit/item/ItemDtoInput.java | 20 - .../shareit/item/ItemDtoRequest.java | 20 - .../shareit/request/RequestController.java | 39 - .../shareit/request/RequestDtoInput.java | 14 - .../shareit/request/RequestDtoItems.java | 26 - .../shareit/request/RequestMapper.java | 62 - .../shareit/request/RequestRepository.java | 17 - .../shareit/request/RequestService.java | 15 - .../shareit/request/RequestServiceImpl.java | 107 - .../ru/practicum/shareit/user/UserDto.java | 29 - .../src/main/resources/application.properties | 17 - .../java/ru/practicum/shareit/ShareItApp.java | 8 +- .../ru/practicum/shareit/booking/Booking.java | 0 .../shareit/booking/BookingController.java | 3 +- .../practicum/shareit/booking/BookingDto.java | 0 .../shareit/booking/BookingDtoInput.java | 28 + .../shareit/booking/BookingDtoOutput.java | 0 .../shareit/booking/BookingDtoShort.java | 0 .../shareit/booking/BookingMapper.java | 0 .../shareit/booking/BookingRepository.java | 0 .../shareit/booking/BookingService.java | 0 .../shareit/booking/BookingServiceImpl.java | 13 + .../shareit/booking/BookingState.java | 2 +- .../shareit/booking/BookingStatus.java | 0 .../exception/DataConflictException.java | 0 .../shareit/exception/ErrorHandler.java | 0 .../shareit/exception/NotFoundException.java | 0 .../exception/RestrictedAccessException.java | 0 .../exception/ValidationException.java | 0 .../ru/practicum/shareit/item/Comment.java | 0 .../ru/practicum/shareit/item/CommentDto.java | 5 + .../shareit/item/CommentDtoOutput.java | 0 .../shareit/item/CommentDtoShort.java | 0 .../practicum/shareit/item/CommentMapper.java | 0 .../shareit/item/CommentRepository.java | 0 .../java/ru/practicum/shareit/item/Item.java | 2 +- .../shareit/item/ItemController.java | 11 +- .../ru/practicum/shareit/item/ItemDto.java | 21 +- .../practicum/shareit/item/ItemDtoOutput.java | 4 +- .../practicum/shareit/item/ItemDtoShort.java | 0 .../ru/practicum/shareit/item/ItemMapper.java | 21 +- .../shareit/item/ItemRepository.java | 0 .../practicum/shareit/item/ItemService.java | 4 +- .../shareit/item/ItemServiceImpl.java | 48 +- .../request/ItemRequestController.java | 12 + .../shareit/request/ItemRequestDto.java | 5 +- .../ru/practicum/shareit/request/Request.java | 7 +- .../java/ru/practicum/shareit/user/User.java | 0 .../shareit/user/UserController.java | 9 +- .../ru/practicum/shareit/user/UserDto.java | 18 +- .../practicum/shareit/user/UserDtoOutput.java | 0 .../practicum/shareit/user/UserDtoShort.java | 0 .../ru/practicum/shareit/user/UserMapper.java | 0 .../shareit/user/UserRepository.java | 0 .../practicum/shareit/user/UserService.java | 0 .../shareit/user/UserServiceImpl.java | 0 .../shareit/validation/CreateObject.java | 0 .../shareit/validation/UpdateObject.java | 0 src/main/resources/application.yaml | 20 + {server/src => src}/main/resources/schema.sql | 0 89 files changed, 3958 insertions(+), 1835 deletions(-) delete mode 100644 docker-compose.yml delete mode 100644 gateway/Dockerfile delete mode 100644 gateway/pom.xml delete mode 100644 gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/BookingState.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/item/CommentDtoInput.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/item/ItemController.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/request/RequestController.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/user/UserClient.java delete mode 100644 gateway/src/main/java/ru/practicum/shareit/user/UserController.java delete mode 100644 gateway/src/main/resources/application.properties create mode 100644 postman_for_shareit_14.json rename postman_for_shareit_16.json => postman_for_shareit_15.json (81%) delete mode 100644 server/Dockerfile delete mode 100644 server/pom.xml delete mode 100644 server/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java delete mode 100644 server/src/main/java/ru/practicum/shareit/exception/ValidationException.java delete mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemDto.java delete mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java delete mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java delete mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestController.java delete mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java delete mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestDtoItems.java delete mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestMapper.java delete mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestRepository.java delete mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestService.java delete mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java delete mode 100644 server/src/main/java/ru/practicum/shareit/user/UserDto.java delete mode 100644 server/src/main/resources/application.properties rename server/src/main/java/ru/practicum/shareit/ShareItServer.java => src/main/java/ru/practicum/shareit/ShareItApp.java (57%) rename {server/src => src}/main/java/ru/practicum/shareit/booking/Booking.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/booking/BookingController.java (97%) rename {server/src => src}/main/java/ru/practicum/shareit/booking/BookingDto.java (100%) create mode 100644 src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java rename {server/src => src}/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/booking/BookingDtoShort.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/booking/BookingMapper.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/booking/BookingRepository.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/booking/BookingService.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java (95%) rename {server/src => src}/main/java/ru/practicum/shareit/booking/BookingState.java (57%) rename {server/src => src}/main/java/ru/practicum/shareit/booking/BookingStatus.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/exception/DataConflictException.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/exception/ErrorHandler.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/exception/NotFoundException.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java (100%) rename {gateway/src => src}/main/java/ru/practicum/shareit/exception/ValidationException.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/item/Comment.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/item/CommentDto.java (51%) rename {server/src => src}/main/java/ru/practicum/shareit/item/CommentDtoOutput.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/item/CommentDtoShort.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/item/CommentMapper.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/item/CommentRepository.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/item/Item.java (98%) rename {server/src => src}/main/java/ru/practicum/shareit/item/ItemController.java (85%) rename gateway/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java => src/main/java/ru/practicum/shareit/item/ItemDto.java (72%) rename {server/src => src}/main/java/ru/practicum/shareit/item/ItemDtoOutput.java (88%) rename {server/src => src}/main/java/ru/practicum/shareit/item/ItemDtoShort.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/item/ItemMapper.java (89%) rename {server/src => src}/main/java/ru/practicum/shareit/item/ItemRepository.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/item/ItemService.java (73%) rename {server/src => src}/main/java/ru/practicum/shareit/item/ItemServiceImpl.java (86%) create mode 100644 src/main/java/ru/practicum/shareit/request/ItemRequestController.java rename server/src/main/java/ru/practicum/shareit/request/RequestDto.java => src/main/java/ru/practicum/shareit/request/ItemRequestDto.java (87%) rename {server/src => src}/main/java/ru/practicum/shareit/request/Request.java (88%) rename {server/src => src}/main/java/ru/practicum/shareit/user/User.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/user/UserController.java (73%) rename gateway/src/main/java/ru/practicum/shareit/user/UserDtoInput.java => src/main/java/ru/practicum/shareit/user/UserDto.java (59%) rename {server/src => src}/main/java/ru/practicum/shareit/user/UserDtoOutput.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/user/UserDtoShort.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/user/UserMapper.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/user/UserRepository.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/user/UserService.java (100%) rename {server/src => src}/main/java/ru/practicum/shareit/user/UserServiceImpl.java (100%) rename {gateway/src => src}/main/java/ru/practicum/shareit/validation/CreateObject.java (100%) rename {gateway/src => src}/main/java/ru/practicum/shareit/validation/UpdateObject.java (100%) create mode 100644 src/main/resources/application.yaml rename {server/src => src}/main/resources/schema.sql (100%) diff --git a/README.md b/README.md index bd0da80..d4b18b6 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ _java-shareit_ # Шеринг вещей -### Согласно задания спринта № 16 (add-item-requests-and-gateway) +### Согласно задания спринта № 14 (add-controllers) ## выполнено Филипповских Сергеем _**Когорта-53**_ -_**Для проверки выполнения 16 спринта использовался postman: -[postman_for_shareit_16.json](/postman_for_shareit_16.json)**_ +_**Для проверки выполнения 14 спринта использовался postman: +[postman_for_shareit_14.json](/postman_for_shareit_14.json)**_ ![](/er_diagram_shareit.png) diff --git a/checkstyle.xml b/checkstyle.xml index 05d5086..c28a0d3 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -161,7 +161,7 @@ - + diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index abe6570..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,39 +0,0 @@ -services: - gateway: - build: gateway - image: shareit-gateway - container_name: shareit-gateway - ports: - - "8080:8080" - depends_on: - - server - environment: - - SHAREIT_SERVER_URL=http://server:9090 - - server: - build: server - image: shareit-server - container_name: shareit-server - ports: - - "9090:9090" - depends_on: - - db - environment: - - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/shareit - - SPRING_DATASOURCE_USERNAME=shareit - - SPRING_DATASOURCE_PASSWORD=shareit - - db: - image: postgres:16.1 - container_name: postgres - ports: - - "6541:5432" - environment: - - POSTGRES_PASSWORD=shareit - - POSTGRES_USER=shareit - - POSTGRES_DB=shareit - healthcheck: - test: pg_isready -q -d $$POSTGRES_DB -U $$POSTGRES_USER - timeout: 5s - interval: 5s - retries: 10 \ No newline at end of file diff --git a/gateway/Dockerfile b/gateway/Dockerfile deleted file mode 100644 index 0ff1817..0000000 --- a/gateway/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM eclipse-temurin:21-jre-jammy -VOLUME /tmp -ARG JAR_FILE=target/*.jar -COPY ${JAR_FILE} app.jar -ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/gateway/pom.xml b/gateway/pom.xml deleted file mode 100644 index f3394c1..0000000 --- a/gateway/pom.xml +++ /dev/null @@ -1,70 +0,0 @@ - - - 4.0.0 - - ru.practicum - shareit - 0.0.1-SNAPSHOT - - - shareit-gateway - 0.0.1-SNAPSHOT - - ShareIt Gateway - - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-validation - - - - org.springframework.boot - spring-boot-starter-actuator - - - - org.hibernate.validator - hibernate-validator - - - - org.apache.httpcomponents.client5 - httpclient5 - - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - org.projectlombok - lombok - true - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - 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 deleted file mode 100644 index 0aa75c3..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java +++ /dev/null @@ -1,12 +0,0 @@ -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); - } - -} diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java deleted file mode 100644 index 7975693..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java +++ /dev/null @@ -1,58 +0,0 @@ -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.client.BaseClient; -import ru.practicum.shareit.exception.ValidationException; - -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 addBooking(Long userId, BookingDtoInput requestDto) { - if (requestDto.getStart().equals(requestDto.getEnd())) { - throw new ValidationException("Начало и окончание бронирования не может быть одним и тем же моментом."); - } - if (requestDto.getEnd().isBefore(requestDto.getStart())) { - throw new ValidationException("Окончание бронирования не может быть раньше его начала."); - } - return post("", userId, requestDto); - } - - public ResponseEntity updateByOwner(Long userId, Long bookingId, Boolean approved) { - Map parameters = Map.of( - "approved", approved - ); - return patch("/" + bookingId + "?approved={approved}", userId, parameters, null); - } - - public ResponseEntity getWithStatusById(Long bookingId, Long userId) { - return get("/" + bookingId, userId); - } - - public ResponseEntity getByUserId(Long userId, BookingState state) { - return get("?state=" + state, userId); - } - - public ResponseEntity getByOwnerId(Long userId, BookingState state) { - return get("/owner?state=" + state, userId); - } -} diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java deleted file mode 100644 index 6d368fb..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ /dev/null @@ -1,100 +0,0 @@ -package ru.practicum.shareit.booking; - -import jakarta.validation.Valid; -import jakarta.validation.ValidationException; -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.*; - -@Controller -@RequestMapping(path = "/bookings") -@RequiredArgsConstructor -@Slf4j -@Validated -public class BookingController { - - private final BookingClient bookingClient; - - /** - * Создает бронирование. - * - * @param bookerId идентификатор пользователя, создающего бронирование - * @param bookingDto данные бронирования - * @return ResponseEntity с объектом, представляющим результат создания бронирования - */ - @PostMapping - public ResponseEntity addBooking(@RequestHeader("X-Sharer-User-Id") Long bookerId, - @Valid @RequestBody BookingDtoInput bookingDto) { - log.info("Создано бронирование {}, пользователем {}", bookingDto, bookerId); - return bookingClient.addBooking(bookerId, bookingDto); - } - - /** - * Обновляет бронирование владельцем. - * - * @param ownerId идентификатор владельца бронирования - * @param bookingId идентификатор бронирования - * @param approved флаг, указывающий, одобрено ли бронирование - * @return ResponseEntity с объектом, представляющим результат обновления бронирования - */ - @PatchMapping("/{bookingId}") - public ResponseEntity updateByOwner(@RequestHeader("X-Sharer-User-Id") Long ownerId, - @PathVariable Long bookingId, - @RequestParam Boolean approved) { - log.info("Обновление бронирования {}, владельцем {}, в состояние {}", bookingId, ownerId, approved); - return bookingClient.updateByOwner(ownerId, bookingId, approved); - } - - /** - * Получает данные о бронировании по его идентификатору. - * - * @param userId идентификатор пользователя, запрашивающего данные о бронировании - * @param bookingId идентификатор бронирования - * @return ResponseEntity с объектом, представляющим результат получения данных о бронировании - */ - @GetMapping("/{bookingId}") - public ResponseEntity getWithStatusById(@RequestHeader("X-Sharer-User-Id") Long userId, - @PathVariable Long bookingId) { - log.info("Получение данных о бронировании {}, пользователем {}", bookingId, userId); - return bookingClient.getWithStatusById( bookingId, userId); - } - - /** - * Получает все бронирования пользователя по его идентификатору и состоянию. - * - * @param userId идентификатор пользователя, запрашивающего данные о бронировании - * @param stateParam состояние бронирования - * @return ResponseEntity с объектом, представляющим результат получения данных о бронировании - */ - @GetMapping - public ResponseEntity getByUserId(@RequestHeader("X-Sharer-User-Id") Long userId, - @RequestParam(value = "state", - defaultValue = "ALL", required = false) String stateParam) { - BookingState state = BookingState.from(stateParam) - .orElseThrow(() -> - new ValidationException("Неизвестное состояние бронирования: " + stateParam)); - log.info("Получение всех бронирований пользователя{}, в состоянии {}", userId, state); - return bookingClient.getByUserId(userId, state); - } - - /** - * Получает все забронированные вещи пользователя по его идентификатору и состоянию. - * - * @param userId идентификатор пользователя, запрашивающего данные о бронировании - * @param stateParam состояние бронирования - * @return ResponseEntity с объектом, представляющим результат получения данных о бронировании - */ - @GetMapping("/owner") - public ResponseEntity getByOwnerId(@RequestHeader("X-Sharer-User-Id") Long userId, - @RequestParam(value = "state", defaultValue = "ALL", - required = false) String stateParam) { - BookingState state = BookingState.from(stateParam) - .orElseThrow(() -> - new ValidationException("Неизвестное состояние бронирования: " + stateParam)); - log.info("Получение всех забронированных вещей пользователя{}, в состоянии {}", userId, state); - return bookingClient.getByOwnerId(userId, state); - } -} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java deleted file mode 100644 index 0441ea2..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.practicum.shareit.booking; - -import jakarta.validation.constraints.Future; -import jakarta.validation.constraints.FutureOrPresent; -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class BookingDtoInput { - - @NotNull(message = "При создании брони должна быть информация о вещи.") - private Long itemId; - - @FutureOrPresent(message = "Дата начала бронирования не должна быть в прошлом") - @NotNull(message = "Дата бронирования не должна быть пустой") - private LocalDateTime start; - - @Future(message = "Дата завершения бронирования должна быть в будущем") - @NotNull(message = "Дата бронирования не должна быть пустой") - private LocalDateTime end; - -} diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingState.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingState.java deleted file mode 100644 index e633731..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/booking/BookingState.java +++ /dev/null @@ -1,16 +0,0 @@ -package ru.practicum.shareit.booking; - -import java.util.Optional; - -public enum BookingState { - ALL, CURRENT, PAST, FUTURE, WAITING, REJECTED; - - public static Optional from(String stringState) { - for (ru.practicum.shareit.booking.BookingState state : values()) { - if (state.name().equalsIgnoreCase(stringState)) { - return Optional.of(state); - } - } - return Optional.empty(); - } -} diff --git a/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java deleted file mode 100644 index 1a2d33a..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java +++ /dev/null @@ -1,121 +0,0 @@ -package ru.practicum.shareit.client; - -import java.util.List; -import java.util.Map; - -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; - -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, 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(); - } -} diff --git a/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java deleted file mode 100644 index 6e40c90..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java +++ /dev/null @@ -1,65 +0,0 @@ -package ru.practicum.shareit.exception; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.validation.BindingResult; -import org.springframework.validation.ObjectError; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.MissingRequestHeaderException; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -@Slf4j -@RestControllerAdvice -public class ErrorHandler { - @ExceptionHandler - @ResponseStatus(HttpStatus.BAD_REQUEST) - public ErrorResponse handleValidationException(final ValidationException e) { - log.error("{} - {}", HttpStatus.BAD_REQUEST, e.getMessage()); - return new ErrorResponse(e.getMessage()); - } - - - @ExceptionHandler - @ResponseStatus(HttpStatus.BAD_REQUEST) - public ErrorResponse handleMethodArgumentNotValidException(final MethodArgumentNotValidException e) { - BindingResult bindingResult = e.getBindingResult(); - List allErrors = bindingResult.getAllErrors(); - String defaultMessage = allErrors.stream() - .map(error -> Objects.requireNonNull(error.getDefaultMessage())) - .collect(Collectors.joining(", ")); - log.error("{} - {}", HttpStatus.BAD_REQUEST, defaultMessage); - return new ErrorResponse(defaultMessage); - } - - - @ExceptionHandler - @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) - public ErrorResponse handleRunTimeException(final RuntimeException e) { - log.error("{} - {}", HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); - return new ErrorResponse(e.getMessage()); - } - - @ExceptionHandler(MissingRequestHeaderException.class) - @ResponseStatus(HttpStatus.BAD_REQUEST) - public ErrorResponse handleMissingRequestHeaderException(MissingRequestHeaderException e) { - log.error("{} - {}", HttpStatus.BAD_REQUEST, e.getMessage()); - return new ErrorResponse(e.getMessage()); - } - - @Getter - public static class ErrorResponse { - private final String error; - - public ErrorResponse(String error) { - this.error = error; - } - - } -} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/item/CommentDtoInput.java b/gateway/src/main/java/ru/practicum/shareit/item/CommentDtoInput.java deleted file mode 100644 index d15069d..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/item/CommentDtoInput.java +++ /dev/null @@ -1,18 +0,0 @@ -package ru.practicum.shareit.item; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class CommentDtoInput { - - @NotNull(message = "Комментарий должен быть указан.") - @NotBlank(message = "Комментарий не может быть пустым.") - private String text; - -} diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java deleted file mode 100644 index ddf344c..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java +++ /dev/null @@ -1,54 +0,0 @@ -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; - -@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 addItem(Long userId, ItemDtoInput itemDto) { - return post("", userId, itemDto); - } - - public ResponseEntity updateItem(Long userId, Long idItem, ItemDtoInput itemDto) { - return patch("/" + idItem, userId, itemDto); - } - - public ResponseEntity getItemById(Long idItem, Long userId) { - return get("/" + idItem, userId); - } - - public ResponseEntity getAllItems(Long userId) { - return get("", userId); - } - - public ResponseEntity removeItem(Long userId, Long idItem) { - return delete("/" + idItem, userId); - } - - public ResponseEntity searchItems(String text) { - return get("/search?text=" + text); - } - - public ResponseEntity saveComment(Long userId, Long itemId, CommentDtoInput commentDto) { - return post("/" + itemId + "/comment", userId, commentDto); - } - -} diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java deleted file mode 100644 index 6de8007..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java +++ /dev/null @@ -1,122 +0,0 @@ -package ru.practicum.shareit.item; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -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.validation.CreateObject; -import ru.practicum.shareit.validation.UpdateObject; - -@Controller -@RequestMapping(path = "/items") -@RequiredArgsConstructor -@Slf4j -@Validated -public class ItemController { - private final ItemClient itemClient; - - /** - * Добавляет новую вещь. - * - * @param itemDto Данные вещи. - * @param idUser Идентификатор пользователя, добавляющего вещь. - * @return ResponseEntity с объектом, представляющим результат добавления вещи. - */ - @PostMapping - @ResponseStatus(HttpStatus.CREATED) - public ResponseEntity addItem(@Validated(CreateObject.class) @RequestBody ItemDtoInput itemDto, - @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { - log.info("Получен запрос пользователем {} на добавление вещи {}", idUser, itemDto); - return itemClient.addItem(idUser, itemDto); - } - - /** - * Обновляет вещь. - * - * @param idItem Идентификатор вещи, которую нужно обновить. - * @param itemDto Данные вещи. - * @param idUser Идентификатор пользователя, обновляющего вещь. - * @return ResponseEntity с объектом, представляющим результат обновления вещи. - */ - @PatchMapping("/{idItem}") - public ResponseEntity updateItem(@PathVariable Long idItem, - @Validated(UpdateObject.class) @RequestBody ItemDtoInput itemDto, - @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { - log.info("Получен запрос пользователем {} на обновление вещи {}", idUser, itemDto); - return itemClient.updateItem(idUser, idItem, itemDto); - } - - /** - * Возвращает вещь по ее идентификатору. - * - * @param idItem Идентификатор вещи, которую нужно вернуть. - * @param idUser Идентификатор пользователя, запрашивающего вещь. - * @return ResponseEntity с объектом, представляющим вещь по ее идентификатору. - */ - @GetMapping("/{idItem}") - public ResponseEntity getItemById(@PathVariable Long idItem, - @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { - log.info("Получен запрос пользователем {} на получение вещи с id={}", idUser, idItem); - return itemClient.getItemById(idItem, idUser); - } - - /** - * Возвращает все вещи пользователя. - * - * @param idUser Идентификатор пользователя, вещи которого нужно вернуть. - * @return ResponseEntity с объектом, представляющим все вещи пользователя. - */ - @GetMapping - public ResponseEntity getAllItemsByUser(@RequestHeader(value = "X-Sharer-User-Id") Long idUser) { - log.info("Получен запрос пользователем {} на получение всех своих вещей", idUser); - return itemClient.getAllItems(idUser); - } - - /** - * Удаляет вещь по ее идентификатору. - * - * @param idItem Идентификатор вещи, которую нужно удалить. - * @param idUser Идентификатор пользователя, удаляющего вещь. - * @return ResponseEntity с объектом, представляющим результат удаления вещи. - */ - @DeleteMapping("/{idItem}") - public ResponseEntity removeItem(@PathVariable Long idItem, - @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { - log.info("Получен запрос пользователем {} на удаление вещи с id={}", idUser, idItem); - return itemClient.removeItem(idUser, idItem); - } - - /** - * Ищет вещи по тексту. - * - * @param text Текст, по которому нужно искать вещи. - * @param idUser Идентификатор пользователя, инициирующего поиск. - * @return ResponseEntity с объектом, представляющим результат поиска вещей по тексту. - */ - @GetMapping("/search") - public ResponseEntity searchItemsByText(@RequestParam String text, - @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { - log.info("Получен запрос пользователем {} на поиск вещей по тексту: {}", idUser, text); - return itemClient.searchItems(text); - } - - /** - * Добавляет комментарий к вещи. - * - * @param userId Идентификатор пользователя, добавляющего комментарий. - * @param itemId Идентификатор вещи, к которой добавляется комментарий. - * @param commentDto Данные комментария. - * @return ResponseEntity с объектом, представляющим результат добавления комментария. - */ - @PostMapping("/{itemId}/comment") - public ResponseEntity addCommentToItem(@RequestHeader("X-Sharer-User-Id") Long userId, - @PathVariable Long itemId, - @RequestBody CommentDtoInput commentDto) { - log.info("Получен запрос пользователем {} на добавление отзыва на вещь с id={} с текстом: {}", - userId, itemId, commentDto.getText()); - return itemClient.saveComment(userId, itemId, commentDto); - } -} diff --git a/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java b/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java deleted file mode 100644 index ba2c019..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java +++ /dev/null @@ -1,41 +0,0 @@ -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; - -@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 addRequest(Long userId, RequestDtoInput requestDto) { - return post("", userId, requestDto); - } - - public ResponseEntity getRequests(Long userId) { - return get("", userId); - } - - public ResponseEntity getAllRequests(Long userId) { - return get("/all", userId); - } - - public ResponseEntity getRequestById(Long requestId) { - return get("/" + requestId); - } -} diff --git a/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java b/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java deleted file mode 100644 index 6f1d1b4..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java +++ /dev/null @@ -1,73 +0,0 @@ -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.*; - -@Controller -@RequestMapping(path = "/requests") -@RequiredArgsConstructor -@Slf4j -@Validated -public class RequestController { - - private final RequestClient requestClient; - - /** - * Добавляет новый запрос. - * - * @param requestorId Идентификатор пользователя, добавляющего запрос. - * @param requestDto Данные запроса. - * @return ResponseEntity с объектом, представляющим результат добавления запроса. - */ - @PostMapping - public ResponseEntity addRequest(@RequestHeader("X-Sharer-User-Id") Long requestorId, - @Valid @RequestBody RequestDtoInput requestDto) { - log.info("Получен запрос на добавление запроса с параметрами: {}", requestDto); - return requestClient.addRequest(requestorId, requestDto); - } - - /** - * Возвращает расширенный список запросов конкретного пользователя. - * - * @param requestorId Идентификатор пользователя, запросы которого нужно вернуть. - * @return ResponseEntity с объектом, представляющим список запросов пользователя. - */ - @GetMapping - public ResponseEntity getRequests(@RequestHeader("X-Sharer-User-Id") Long requestorId) { - log.info("Получен запрос на получение расширенного списка запросов пользователя с id: {}", requestorId); - return requestClient.getRequests(requestorId); - } - - /** - * Возвращает все запросы (за исключением запросов самого пользователя). - * - * @param requestorId Идентификатор пользователя. - * @return ResponseEntity с объектом, представляющим все запросы. - */ - @GetMapping("/all") - public ResponseEntity getAllRequests(@RequestHeader("X-Sharer-User-Id") Long requestorId) { - log.info("Получен запрос на получение всех запросов, кроме запросов самого пользователя с id: {}", requestorId); - return requestClient.getAllRequests(requestorId); - } - - /** - * Возвращает запрос по его идентификатору. - * - * @param requestorId Идентификатор пользователя, который делает запрос. - * @param requestId Идентификатор запроса, который нужно вернуть. - * @return ResponseEntity с объектом, представляющим запрос по его идентификатору. - */ - @GetMapping("/{requestId}") - public ResponseEntity getRequestsById(@RequestHeader("X-Sharer-User-Id") Long requestorId, - @PathVariable Long requestId) { - log.info("Получен запрос на получение запроса с id: {} по идентификатору пользователя с id: {}", - requestId, requestorId); - return requestClient.getRequestById(requestId); - } - -} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java b/gateway/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java deleted file mode 100644 index 90542a2..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java +++ /dev/null @@ -1,18 +0,0 @@ -package ru.practicum.shareit.request; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class RequestDtoInput { - - @NotNull(message = "Запрос не может быть пустым.") - @NotBlank(message = "Запрос не может быть пустым.") - private String description; - -} diff --git a/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java b/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java deleted file mode 100644 index fe6a014..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java +++ /dev/null @@ -1,46 +0,0 @@ -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; - -@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 addUser( UserDtoInput userDto) { - return post("", userDto); - } - - public ResponseEntity updateUser(Long userId, UserDtoInput userDto) { - return patch("/" + userId, userDto); - } - - public ResponseEntity getUserById(Long userId) { - return get("/" + userId); - } - - public ResponseEntity getAllUsers() { - return get(API_PREFIX); - } - - public ResponseEntity removeUser(Long userId) { - return delete("/" + userId); - } - -} diff --git a/gateway/src/main/java/ru/practicum/shareit/user/UserController.java b/gateway/src/main/java/ru/practicum/shareit/user/UserController.java deleted file mode 100644 index 5c5f256..0000000 --- a/gateway/src/main/java/ru/practicum/shareit/user/UserController.java +++ /dev/null @@ -1,81 +0,0 @@ -package ru.practicum.shareit.user; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -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.validation.CreateObject; -import ru.practicum.shareit.validation.UpdateObject; - -@Controller -@RequestMapping("/users") -@RequiredArgsConstructor -@Slf4j -@Validated -public class UserController { - private final UserClient userClient; - - /** - * Добавляет нового пользователя. - * - * @param userDto данные пользователя - * @return ResponseEntity с объектом, представляющим результат добавления пользователя - */ - @PostMapping - @ResponseStatus(HttpStatus.CREATED) - public ResponseEntity addUser(@Validated(CreateObject.class) @RequestBody UserDtoInput userDto) { - log.info("Добавление нового пользователя {}", userDto); - return userClient.addUser(userDto); - } - - /** - * Обновляет данные пользователя. - * - * @param idUser идентификатор пользователя, данные которого нужно обновить - * @param userDto новые данные пользователя - * @return ResponseEntity с объектом, представляющим результат обновления данных пользователя - */ - @PatchMapping("/{idUser}") - public ResponseEntity updateUser(@PathVariable Long idUser, - @Validated(UpdateObject.class) @RequestBody UserDtoInput userDto) { - log.info("Обновление данных пользователя с id {} на основе данных {}", idUser, userDto); - return userClient.updateUser(idUser, userDto); - } - - /** - * Получает данные пользователя по его идентификатору. - * - * @param idUser идентификатор пользователя, данные которого нужно получить - * @return ResponseEntity с объектом, представляющим результат получения данных пользователя - */ - @GetMapping("/{idUser}") - public ResponseEntity getUserById(@PathVariable Long idUser) { - log.info("Получение данных пользователя с id {}", idUser); - return userClient.getUserById(idUser); - } - - /** - * Получает всех пользователей. - * - * @return ResponseEntity с объектом, представляющим результат получения всех пользователей - */ - @GetMapping - public ResponseEntity getAllUsers() { - log.info("Получение всех пользователей"); - return userClient.getAllUsers(); - } - - /** - * Удаляет пользователя по его идентификатору. - * - * @param idUser идентификатор пользователя, которого нужно удалить - */ - @DeleteMapping("/{idUser}") - public ResponseEntity removeUser(@PathVariable Long idUser) { - log.info("Удаление пользователя с id {}", idUser); - return userClient.removeUser(idUser); - } -} \ No newline at end of file diff --git a/gateway/src/main/resources/application.properties b/gateway/src/main/resources/application.properties deleted file mode 100644 index 2ee0851..0000000 --- a/gateway/src/main/resources/application.properties +++ /dev/null @@ -1,7 +0,0 @@ -logging.level.org.springframework.web.client.RestTemplate=DEBUG -#logging.level.org.apache.http=DEBUG -#logging.level.httpclient.wire=DEBUG - -server.port=8080 - -shareit-server.url=http://localhost:9090 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 79cba37..2185679 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,6 @@ ru.practicum shareit - pom 0.0.1-SNAPSHOT ShareIt @@ -20,29 +19,73 @@ 21 - - gateway - server - + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.postgresql + postgresql + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + + + src/main/resources + true + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + - - org.springframework.boot - spring-boot-maven-plugin - - - true - - - - org.projectlombok - lombok - - - - org.apache.maven.plugins maven-surefire-plugin @@ -189,5 +232,17 @@ + + coverage + + + + org.jacoco + jacoco-maven-plugin + + + + - \ No newline at end of file + + diff --git a/postman_for_shareit_14.json b/postman_for_shareit_14.json new file mode 100644 index 0000000..d04cf55 --- /dev/null +++ b/postman_for_shareit_14.json @@ -0,0 +1,2725 @@ +{ + "info": { + "_postman_id": "f4a82602-e802-4363-87b5-c1814ccd49db", + "name": "Sprint 14 ShareIt (add-controllers)", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "23073145", + "_collection_link": "https://universal-shadow-295426.postman.co/workspace/My-Workspace~4200f6aa-0504-44b1-8a1d-707d0dcbd5ce/collection/13708500-f4a82602-e802-4363-87b5-c1814ccd49db?action=share&source=collection_link&creator=23073145" + }, + "item": [ + { + "name": "users", + "item": [ + { + "name": "Create user", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = rnd.getUser();\r", + " pm.collectionVariables.set(\"userName\", user.name);\r", + " pm.collectionVariables.set(\"userEmail\", user.email);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200 or 201\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([200,201]);\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"{{userEmail}}\"\n}" + }, + "url": { + "raw": "localhost:8080/users", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "users" + ] + } + }, + "response": [] + }, + { + "name": "Create user without email", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user2 = rnd.getUser();\r", + " pm.collectionVariables.set(\"userName\", user2.name);\r", + " pm.collectionVariables.set(\"userEmail\", user2.email);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{userName}}\"\n}" + }, + "url": { + "raw": "localhost:8080/users", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "users" + ] + } + }, + "response": [] + }, + { + "name": "Create 2 users with same email", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user1 = rnd.getUser();\r", + " us = await api.addUser(user1);\r", + " user2 = rnd.getUser();\r", + " user2.email = user1.email;\r", + " pm.collectionVariables.set(\"userName\", user2.name);\r", + " pm.collectionVariables.set(\"userEmail\", user2.email);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 409\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([409, 500]);\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"{{userEmail}}\"\n}" + }, + "url": { + "raw": "localhost:8080/users", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "users" + ] + } + }, + "response": [] + }, + { + "name": "Create user with invalid email", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = rnd.getUser();\r", + " pm.collectionVariables.set(\"userName\", user.name);\r", + " pm.collectionVariables.set(\"userEmail\", user.email);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"user.com\"\n}" + }, + "url": { + "raw": "localhost:8080/users", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "users" + ] + } + }, + "response": [] + }, + { + "name": "User update", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = rnd.getUser();\r", + " us = await api.addUser(user);\r", + " pm.collectionVariables.set(\"userId\", us.id);\r", + " us = rnd.getUser()\r", + " pm.collectionVariables.set(\"userName\", us.name);\r", + " pm.collectionVariables.set(\"userEmail\", us.email);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {\r", + " pm.response.to.be.ok;\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "pm.test(\"Test user 'id' field\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('id');\r", + " var id = pm.collectionVariables.get(\"userId\");\r", + " pm.expect(jsonData.id, '\"id\" must be ' + id).to.eql(Number(id));\r", + "});\r", + "pm.test(\"Test user 'email' field\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('email');\r", + " var email = pm.collectionVariables.get(\"userEmail\");\r", + " pm.expect(jsonData.email, '\"email\" must be ' + email).to.eql(email);\r", + "});\r", + "pm.test(\"Test user 'name' field\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('name');\r", + " var name = pm.collectionVariables.get(\"userName\");\r", + " pm.expect(jsonData.name, '\"name\" must be ' + name).to.eql(name);\r", + "});\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"{{userEmail}}\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/users/{{userId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } + }, + "response": [] + }, + { + "name": "User update name", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = rnd.getUser();\r", + " us = await api.addUser(user);\r", + " pm.collectionVariables.set(\"userId\", us.id);\r", + " us = rnd.getUser()\r", + " pm.collectionVariables.set(\"userName\", us.name);\r", + " pm.collectionVariables.set(\"userEmail\", us.email);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {\r", + " pm.response.to.be.ok;\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "pm.test(\"Test user 'id' field\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('id');\r", + " var id = pm.collectionVariables.get(\"userId\");\r", + " pm.expect(jsonData.id, '\"id\" must be ' + id).to.eql(Number(id));\r", + "});\r", + "pm.test(\"Test user 'name' field\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('name');\r", + " var name = pm.collectionVariables.get(\"userName\");\r", + " pm.expect(jsonData.name, '\"name\" must be ' + name).to.eql(name);\r", + "});\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{userName}}\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/users/{{userId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } + }, + "response": [] + }, + { + "name": "User update email", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = rnd.getUser();\r", + " us = await api.addUser(user);\r", + " pm.collectionVariables.set(\"userId\", us.id);\r", + " us = rnd.getUser()\r", + " pm.collectionVariables.set(\"userName\", us.name);\r", + " pm.collectionVariables.set(\"userEmail\", us.email);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {\r", + " pm.response.to.be.ok;\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "pm.test(\"Test user 'id' field\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('id');\r", + " var id = pm.collectionVariables.get(\"userId\");\r", + " pm.expect(jsonData.id, '\"id\" must be ' + id).to.eql(Number(id));\r", + "});\r", + "pm.test(\"Test user 'email' field\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('email');\r", + " var email = pm.collectionVariables.get(\"userEmail\");\r", + " pm.expect(jsonData.email, '\"email\" must be ' + email).to.eql(email);\r", + "});" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"email\": \"{{userEmail}}\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/users/{{userId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } + }, + "response": [] + }, + { + "name": "User update with existing email", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = rnd.getUser();\r", + " us = await api.addUser(user);\r", + " user2 = rnd.getUser();\r", + " us2 = await api.addUser(user2)\r", + " pm.collectionVariables.set(\"userId\", us2.id);\r", + " usa = rnd.getUser()\r", + " pm.collectionVariables.set(\"userName\", usa.name);\r", + " pm.collectionVariables.set(\"userEmail\", user.email);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 409\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([409, 500]);\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"{{userEmail}}\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/users/{{userId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } + }, + "response": [] + }, + { + "name": "Get user", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = rnd.getUser();\r", + " us = await api.addUser(user);\r", + " pm.collectionVariables.set(\"userId\", us.id);\r", + " pm.collectionVariables.set(\"userName\", us.name);\r", + " pm.collectionVariables.set(\"userEmail\", us.email);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {\r", + " pm.response.to.be.ok;\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "pm.test(\"Test user 'id' field\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('id');\r", + " var id = pm.collectionVariables.get(\"userId\");\r", + " pm.expect(jsonData.id, '\"id\" must be ' + id).to.eql(Number(id));\r", + "});\r", + "pm.test(\"Test user 'email' field\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('email');\r", + " var email = pm.collectionVariables.get(\"userEmail\");\r", + " pm.expect(jsonData.email, '\"email\" must be ' + email).to.eql(email);\r", + "});\r", + "pm.test(\"Test user 'name' field\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('name');\r", + " var name = pm.collectionVariables.get(\"userName\");\r", + " pm.expect(jsonData.name, '\"name\" must be ' + name).to.eql(name);\r", + "});\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}/users/{{userId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } + }, + "response": [] + }, + { + "name": "User delete", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([200,204]);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = rnd.getUser();\r", + " us = await api.addUser(user);\r", + " pm.collectionVariables.set(\"userId\", us.id);\r", + " pm.collectionVariables.set(\"userName\", us.name);\r", + " pm.collectionVariables.set(\"userEmail\", us.email);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/users/{{userId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "users", + "{{userId}}" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "items", + "item": [ + { + "name": "Item create", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200 or 201\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([200,201]);\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "var item = pm.collectionVariables.get(\"item\");\r", + "\r", + "pm.test(\"Response data equal to request\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('id');\r", + " pm.expect(jsonData).to.have.property('name');\r", + " pm.expect(jsonData).to.have.property('description');\r", + " pm.expect(jsonData).to.have.property('available');\r", + " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);\r", + " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);\r", + " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());\r", + "});" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" + }, + "url": { + "raw": "localhost:8080/items", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "items" + ] + } + }, + "response": [] + }, + { + "name": "Item create without X-Sharer-User-Id", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400 or 500\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([500, 400]);\r", + "});\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "disabled": true + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" + }, + "url": { + "raw": "localhost:8080/items", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "items" + ] + } + }, + "response": [] + }, + { + "name": "Item create with non-existent user", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id + '1');\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 404\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([404]);\r", + "});\r", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" + }, + "url": { + "raw": "localhost:8080/items", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "items" + ] + } + }, + "response": [] + }, + { + "name": "Item create without available field", + "event": [ + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", + "});" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\"\n}" + }, + "url": { + "raw": "localhost:8080/items", + "host": [ + "localhost" + ], + "port": "8080", + "path": [ + "items" + ] + } + }, + "response": [] + }, + { + "name": "Item create with empty name field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([400, 500]);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"\",\n \"description\": \"Аккумуляторная отвертка\",\n \"available\": true\n}" + }, + "url": { + "raw": "{{baseUrl}}/items", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items" + ] + } + }, + "response": [] + }, + { + "name": "Item create with empty description field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([400, 500]);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Отвертка\",\n \"available\": true\n}" + }, + "url": { + "raw": "{{baseUrl}}/items", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items" + ] + } + }, + "response": [] + }, + { + "name": "Item update", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Response have body\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + "});", + "var item = pm.collectionVariables.get(\"item\");", + "", + "pm.test(\"Response data equal to request\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property('id');", + " pm.expect(jsonData).to.have.property('name');", + " pm.expect(jsonData).to.have.property('description');", + " pm.expect(jsonData).to.have.property('available');", + " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", + " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", + " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " item = rnd.getItem();\r", + " var it = await api.addItem(item, user.id)\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item); \r", + " pm.collectionVariables.set(\"itemId\", it.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" + }, + "url": { + "raw": "{{baseUrl}}/items/{{itemId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "{{itemId}}" + ] + } + }, + "response": [] + }, + { + "name": "Item update without X-Sharer-User-Id", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 500\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([500, 400]);", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " item = rnd.getItem();\r", + " var it = await api.addItem(item, user.id)\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item); \r", + " pm.collectionVariables.set(\"itemId\", it.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text", + "disabled": true + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" + }, + "url": { + "raw": "{{baseUrl}}/items/{{itemId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "{{itemId}}" + ] + } + }, + "response": [] + }, + { + "name": "Item update with other user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 404\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([404, 403]);", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id + 1);\r", + " item = rnd.getItem();\r", + " var it = await api.addItem(item, user.id)\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item); \r", + " pm.collectionVariables.set(\"itemId\", it.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" + }, + "url": { + "raw": "{{baseUrl}}/items/{{itemId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "{{itemId}}" + ] + } + }, + "response": [] + }, + { + "name": "Item update available field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Response have body\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + "});", + "var item = pm.collectionVariables.get(\"item\");", + "", + "pm.test(\"Response data equal to request\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property('id');", + " pm.expect(jsonData).to.have.property('available');", + " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " item = rnd.getItem();\r", + " var it = await api.addItem(item, user.id)\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item); \r", + " pm.collectionVariables.set(\"itemId\", it.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": {{itemAvailable}}\n}" + }, + "url": { + "raw": "{{baseUrl}}/items/{{itemId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "{{itemId}}" + ] + } + }, + "response": [] + }, + { + "name": "Item update description field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Response have body\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + "});", + "var item = pm.collectionVariables.get(\"item\");", + "", + "pm.test(\"Response data equal to request\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property('id');", + " pm.expect(jsonData).to.have.property('name');", + " pm.expect(jsonData).to.have.property('description');", + " pm.expect(jsonData).to.have.property('available');", + " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " item = rnd.getItem();\r", + " var it = await api.addItem(item, user.id)\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item); \r", + " pm.collectionVariables.set(\"itemId\", it.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"description\": \"{{itemDescription}}\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/items/{{itemId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "{{itemId}}" + ] + } + }, + "response": [] + }, + { + "name": "Item update name field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Response have body\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + "});", + "var item = pm.collectionVariables.get(\"item\");", + "", + "pm.test(\"Response data equal to request\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property('id');", + " pm.expect(jsonData).to.have.property('name');", + " pm.expect(jsonData).to.have.property('description');", + " pm.expect(jsonData).to.have.property('available');", + " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " item = rnd.getItem();\r", + " var it = await api.addItem(item, user.id)\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item); \r", + " pm.collectionVariables.set(\"itemId\", it.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/items/{{itemId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "{{itemId}}" + ] + } + }, + "response": [] + }, + { + "name": "Item get", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Response have body\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + "});", + "var item = pm.collectionVariables.get(\"item\");", + "", + "pm.test(\"Response data equal to request\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property('id');", + " pm.expect(jsonData).to.have.property('name');", + " pm.expect(jsonData).to.have.property('description');", + " pm.expect(jsonData).to.have.property('available');", + " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", + " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", + " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " item = await api.addItem(rnd.getItem(), user.id)\r", + " pm.collectionVariables.set(\"item\", item);\r", + " pm.collectionVariables.set(\"itemId\", item.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/items/{{itemId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "{{itemId}}" + ] + } + }, + "response": [] + }, + { + "name": "Get all items from user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Test list item response\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.length, 'List length must be 2').to.eql(2);", + "});", + "", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " await api.addItem(rnd.getItem(), user.id)\r", + " await api.addItem(rnd.getItem(), user.id)\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/items", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items" + ] + } + }, + "response": [] + }, + { + "name": "Item search", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Test list item response\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.length, 'List length must be 1').to.eql(1);", + " pm.expect(jsonData[0].name.toUpperCase(), 'Name should include ' + pm.collectionVariables.get(\"searchString\")).to.eql(pm.collectionVariables.get(\"searchString\"))", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " it = rnd.getItem()\r", + " it.available = true\r", + " item = await api.addItem(it, user.id)\r", + " pm.collectionVariables.set(\"item\", item);\r", + " pm.collectionVariables.set(\"itemId\", item.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"searchString\", item.name.toUpperCase());\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/items/search?text={{searchString}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "search" + ], + "query": [ + { + "key": "text", + "value": "{{searchString}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Item search unavailable", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Test list item response\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.length, 'List length must be 0').to.eql(0);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " it = rnd.getItem()\r", + " it.available = false\r", + " item = await api.addItem(it, user.id)\r", + " pm.collectionVariables.set(\"item\", item);\r", + " pm.collectionVariables.set(\"itemId\", item.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"searchString\", item.name.toUpperCase());\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/items/search?text={{searchString}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "search" + ], + "query": [ + { + "key": "text", + "value": "{{searchString}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Item search empty", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Test search item response\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.length, 'List length must be 0').to.eql(0);", + "});" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/items/search?text=", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "search" + ], + "query": [ + { + "key": "text", + "value": "" + } + ] + } + }, + "response": [] + } + ] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "API = class {\r", + " constructor(postman, verbose = false, baseUrl = \"http://localhost:8080\") {\r", + " this.baseUrl = baseUrl;\r", + " this.pm = postman;\r", + " this._verbose = verbose;\r", + " }\r", + "\r", + " async addUser(user, id=0, verbose=null) {\r", + " return this.post(\"/users\", user, id, \"Ошибка при добавлении нового пользователя: \", verbose);\r", + " }\r", + "\r", + " async addItem(item, id=0, verbose=null) {\r", + " return this.post(\"/items\", item, id, \"Ошибка при добавлении новой вещи: \", verbose);\r", + " }\r", + "\r", + " async addRequest(request, id=0, verbose=null) {\r", + " return this.post(\"/requests\", request, id, \"Ошибка при добавлении нового запроса: \", verbose);\r", + " }\r", + " \r", + " async post(path, body, id=0, errorText = \"Ошибка при выполнении post-запроса: \", verbose=null) {\r", + " return this.sendRequest(\"POST\", path, body, id, errorText, verbose);\r", + " }\r", + "\r", + " async patch(path, body = null, id=0, errorText = \"Ошибка при выполнении patch-запроса: \", verbose=null) {\r", + " return this.sendRequest(\"PATCH\", path, body, id, errorText, verbose);\r", + " }\r", + "\r", + " async get(path, body = null, id=0, errorText = \"Ошибка при выполнении get-запроса: \", verbose=null) {\r", + " return this.sendRequest(\"GET\", path, body, id, errorText, verbose);\r", + " }\r", + "\r", + " async put(path, body = null, id=0, errorText = \"Ошибка при выполнении put-запроса: \", verbose=null) {\r", + " return this.sendRequest(\"PUT\", path, body, id, errorText, verbose);\r", + " }\r", + "\r", + " async delete(path, body = null, id=0, errorText = \"Ошибка при выполнении delte-запроса: \", verbose=null) {\r", + " return this.sendRequest(\"DELETE\", path, body, id, errorText, verbose);\r", + " }\r", + "\r", + " async sendRequest(method, path, body=null, id=0, errorText = \"Ошибка при выполнении запроса: \", verbose=null) {\r", + " return new Promise((resolve, reject) => {\r", + " verbose = verbose == null ? this._verbose : verbose;\r", + " var req = {};\r", + " if (id == 0){\r", + " req = {\r", + " url: this.baseUrl + path,\r", + " method: method,\r", + " body: body == null ? \"\" : JSON.stringify(body),\r", + " header: { \"Content-Type\": \"application/json\"},\r", + " };\r", + " }else{\r", + " req = {\r", + " url: this.baseUrl + path,\r", + " method: method,\r", + " body: body == null ? \"\" : JSON.stringify(body),\r", + " header: [{\r", + " \"key\": \"X-Sharer-User-Id\",\r", + " \"value\": id,\r", + " \"type\": \"text\",\r", + " },\r", + " {\r", + " \"key\": \"Content-Type\",\r", + " \"name\": \"Content-Type\",\r", + " \"value\": \"application/json\",\r", + " \"type\": \"text\"\r", + " }]\r", + " };\r", + " }\r", + " if(verbose) {\r", + " console.log(\"Отправляю запрос: \", req);\r", + " }\r", + "\r", + " try {\r", + " this.pm.sendRequest(req, (error, response) => {\r", + " if(error || (response.code >= 400 && response.code <= 599)) {\r", + " let err = error ? error : JSON.stringify(response.json());\r", + " console.error(\"При выполнении запроса к серверу возникла ошибка.\\n\", err,\r", + " \"\\nДля отладки проблемы повторите такой же запрос к вашей программе \" + \r", + " \"на локальном компьютере. Данные запроса:\\n\", JSON.stringify(request));\r", + "\r", + " reject(new Error(errorText + err));\r", + " }\r", + " if(verbose) {\r", + " console.log(\"Результат обработки запроса: код состояния - \", response.code, \", тело: \", response.json());\r", + " }\r", + " if (response.stream.length === 0){\r", + " resolve(null);\r", + " }else{\r", + " resolve(response.json());\r", + " }\r", + " });\r", + " \r", + " } catch(err) {\r", + " if(verbose) {\r", + " console.error(errorText, err);\r", + " }\r", + " return Promise.reject(err);\r", + " }\r", + " });\r", + " }\r", + "};\r", + "\r", + "RandomUtils = class {\r", + " constructor() {}\r", + "\r", + " getUser() {\r", + " return {\r", + " name: pm.variables.replaceIn('{{$randomFullName}}'),\r", + " email: pm.variables.replaceIn('{{$randomEmail}}'),\r", + " };\r", + " }\r", + "\r", + " getRequest() {\r", + " return {\r", + " description: this.getWord(50)\r", + " };\r", + " }\r", + "\r", + " getItem() {\r", + " return {\r", + " name: this.getWord(10),\r", + " description: this.getWord(50),\r", + " available: pm.variables.replaceIn('{{$randomBoolean}}')\t\r", + " };\r", + " }\r", + "\r", + " getItemForRequest(id) {\r", + " return {\r", + " name: this.getWord(10),\r", + " description: this.getWord(50),\r", + " available: pm.variables.replaceIn('{{$randomBoolean}}'),\r", + " requestId: id\r", + " };\r", + " }\r", + "\r", + " getFilm(director=null) {\r", + " let date = new Date(new Date(1960, 0, 1).getTime() + Math.random() * (new Date(2010, 0, 1).getTime() - new Date(1960, 0, 1).getTime()));\r", + " var toReturn = {\r", + " name: this.getWord(15),\r", + " description: this.getWord(50),\r", + " releaseDate: date.toISOString().slice(0,10),\r", + " duration: Math.floor(Math.random() * (180 - 60 + 1) + 60),\r", + " mpa: { id: Math.floor(Math.random() * (5 - 1 + 1) + 1)},\r", + " genres: [{ id: Math.floor(Math.random() * (6 - 1 + 1) + 1)}]\r", + " };\r", + " if (director!==null)\r", + " toReturn.directors = [{ id: director.id}];\r", + " return toReturn;\r", + " }\r", + "\r", + "\r", + " getWord(length = 1) {\r", + " let result = '';\r", + " const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\r", + " const charactersLength = characters.length;\r", + " let counter = 0;\r", + " while (counter < length) {\r", + " result += characters.charAt(Math.floor(Math.random() * charactersLength));\r", + " counter += 1;\r", + " }\r", + " return result;\r", + " }\r", + "\r", + " getName(length = 1) {\r", + " let result = '';\r", + " const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\r", + " const charactersLength = characters.length;\r", + " let counter = 0;\r", + " while (counter < length) {\r", + " result += characters.charAt(Math.floor(Math.random() * charactersLength));\r", + " counter += 1;\r", + " }\r", + " return result;\r", + " }\r", + "\r", + "}" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "packages": {}, + "exec": [ + "" + ] + } + } + ], + "variable": [ + { + "key": "baseUrl", + "value": "http://localhost:8080" + }, + { + "key": "userName", + "value": "" + }, + { + "key": "userEmail", + "value": "" + }, + { + "key": "userId", + "value": "1", + "type": "string" + }, + { + "key": "item", + "value": "" + }, + { + "key": "itemName", + "value": "" + }, + { + "key": "itemAvailable", + "value": "" + }, + { + "key": "itemDescription", + "value": "" + }, + { + "key": "itemId", + "value": "" + }, + { + "key": "searchString", + "value": "" + } + ] +} diff --git a/postman_for_shareit_16.json b/postman_for_shareit_15.json similarity index 81% rename from postman_for_shareit_16.json rename to postman_for_shareit_15.json index 6e87b6b..26845fe 100644 --- a/postman_for_shareit_16.json +++ b/postman_for_shareit_15.json @@ -1,10 +1,10 @@ { "info": { - "_postman_id": "7fc26681-0775-4307-885f-fd53560f0317", - "name": "Sprint 16 ShareIt (add-item-requests-and-gateway)", + "_postman_id": "069d256e-037e-4bd9-a5de-be6910726adc", + "name": "Sprint 15 ShareIt (add-bookings)", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "23073145", - "_collection_link": "https://universal-shadow-295426.postman.co/workspace/My-Workspace~4200f6aa-0504-44b1-8a1d-707d0dcbd5ce/collection/13708500-7fc26681-0775-4307-885f-fd53560f0317?action=share&source=collection_link&creator=23073145" + "_collection_link": "https://universal-shadow-295426.postman.co/workspace/My-Workspace~4200f6aa-0504-44b1-8a1d-707d0dcbd5ce/collection/13708500-069d256e-037e-4bd9-a5de-be6910726adc?action=share&source=collection_link&creator=23073145" }, "item": [ { @@ -921,10 +921,10 @@ ] }, { - "name": "Item", + "name": "items", "item": [ { - "name": "Create Item", + "name": "Item create", "event": [ { "listen": "prerequest", @@ -1027,7 +1027,7 @@ "response": [] }, { - "name": "Create Item on request", + "name": "Item create without X-Sharer-User-Id", "event": [ { "listen": "prerequest", @@ -1038,18 +1038,13 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", - " user1 = await api.addUser(rnd.getUser());\r", - " var request = await api.addRequest(rnd.getRequest(), user1.id);\r", - " pm.collectionVariables.set(\"requestId\", request.id);\r", - " user2 = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user2.id);\r", - "\r", " item = rnd.getItem();\r", " pm.collectionVariables.set(\"item\", item);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", @@ -1080,25 +1075,10 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Status code is 200 or 201\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([200,201]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", + "pm.test(\"Status code is 400 or 500\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([500, 400]);\r", "});\r", - "var item = pm.collectionVariables.get(\"item\");\r", - "\r", - "pm.test(\"Response data equal to request\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('id');\r", - " pm.expect(jsonData).to.have.property('name');\r", - " pm.expect(jsonData).to.have.property('description');\r", - " pm.expect(jsonData).to.have.property('available');\r", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);\r", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);\r", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());\r", - "});" + "" ], "type": "text/javascript", "packages": {} @@ -1114,12 +1094,13 @@ }, { "key": "X-Sharer-User-Id", - "value": "{{userId}}" + "value": "{{userId}}", + "disabled": true } ], "body": { "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}},\n \"requestId\": {{requestId}}\n}" + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" }, "url": { "raw": "localhost:8080/items", @@ -1135,7 +1116,7 @@ "response": [] }, { - "name": "Create Item without name on request", + "name": "Item create with non-existent user", "event": [ { "listen": "prerequest", @@ -1146,18 +1127,13 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", - " user1 = await api.addUser(rnd.getUser());\r", - " var request = await api.addRequest(rnd.getRequest(), user1.id);\r", - " pm.collectionVariables.set(\"requestId\", request.id);\r", - " user2 = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user2.id);\r", - "\r", " item = rnd.getItem();\r", " pm.collectionVariables.set(\"item\", item);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id + '1');\r", "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", @@ -1188,17 +1164,10 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Status code is 400\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", + "pm.test(\"Status code is 404\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([404]);\r", "});\r", - "pm.test(\"Response data have error\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('error');\r", - "});" + "" ], "type": "text/javascript", "packages": {} @@ -1219,7 +1188,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}},\n \"requestId\": {{requestId}}\n}" + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" }, "url": { "raw": "localhost:8080/items", @@ -1235,7 +1204,7 @@ "response": [] }, { - "name": "Create Item without description on request", + "name": "Item create without available field", "event": [ { "listen": "prerequest", @@ -1246,18 +1215,13 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", - " user1 = await api.addUser(rnd.getUser());\r", - " var request = await api.addRequest(rnd.getRequest(), user1.id);\r", - " pm.collectionVariables.set(\"requestId\", request.id);\r", - " user2 = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user2.id);\r", - "\r", " item = rnd.getItem();\r", " pm.collectionVariables.set(\"item\", item);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", @@ -1290,14 +1254,6 @@ "exec": [ "pm.test(\"Status code is 400\", function () {\r", " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "pm.test(\"Response data have error\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('error');\r", "});" ], "type": "text/javascript", @@ -1319,7 +1275,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"available\": {{itemAvailable}},\n \"requestId\": {{requestId}}\n}" + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\"\n}" }, "url": { "raw": "localhost:8080/items", @@ -1335,8 +1291,140 @@ "response": [] }, { - "name": "Create Item without available on request", + "name": "Item create with empty name field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([400, 500]);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"\",\n \"description\": \"Аккумуляторная отвертка\",\n \"available\": true\n}" + }, + "url": { + "raw": "{{baseUrl}}/items", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items" + ] + } + }, + "response": [] + }, + { + "name": "Item create with empty description field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 400\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([400, 500]);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Отвертка\",\n \"available\": true\n}" + }, + "url": { + "raw": "{{baseUrl}}/items", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items" + ] + } + }, + "response": [] + }, + { + "name": "Item update", "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Response have body\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + "});", + "var item = pm.collectionVariables.get(\"item\");", + "", + "pm.test(\"Response data equal to request\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property('id');", + " pm.expect(jsonData).to.have.property('name');", + " pm.expect(jsonData).to.have.property('description');", + " pm.expect(jsonData).to.have.property('available');", + " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", + " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", + " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, { "listen": "prerequest", "script": { @@ -1346,19 +1434,17 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", - " user1 = await api.addUser(rnd.getUser());\r", - " var request = await api.addRequest(rnd.getRequest(), user1.id);\r", - " pm.collectionVariables.set(\"requestId\", request.id);\r", - " user2 = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user2.id);\r", - "\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item);\r", + " var it = await api.addItem(item, user.id)\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item); \r", + " pm.collectionVariables.set(\"itemId\", it.id);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", " pm.collectionVariables.set(\"itemDescription\", item.description);\r", "\r", - "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -1383,65 +1469,694 @@ "type": "text/javascript", "packages": {} } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" }, + "url": { + "raw": "{{baseUrl}}/items/{{itemId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "{{itemId}}" + ] + } + }, + "response": [] + }, + { + "name": "Item update without X-Sharer-User-Id", + "event": [ { "listen": "test", "script": { "exec": [ - "pm.test(\"Status code is 400\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "pm.test(\"Response data have error\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('error');\r", + "pm.test(\"Status code is 500\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([500, 400]);", "});" ], "type": "text/javascript", "packages": {} } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}" + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " item = rnd.getItem();\r", + " var it = await api.addItem(item, user.id)\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item); \r", + " pm.collectionVariables.set(\"itemId\", it.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text", + "disabled": true + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" + }, + "url": { + "raw": "{{baseUrl}}/items/{{itemId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "{{itemId}}" + ] + } + }, + "response": [] + }, + { + "name": "Item update with other user", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 404\", function () {", + " pm.expect(pm.response.code).to.be.oneOf([404, 403]);", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id + 1);\r", + " item = rnd.getItem();\r", + " var it = await api.addItem(item, user.id)\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item); \r", + " pm.collectionVariables.set(\"itemId\", it.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" + }, + "url": { + "raw": "{{baseUrl}}/items/{{itemId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "{{itemId}}" + ] + } + }, + "response": [] + }, + { + "name": "Item update available field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Response have body\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + "});", + "var item = pm.collectionVariables.get(\"item\");", + "", + "pm.test(\"Response data equal to request\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property('id');", + " pm.expect(jsonData).to.have.property('available');", + " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " item = rnd.getItem();\r", + " var it = await api.addItem(item, user.id)\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item); \r", + " pm.collectionVariables.set(\"itemId\", it.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"available\": {{itemAvailable}}\n}" + }, + "url": { + "raw": "{{baseUrl}}/items/{{itemId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "{{itemId}}" + ] + } + }, + "response": [] + }, + { + "name": "Item update description field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Response have body\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + "});", + "var item = pm.collectionVariables.get(\"item\");", + "", + "pm.test(\"Response data equal to request\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property('id');", + " pm.expect(jsonData).to.have.property('name');", + " pm.expect(jsonData).to.have.property('description');", + " pm.expect(jsonData).to.have.property('available');", + " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " item = rnd.getItem();\r", + " var it = await api.addItem(item, user.id)\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item); \r", + " pm.collectionVariables.set(\"itemId\", it.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"description\": \"{{itemDescription}}\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/items/{{itemId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "{{itemId}}" + ] + } + }, + "response": [] + }, + { + "name": "Item update name field", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Response have body\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + "});", + "var item = pm.collectionVariables.get(\"item\");", + "", + "pm.test(\"Response data equal to request\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property('id');", + " pm.expect(jsonData).to.have.property('name');", + " pm.expect(jsonData).to.have.property('description');", + " pm.expect(jsonData).to.have.property('available');", + " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " item = rnd.getItem();\r", + " var it = await api.addItem(item, user.id)\r", + " item = rnd.getItem();\r", + " pm.collectionVariables.set(\"item\", item); \r", + " pm.collectionVariables.set(\"itemId\", it.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + "\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "PATCH", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\"\n}" + }, + "url": { + "raw": "{{baseUrl}}/items/{{itemId}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "{{itemId}}" + ] + } + }, + "response": [] + }, + { + "name": "Item get", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Response have body\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + "});", + "var item = pm.collectionVariables.get(\"item\");", + "", + "pm.test(\"Response data equal to request\", function () {", + " var jsonData = pm.response.json();", + " pm.expect(jsonData).to.have.property('id');", + " pm.expect(jsonData).to.have.property('name');", + " pm.expect(jsonData).to.have.property('description');", + " pm.expect(jsonData).to.have.property('available');", + " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", + " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", + " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", + "});" + ], + "type": "text/javascript", + "packages": {} + } + }, + { + "listen": "prerequest", + "script": { + "exec": [ + "const main = async () => {\r", + " const api = new API(pm);\r", + " const rnd = new RandomUtils();\r", + "\r", + " try {\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " item = await api.addItem(rnd.getItem(), user.id)\r", + " pm.collectionVariables.set(\"item\", item);\r", + " pm.collectionVariables.set(\"itemId\", item.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + " } catch(err) {\r", + " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", + " }\r", + "\r", + "};\r", + "\r", + "const interval = setInterval(() => {}, 1000);\r", + "\r", + "setTimeout(async () => \r", + " {\r", + " try {\r", + " await main();\r", + " } catch (e) {\r", + " console.error(e);\r", + " } finally {\r", + " clearInterval(interval);\r", + " }\r", + " }, \r", + " 100 \r", + ");" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" } ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"requestId\": {{requestId}}\n}" - }, "url": { - "raw": "localhost:8080/items", + "raw": "{{baseUrl}}/items/{{itemId}}", "host": [ - "localhost" + "{{baseUrl}}" ], - "port": "8080", "path": [ - "items" + "items", + "{{itemId}}" ] } }, "response": [] - } - ] - }, - { - "name": "requests", - "item": [ + }, { - "name": "Create request", + "name": "Get all items from user", "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Test list item response\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.length, 'List length must be 2').to.eql(2);", + "});", + "", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, { "listen": "prerequest", "script": { @@ -1451,11 +2166,10 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", - " request1 = rnd.getRequest();\r", - " pm.collectionVariables.set(\"requestDescription\", request1.description);\r", " user = await api.addUser(rnd.getUser());\r", " pm.collectionVariables.set(\"userId\", user.id);\r", - "\r", + " await api.addItem(rnd.getItem(), user.id)\r", + " await api.addItem(rnd.getItem(), user.id)\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -1480,65 +2194,57 @@ "type": "text/javascript", "packages": {} } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200 or 201\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([200,201]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "\r", - "pm.test(\"Response data equal to request\", function () {\r", - " var description = pm.collectionVariables.get(\"requestDescription\");\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('id');\r", - " pm.expect(jsonData).to.have.property('description');\r", - " pm.expect(jsonData).to.have.property('created');\r", - " pm.expect(jsonData.description, `\"description\" must be ${description}`).to.eql(description);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } } ], "request": { - "method": "POST", + "method": "GET", "header": [ { - "key": "Content-Type", - "value": "application/json" + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" }, { - "key": "X-Sharer-User-Id", - "value": "{{userId}}" + "key": "Accept", + "value": "*/*", + "type": "text" } ], - "body": { - "mode": "raw", - "raw": "{ \n \"description\": \"{{requestDescription}}\"\n}" - }, "url": { - "raw": "localhost:8080/requests", + "raw": "{{baseUrl}}/items", "host": [ - "localhost" + "{{baseUrl}}" ], - "port": "8080", "path": [ - "requests" + "items" ] } }, "response": [] }, { - "name": "Get user requests", + "name": "Item search", "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Test list item response\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.length, 'List length must be 1').to.eql(1);", + " pm.expect(jsonData[0].name.toUpperCase(), 'Name should include ' + pm.collectionVariables.get(\"searchString\")).to.eql(pm.collectionVariables.get(\"searchString\"))", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, { "listen": "prerequest", "script": { @@ -1550,9 +2256,15 @@ " try {\r", " user = await api.addUser(rnd.getUser());\r", " pm.collectionVariables.set(\"userId\", user.id);\r", - " request1 = await api.addRequest(rnd.getRequest(), user.id);\r", - " request2 = await api.addRequest(rnd.getRequest(), user.id);\r", - "\r", + " it = rnd.getItem()\r", + " it.available = true\r", + " item = await api.addItem(it, user.id)\r", + " pm.collectionVariables.set(\"item\", item);\r", + " pm.collectionVariables.set(\"itemId\", item.id);\r", + " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"searchString\", item.name.toUpperCase());\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -1577,54 +2289,63 @@ "type": "text/javascript", "packages": {} } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {\r", - " pm.response.to.be.ok;\r", - "});\r", - "pm.test(\"User requests amount\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData.length, 'List length must be 2').to.eql(2);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } } ], "request": { "method": "GET", "header": [ { - "key": "Content-Type", - "value": "application/json" + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" }, { - "key": "X-Sharer-User-Id", - "value": "{{userId}}" + "key": "Accept", + "value": "*/*", + "type": "text" } ], "url": { - "raw": "localhost:8080/requests", + "raw": "{{baseUrl}}/items/search?text={{searchString}}", "host": [ - "localhost" + "{{baseUrl}}" ], - "port": "8080", "path": [ - "requests" + "items", + "search" + ], + "query": [ + { + "key": "text", + "value": "{{searchString}}" + } ] } }, "response": [] }, { - "name": "Get user request by id", + "name": "Item search unavailable", "event": [ + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Test list item response\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.length, 'List length must be 0').to.eql(0);", + "});", + "" + ], + "type": "text/javascript", + "packages": {} + } + }, { "listen": "prerequest", "script": { @@ -1634,13 +2355,17 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", - " user1 = await api.addUser(rnd.getUser());\r", - " user2 = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user2.id);\r", - " req = await api.addRequest(rnd.getRequest(), user1.id);\r", - " pm.collectionVariables.set(\"requestId\", req.id);\r", - " item = await api.addItem(rnd.getItemForRequest(req.id), user2.id);\r", + " user = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user.id);\r", + " it = rnd.getItem()\r", + " it.available = false\r", + " item = await api.addItem(it, user.id)\r", + " pm.collectionVariables.set(\"item\", item);\r", + " pm.collectionVariables.set(\"itemId\", item.id);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", + " pm.collectionVariables.set(\"searchString\", item.name.toUpperCase());\r", + " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", + " pm.collectionVariables.set(\"itemDescription\", item.description);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -1665,28 +2390,59 @@ "type": "text/javascript", "packages": {} } - }, + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" + }, + { + "key": "Accept", + "value": "*/*", + "type": "text" + } + ], + "url": { + "raw": "{{baseUrl}}/items/search?text={{searchString}}", + "host": [ + "{{baseUrl}}" + ], + "path": [ + "items", + "search" + ], + "query": [ + { + "key": "text", + "value": "{{searchString}}" + } + ] + } + }, + "response": [] + }, + { + "name": "Item search empty", + "event": [ { "listen": "test", "script": { "exec": [ - "pm.test(\"Status code is 200\", function () {\r", - " pm.response.to.be.ok;\r", - "});\r", - "pm.test(\"User requests amount\", function () {\r", - " var name = pm.collectionVariables.get(\"itemName\");\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('id');\r", - " pm.expect(jsonData).to.have.property('description');\r", - " pm.expect(jsonData).to.have.property('created');\r", - " pm.expect(jsonData).to.have.property('items');\r", - " pm.expect(jsonData.items[0].name, `\"item name\" must be ${name}`).to.eql(name);\r", + "pm.test(\"Status code is 200\", function () {", + " pm.response.to.be.ok;", + "});", + "pm.test(\"Test search item response\", function () {", + " pm.response.to.be.withBody;", + " pm.response.to.be.json;", + " var jsonData = pm.response.json();", + " pm.expect(jsonData.length, 'List length must be 0').to.eql(0);", "});" ], - "type": "text/javascript", - "packages": {} + "type": "text/javascript" } } ], @@ -1694,23 +2450,30 @@ "method": "GET", "header": [ { - "key": "Content-Type", - "value": "application/json" + "key": "X-Sharer-User-Id", + "value": "{{userId}}", + "type": "text" }, { - "key": "X-Sharer-User-Id", - "value": "{{userId}}" + "key": "Accept", + "value": "*/*", + "type": "text" } ], "url": { - "raw": "localhost:8080/requests/{{requestId}}", + "raw": "{{baseUrl}}/items/search?text=", "host": [ - "localhost" + "{{baseUrl}}" ], - "port": "8080", "path": [ - "requests", - "{{requestId}}" + "items", + "search" + ], + "query": [ + { + "key": "text", + "value": "" + } ] } }, @@ -3976,6 +4739,10 @@ } ], "variable": [ + { + "key": "baseUrl", + "value": "http://localhost:8080" + }, { "key": "userName", "value": "" @@ -3986,35 +4753,31 @@ }, { "key": "userId", - "value": "1" - }, - { - "key": "itemName", "value": "" }, { - "key": "itemAvailable", + "key": "item", "value": "" }, { - "key": "itemDescription", + "key": "itemName", "value": "" }, { - "key": "item", + "key": "itemAvailable", "value": "" }, { - "key": "requestId", + "key": "itemDescription", "value": "" }, { - "key": "requestDescription", + "key": "itemId", "value": "" }, { - "key": "baseUrl", - "value": "localhost:8080" + "key": "searchString", + "value": "" }, { "key": "start", @@ -4024,10 +4787,6 @@ "key": "end", "value": "" }, - { - "key": "itemId", - "value": "" - }, { "key": "user1", "value": "" @@ -4038,7 +4797,7 @@ }, { "key": "bookingId", - "value": "1" + "value": "" }, { "key": "booking", diff --git a/server/Dockerfile b/server/Dockerfile deleted file mode 100644 index 0ff1817..0000000 --- a/server/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM eclipse-temurin:21-jre-jammy -VOLUME /tmp -ARG JAR_FILE=target/*.jar -COPY ${JAR_FILE} app.jar -ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/server/pom.xml b/server/pom.xml deleted file mode 100644 index 566db3e..0000000 --- a/server/pom.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - 4.0.0 - - ru.practicum - shareit - 0.0.1-SNAPSHOT - - - shareit-server - 0.0.1-SNAPSHOT - - ShareIt Server - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - - org.springframework.boot - spring-boot-starter-web - - - - org.springframework.boot - spring-boot-starter-actuator - - - - org.postgresql - postgresql - runtime - - - - com.h2database - h2 - runtime - - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - org.projectlombok - lombok - true - - - - org.springframework.boot - spring-boot-starter-test - test - - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - - - - coverage - - - - org.jacoco - jacoco-maven-plugin - - - - - - - diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java b/server/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java deleted file mode 100644 index 382f6a2..0000000 --- a/server/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.practicum.shareit.booking; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class BookingDtoInput { - - private Long itemId; - - private LocalDateTime start; - - private LocalDateTime end; - -} diff --git a/server/src/main/java/ru/practicum/shareit/exception/ValidationException.java b/server/src/main/java/ru/practicum/shareit/exception/ValidationException.java deleted file mode 100644 index 59043da..0000000 --- a/server/src/main/java/ru/practicum/shareit/exception/ValidationException.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.shareit.exception; - -public class ValidationException extends RuntimeException { - public ValidationException(String message) { - super(message); - } -} diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDto.java b/server/src/main/java/ru/practicum/shareit/item/ItemDto.java deleted file mode 100644 index 4f5f206..0000000 --- a/server/src/main/java/ru/practicum/shareit/item/ItemDto.java +++ /dev/null @@ -1,37 +0,0 @@ -package ru.practicum.shareit.item; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.shareit.booking.Booking; -import ru.practicum.shareit.request.RequestDto; -import ru.practicum.shareit.user.User; - -import java.util.List; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class ItemDto { - - private Long id; - - private String name; - - private String description; - - private Boolean available; - - private User owner; - - private RequestDto request; - - private List comments; - - private List bookings; - - private Booking lastBooking; - - private Booking nextBooking; - -} diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java b/server/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java deleted file mode 100644 index 97209f5..0000000 --- a/server/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.practicum.shareit.item; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class ItemDtoInput { - - private String name; - - private String description; - - private Boolean available; - - private Long requestId; - -} diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java b/server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java deleted file mode 100644 index 95ffbee..0000000 --- a/server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.practicum.shareit.item; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class ItemDtoRequest { - - private Long idItem; - - private String name; - - private Long idOwner; - - private String Owner; - -} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestController.java b/server/src/main/java/ru/practicum/shareit/request/RequestController.java deleted file mode 100644 index d3c49ec..0000000 --- a/server/src/main/java/ru/practicum/shareit/request/RequestController.java +++ /dev/null @@ -1,39 +0,0 @@ -package ru.practicum.shareit.request; - -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -/** - * TODO Sprint add-item-requests. - */ -@RestController -@RequestMapping(path = "/requests") -@RequiredArgsConstructor -public class RequestController { - - private final RequestService requestService; - - @PostMapping - public RequestDto addRequest(@RequestHeader("X-Sharer-User-Id") Long requestorId, - @RequestBody RequestDtoInput requestDto) { - return RequestMapper.toRequestDto(requestService.addRequest(requestorId, requestDto)); - } - - @GetMapping - public List getRequests(@RequestHeader("X-Sharer-User-Id") Long requestorId) { - return requestService.getRequests(requestorId).stream().map(RequestMapper::toRequestDtoItems).toList(); - } - - @GetMapping("/all") - public List getAllRequests(@RequestHeader("X-Sharer-User-Id") Long requestorId) { - return requestService.getAllRequests(requestorId).stream().map(RequestMapper::toRequestDto).toList(); - } - - @GetMapping("/{requestId}") - public RequestDtoItems getRequestsById(@PathVariable Long requestId) { - return RequestMapper.toRequestDtoItems(requestService.getRequestById(requestId)); - } - -} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java b/server/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java deleted file mode 100644 index 8d44bc6..0000000 --- a/server/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java +++ /dev/null @@ -1,14 +0,0 @@ -package ru.practicum.shareit.request; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class RequestDtoInput { - - private String description; - -} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestDtoItems.java b/server/src/main/java/ru/practicum/shareit/request/RequestDtoItems.java deleted file mode 100644 index 178918b..0000000 --- a/server/src/main/java/ru/practicum/shareit/request/RequestDtoItems.java +++ /dev/null @@ -1,26 +0,0 @@ -package ru.practicum.shareit.request; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.shareit.item.ItemDtoRequest; -import ru.practicum.shareit.user.User; - -import java.time.LocalDateTime; -import java.util.List; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class RequestDtoItems { - - private Long id; - - private String description; - - private User requestor; - - private LocalDateTime created; - - List items; -} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestMapper.java b/server/src/main/java/ru/practicum/shareit/request/RequestMapper.java deleted file mode 100644 index 9608ce2..0000000 --- a/server/src/main/java/ru/practicum/shareit/request/RequestMapper.java +++ /dev/null @@ -1,62 +0,0 @@ -package ru.practicum.shareit.request; - -import ru.practicum.shareit.item.ItemMapper; - -public class RequestMapper { - - public static RequestDto toRequestDto(Request request) { - if (request == null) { - return null; - } - RequestDto requestDto = new RequestDto(); - - requestDto.setId(request.getId()); - requestDto.setDescription(request.getDescription()); - requestDto.setRequestor(request.getRequestor()); - requestDto.setCreated(request.getCreated()); - - return requestDto; - } - - public static RequestDtoItems toRequestDtoItems(Request request) { - if (request == null) { - return null; - } - RequestDtoItems requestDto = new RequestDtoItems(); - - requestDto.setId(request.getId()); - requestDto.setDescription(request.getDescription()); - requestDto.setRequestor(request.getRequestor()); - requestDto.setCreated(request.getCreated()); - requestDto.setItems(request.getItems().stream().map(ItemMapper::toItemDtoRequest).toList()); - - return requestDto; - } - - - public static Request toRequest(RequestDto requestDto) { - Request request = new Request(); - - if (requestDto.getId() != null) { - if (requestDto.getId() > 0) { - request.setId(requestDto.getId()); - } - } - if (requestDto.getDescription() != null) { - if (!requestDto.getDescription().trim().isEmpty()) { - request.setDescription(requestDto.getDescription().trim()); - } - } - if (requestDto.getRequestor().getId() != null) { - if (requestDto.getRequestor().getId() > 0) { - request.setRequestor(requestDto.getRequestor()); - } - } - if (requestDto.getCreated() != null) { - request.setCreated(requestDto.getCreated()); - } - - return request; - } - -} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestRepository.java b/server/src/main/java/ru/practicum/shareit/request/RequestRepository.java deleted file mode 100644 index db723d3..0000000 --- a/server/src/main/java/ru/practicum/shareit/request/RequestRepository.java +++ /dev/null @@ -1,17 +0,0 @@ -package ru.practicum.shareit.request; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; -import ru.practicum.shareit.user.User; - -import java.util.List; - -@Repository -public interface RequestRepository extends JpaRepository { - - List findAllByRequestorOrderByCreatedDesc(User requestor); - - List findAllByOrderByCreatedDesc(); - - List findAllByRequestorNotOrderByCreatedDesc(User requestor); -} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestService.java b/server/src/main/java/ru/practicum/shareit/request/RequestService.java deleted file mode 100644 index 6e3bba3..0000000 --- a/server/src/main/java/ru/practicum/shareit/request/RequestService.java +++ /dev/null @@ -1,15 +0,0 @@ -package ru.practicum.shareit.request; - -import java.util.List; - -public interface RequestService { - - Request addRequest(Long requestorId, RequestDtoInput requestDto); - - List getRequests(Long requestorId); - - List getAllRequests(Long requestorId); - - Request getRequestById(Long requestId); - -} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java b/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java deleted file mode 100644 index 60dae61..0000000 --- a/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java +++ /dev/null @@ -1,107 +0,0 @@ -package ru.practicum.shareit.request; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import ru.practicum.shareit.exception.NotFoundException; -import ru.practicum.shareit.item.ItemRepository; -import ru.practicum.shareit.user.User; -import ru.practicum.shareit.user.UserRepository; - -import java.time.LocalDateTime; -import java.util.List; - -@Slf4j -@Service -@RequiredArgsConstructor -public class RequestServiceImpl implements RequestService { - - private final RequestRepository requestRepository; - private final UserRepository userRepository; - - /** - * Добавляет новый запрос в базу данных. - * - * @param requestorId идентификатор пользователя, который создает запрос - * @param requestDto объект, содержащий данные для создания запроса - * @return созданный запрос - * @throws NotFoundException если пользователь с указанным идентификатором не найден в базе данных - */ - @Override - public Request addRequest(Long requestorId, RequestDtoInput requestDto) { - User requestor = new User(); - Request request = new Request(); - requestor = userRepository.findById(requestorId).orElse(null); - if (requestor == null) { - String error = "Пользователь с id [ " + requestorId + " ] не найден в БД при добавлении запроса."; - log.info(error); - throw new NotFoundException(error); - } - request.setDescription(requestDto.getDescription()); - request.setRequestor(requestor); - request.setCreated(LocalDateTime.now()); - request = requestRepository.save(request); - log.info("Запрос [ {} ] добавлен в БД.",request); - return request; - } - - /** - * Возвращает список запросов, созданных определенным пользователем, отсортированный по дате создания в обратном порядке. - * - * @param requestorId идентификатор пользователя, чьи запросы нужно получить - * @return список запросов, созданных пользователем - */ - @Override - public List getRequests(Long requestorId) { - User requestor = new User(); - requestor = userRepository.findById(requestorId).orElse(null); - if (requestor == null) { - log.info("Пользователь с id [ {} ] не найден в БД при получении своего списка запросов - вернулся пустой список.", requestorId); - return List.of(); - } - List requests = requestRepository.findAllByRequestorOrderByCreatedDesc(requestor); - log.info("Список запросов пользователя [ {} ] получен из БД в количестве [ {} ].", requestorId, requests.size()); - return requests; - } - - /** - * Возвращает список всех запросов, отсортированных по дате создания в обратном порядке. - * Если передан идентификатор пользователя, то возвращаются все запросы, - * кроме тех, которые были созданы этим пользователем. - * - * @param requestorId идентификатор пользователя, чьи запросы нужно исключить из списка (может быть null) - * @return список всех запросов, отсортированных по дате создания в обратном порядке - */ - @Override - public List getAllRequests(Long requestorId) { - User requestor = new User(); - requestor = userRepository.findById(requestorId).orElse(null); - if (requestor == null) { - List requests = requestRepository.findAllByOrderByCreatedDesc(); - log.info("Список доступных запросов получен из БД в количестве [ {} ].", requests.size()); - return requests; - } - List requests = requestRepository.findAllByRequestorNotOrderByCreatedDesc(requestor); - log.info("Список доступных запросов ( кроме запросов пользователя [ {} ] ) получен из БД в количестве [ {} ].", - requestorId, requests.size()); - return requests; - } - - /** - * Возвращает запрос по его идентификатору. - * - * @param requestId идентификатор запроса - * @return запрос с указанным идентификатором - * @throws NotFoundException если запрос с указанным идентификатором не найден в базе данных - */ - @Override - public Request getRequestById(Long requestId) { - Request request = requestRepository.findById(requestId).orElse(null); - if (request == null) { - String error = "Запрос с id [ " + requestId + " ] не найден в БД при получении."; - log.info(error); - throw new NotFoundException(error); - } - return request; - } -} diff --git a/server/src/main/java/ru/practicum/shareit/user/UserDto.java b/server/src/main/java/ru/practicum/shareit/user/UserDto.java deleted file mode 100644 index 924c595..0000000 --- a/server/src/main/java/ru/practicum/shareit/user/UserDto.java +++ /dev/null @@ -1,29 +0,0 @@ -package ru.practicum.shareit.user; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.shareit.booking.Booking; -import ru.practicum.shareit.item.Comment; -import ru.practicum.shareit.item.Item; - -import java.util.List; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class UserDto { - - private Long id; - - private String name; - - private String email; - - private List items; - - private List bookings; - - private List comments; - -} diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties deleted file mode 100644 index 0f5ef57..0000000 --- a/server/src/main/resources/application.properties +++ /dev/null @@ -1,17 +0,0 @@ -server.port=9090 - -spring.jpa.hibernate.ddl-auto=none -spring.jpa.properties.hibernate.format_sql=true -spring.sql.init.mode=always - -#--- -spring.datasource.driverClassName=org.postgresql.Driver -spring.datasource.url=jdbc:postgresql://localhost:5432/shareit -spring.datasource.username=shareituser -spring.datasource.password=12345678 -#--- -spring.config.activate.on-profile=test -spring.datasource.driverClassName=org.h2.Driver -spring.datasource.url=jdbc:h2:mem:shareit -spring.datasource.username=shareit -spring.datasource.password=shareit \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/ShareItServer.java b/src/main/java/ru/practicum/shareit/ShareItApp.java similarity index 57% rename from server/src/main/java/ru/practicum/shareit/ShareItServer.java rename to src/main/java/ru/practicum/shareit/ShareItApp.java index 303541d..a10a87d 100644 --- a/server/src/main/java/ru/practicum/shareit/ShareItServer.java +++ b/src/main/java/ru/practicum/shareit/ShareItApp.java @@ -4,10 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class ShareItServer { +public class ShareItApp { - public static void main(String[] args) { - SpringApplication.run(ShareItServer.class, args); - } + public static void main(String[] args) { + SpringApplication.run(ShareItApp.class, args); + } } diff --git a/server/src/main/java/ru/practicum/shareit/booking/Booking.java b/src/main/java/ru/practicum/shareit/booking/Booking.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/booking/Booking.java rename to src/main/java/ru/practicum/shareit/booking/Booking.java diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingController.java b/src/main/java/ru/practicum/shareit/booking/BookingController.java similarity index 97% rename from server/src/main/java/ru/practicum/shareit/booking/BookingController.java rename to src/main/java/ru/practicum/shareit/booking/BookingController.java index fd62891..40df0bc 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ b/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.booking; +import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; @@ -19,7 +20,7 @@ public class BookingController { */ @PostMapping public BookingDtoOutput addBooking(@RequestHeader("X-Sharer-User-Id") Long bookerId, - @RequestBody BookingDtoInput bookingDto) { + @Valid @RequestBody BookingDtoInput bookingDto) { return BookingMapper.toBookingDtoOutput(bookingService.addBooking(bookerId, bookingDto)); } diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingDto.java b/src/main/java/ru/practicum/shareit/booking/BookingDto.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/booking/BookingDto.java rename to src/main/java/ru/practicum/shareit/booking/BookingDto.java diff --git a/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java b/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java new file mode 100644 index 0000000..b39e330 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java @@ -0,0 +1,28 @@ +package ru.practicum.shareit.booking; + +import jakarta.validation.constraints.FutureOrPresent; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.validation.CreateObject; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BookingDtoInput { + + @NotNull(groups = {CreateObject.class}, message = "При создании брони должна быть информация о вещи.") + private Long itemId; + + @FutureOrPresent(groups = {CreateObject.class}, message = "Дата не должна быть в прошлом") + @NotNull(groups = {CreateObject.class}, message = "Дата не должна быть пустой") + private LocalDateTime start; + + @FutureOrPresent(groups = {CreateObject.class}, message = "Дата не должна быть в прошлом") + @NotNull(groups = {CreateObject.class}, message = "Дата не должна быть пустой") + private LocalDateTime end; + +} diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java b/src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java rename to src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java b/src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java rename to src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java b/src/main/java/ru/practicum/shareit/booking/BookingMapper.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java rename to src/main/java/ru/practicum/shareit/booking/BookingMapper.java diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingRepository.java b/src/main/java/ru/practicum/shareit/booking/BookingRepository.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/booking/BookingRepository.java rename to src/main/java/ru/practicum/shareit/booking/BookingRepository.java diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingService.java b/src/main/java/ru/practicum/shareit/booking/BookingService.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/booking/BookingService.java rename to src/main/java/ru/practicum/shareit/booking/BookingService.java diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java b/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java similarity index 95% rename from server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java rename to src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java index d0f22f3..991eba2 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java +++ b/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java @@ -203,6 +203,7 @@ public List getByOwnerId(Long userId, String state) { switch (bookingState) { case ALL: { result = bookingRepository.findAllByItem_OwnerOrderByStartDesc(bookerFromDb); + System.out.println(result); break; } case CURRENT: { @@ -252,6 +253,18 @@ private void validateBooking(BookingDto bookingDto, Item item, User booker) { throw new ValidationException(message); } + if (bookingDto.getStart().equals(bookingDto.getEnd())) { + String message = "Начало и окончание бронирования не может быть одним и тем же временем."; + log.info(message); + throw new ValidationException(message); + } + + if (bookingDto.getEnd().isBefore(bookingDto.getStart())) { + String message = "Окончание бронирования не может быть раньше его начала."; + log.info(message); + throw new ValidationException(message); + } + List bookings = item.getBookings(); if (!bookings.isEmpty()) { for (Booking b : bookings) { diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingState.java b/src/main/java/ru/practicum/shareit/booking/BookingState.java similarity index 57% rename from server/src/main/java/ru/practicum/shareit/booking/BookingState.java rename to src/main/java/ru/practicum/shareit/booking/BookingState.java index 76499ce..75be8b4 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/BookingState.java +++ b/src/main/java/ru/practicum/shareit/booking/BookingState.java @@ -1,5 +1,5 @@ package ru.practicum.shareit.booking; public enum BookingState { - ALL, CURRENT, PAST, FUTURE, WAITING, REJECTED + ALL, CURRENT, PAST, FUTURE, WAITING, REJECTED; } diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingStatus.java b/src/main/java/ru/practicum/shareit/booking/BookingStatus.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/booking/BookingStatus.java rename to src/main/java/ru/practicum/shareit/booking/BookingStatus.java diff --git a/server/src/main/java/ru/practicum/shareit/exception/DataConflictException.java b/src/main/java/ru/practicum/shareit/exception/DataConflictException.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/exception/DataConflictException.java rename to src/main/java/ru/practicum/shareit/exception/DataConflictException.java diff --git a/server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java rename to src/main/java/ru/practicum/shareit/exception/ErrorHandler.java diff --git a/server/src/main/java/ru/practicum/shareit/exception/NotFoundException.java b/src/main/java/ru/practicum/shareit/exception/NotFoundException.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/exception/NotFoundException.java rename to src/main/java/ru/practicum/shareit/exception/NotFoundException.java diff --git a/server/src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java b/src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java rename to src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java diff --git a/gateway/src/main/java/ru/practicum/shareit/exception/ValidationException.java b/src/main/java/ru/practicum/shareit/exception/ValidationException.java similarity index 100% rename from gateway/src/main/java/ru/practicum/shareit/exception/ValidationException.java rename to src/main/java/ru/practicum/shareit/exception/ValidationException.java diff --git a/server/src/main/java/ru/practicum/shareit/item/Comment.java b/src/main/java/ru/practicum/shareit/item/Comment.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/item/Comment.java rename to src/main/java/ru/practicum/shareit/item/Comment.java diff --git a/server/src/main/java/ru/practicum/shareit/item/CommentDto.java b/src/main/java/ru/practicum/shareit/item/CommentDto.java similarity index 51% rename from server/src/main/java/ru/practicum/shareit/item/CommentDto.java rename to src/main/java/ru/practicum/shareit/item/CommentDto.java index fc9956b..bd2daa4 100644 --- a/server/src/main/java/ru/practicum/shareit/item/CommentDto.java +++ b/src/main/java/ru/practicum/shareit/item/CommentDto.java @@ -1,9 +1,12 @@ package ru.practicum.shareit.item; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import ru.practicum.shareit.user.User; +import ru.practicum.shareit.validation.CreateObject; import java.time.LocalDateTime; @@ -14,6 +17,8 @@ public class CommentDto { private Long id; + @NotNull(groups = {CreateObject.class}, message = "комментарий должен быть указан.") + @NotBlank(groups = {CreateObject.class}, message = "Комментарий не может быть пустым.") private String text; private Item item; diff --git a/server/src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java b/src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java rename to src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java diff --git a/server/src/main/java/ru/practicum/shareit/item/CommentDtoShort.java b/src/main/java/ru/practicum/shareit/item/CommentDtoShort.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/item/CommentDtoShort.java rename to src/main/java/ru/practicum/shareit/item/CommentDtoShort.java diff --git a/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java b/src/main/java/ru/practicum/shareit/item/CommentMapper.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/item/CommentMapper.java rename to src/main/java/ru/practicum/shareit/item/CommentMapper.java diff --git a/server/src/main/java/ru/practicum/shareit/item/CommentRepository.java b/src/main/java/ru/practicum/shareit/item/CommentRepository.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/item/CommentRepository.java rename to src/main/java/ru/practicum/shareit/item/CommentRepository.java diff --git a/server/src/main/java/ru/practicum/shareit/item/Item.java b/src/main/java/ru/practicum/shareit/item/Item.java similarity index 98% rename from server/src/main/java/ru/practicum/shareit/item/Item.java rename to src/main/java/ru/practicum/shareit/item/Item.java index bd80d79..99e8100 100644 --- a/server/src/main/java/ru/practicum/shareit/item/Item.java +++ b/src/main/java/ru/practicum/shareit/item/Item.java @@ -39,7 +39,7 @@ public class Item { @JoinColumn(name = "owner_id", nullable = false) private User owner; - @ManyToOne + @OneToOne @JoinColumn(name = "request_id") private Request request; diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemController.java b/src/main/java/ru/practicum/shareit/item/ItemController.java similarity index 85% rename from server/src/main/java/ru/practicum/shareit/item/ItemController.java rename to src/main/java/ru/practicum/shareit/item/ItemController.java index c95d869..affabdd 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemController.java +++ b/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -2,7 +2,11 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.validation.CreateObject; +import ru.practicum.shareit.validation.UpdateObject; + import java.util.List; @RestController @@ -13,14 +17,14 @@ public class ItemController { @PostMapping @ResponseStatus(HttpStatus.CREATED) - public ItemDtoOutput addItem(@RequestBody ItemDtoInput itemDto, + public ItemDtoOutput addItem(@Validated(CreateObject.class) @RequestBody ItemDto itemDto, @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { return ItemMapper.toItemDtoOutput(itemService.addItem(idUser, itemDto), idUser); } @PatchMapping("/{idItem}") public ItemDtoOutput updateItem(@PathVariable Long idItem, - @RequestBody ItemDtoInput itemDto, + @Validated(UpdateObject.class) @RequestBody ItemDto itemDto, @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { return ItemMapper.toItemDtoOutput(itemService.updateItem(idUser, idItem, itemDto), idUser); } @@ -52,8 +56,7 @@ public List searchItemsByText(@RequestParam String text, @PostMapping("/{itemId}/comment") public CommentDtoOutput addCommentToItem(@RequestHeader("X-Sharer-User-Id") Long userId, - @PathVariable Long itemId, - @RequestBody CommentDto commentDto) { + @PathVariable Long itemId, @RequestBody CommentDto commentDto) { return CommentMapper.toCommentDtoOutput((itemService.saveComment(userId, itemId, commentDto))); } } diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java b/src/main/java/ru/practicum/shareit/item/ItemDto.java similarity index 72% rename from gateway/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java rename to src/main/java/ru/practicum/shareit/item/ItemDto.java index 3173be9..2fb9d3e 100644 --- a/gateway/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java +++ b/src/main/java/ru/practicum/shareit/item/ItemDto.java @@ -5,12 +5,19 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.request.Request; +import ru.practicum.shareit.user.User; import ru.practicum.shareit.validation.CreateObject; +import java.util.List; + @Data @AllArgsConstructor @NoArgsConstructor -public class ItemDtoInput { +public class ItemDto { + + private Long id; @NotNull(groups = {CreateObject.class}, message = "Название вещи должно быть указано.") @NotBlank(groups = {CreateObject.class}, message = "Название вещи не может быть пустым.") @@ -23,6 +30,16 @@ public class ItemDtoInput { @NotNull(groups = {CreateObject.class}, message = "Доступность вещи должна быть указана.") private Boolean available; - private Long requestId; + private User owner; + + private Request request; + + private List comments; + + private List bookings; + + private Booking lastBooking; + + private Booking nextBooking; } diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java b/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java similarity index 88% rename from server/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java rename to src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java index c68c82f..36ff82c 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java +++ b/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java @@ -4,7 +4,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import ru.practicum.shareit.booking.BookingDtoShort; -import ru.practicum.shareit.request.RequestDto; +import ru.practicum.shareit.request.Request; import ru.practicum.shareit.user.UserDtoShort; import java.util.List; @@ -24,7 +24,7 @@ public class ItemDtoOutput { private UserDtoShort owner; - private RequestDto request; + private Request request; private List comments; diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDtoShort.java b/src/main/java/ru/practicum/shareit/item/ItemDtoShort.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/item/ItemDtoShort.java rename to src/main/java/ru/practicum/shareit/item/ItemDtoShort.java diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemMapper.java b/src/main/java/ru/practicum/shareit/item/ItemMapper.java similarity index 89% rename from server/src/main/java/ru/practicum/shareit/item/ItemMapper.java rename to src/main/java/ru/practicum/shareit/item/ItemMapper.java index 9f81b4e..d076fa1 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemMapper.java +++ b/src/main/java/ru/practicum/shareit/item/ItemMapper.java @@ -3,7 +3,6 @@ import ru.practicum.shareit.booking.Booking; import ru.practicum.shareit.booking.BookingMapper; import ru.practicum.shareit.booking.BookingStatus; -import ru.practicum.shareit.request.RequestMapper; import ru.practicum.shareit.user.UserMapper; import java.time.LocalDateTime; @@ -25,7 +24,7 @@ public static ItemDto toItemDto(Item item) { itemDto.setDescription(item.getDescription()); itemDto.setAvailable(item.getAvailable()); itemDto.setOwner(item.getOwner()); - itemDto.setRequest(RequestMapper.toRequestDto(item.getRequest())); + itemDto.setRequest(item.getRequest()); itemDto.setComments(item.getComments()); itemDto.setBookings(item.getBookings()); @@ -43,7 +42,7 @@ public static ItemDtoOutput toItemDtoOutput(Item item, Long userId) { itemDto.setDescription(item.getDescription()); itemDto.setAvailable(item.getAvailable()); itemDto.setOwner(UserMapper.toUserDtoShort(item.getOwner())); - itemDto.setRequest(RequestMapper.toRequestDto(item.getRequest())); + itemDto.setRequest(item.getRequest()); List comments = Optional.ofNullable(item.getComments()).orElse(Collections.emptyList()); if (!comments.isEmpty()) { itemDto.setComments(comments.stream().map(CommentMapper::toCommentDtoShort).toList()); @@ -84,20 +83,6 @@ public static ItemDtoShort toItemDtoShort(Item item) { return itemDto; } - public static ItemDtoRequest toItemDtoRequest(Item item) { - if (item == null) { - return null; - } - ItemDtoRequest itemDto = new ItemDtoRequest(); - - itemDto.setIdItem(item.getId()); - itemDto.setName(item.getName()); - itemDto.setIdOwner(item.getOwner().getId()); - itemDto.setOwner(item.getOwner().getName()); - - return itemDto; - } - public static Item toItem(ItemDto itemDto) { Item item = new Item(); @@ -122,7 +107,7 @@ public static Item toItem(ItemDto itemDto) { } if (itemDto.getRequest() != null) { if (itemDto.getRequest().getId() > 0) { - item.setRequest(RequestMapper.toRequest(itemDto.getRequest())); + item.setRequest(itemDto.getRequest()); } } if (itemDto.getComments() != null) { diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemRepository.java b/src/main/java/ru/practicum/shareit/item/ItemRepository.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/item/ItemRepository.java rename to src/main/java/ru/practicum/shareit/item/ItemRepository.java diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemService.java b/src/main/java/ru/practicum/shareit/item/ItemService.java similarity index 73% rename from server/src/main/java/ru/practicum/shareit/item/ItemService.java rename to src/main/java/ru/practicum/shareit/item/ItemService.java index c72f72e..ba5bf8a 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemService.java +++ b/src/main/java/ru/practicum/shareit/item/ItemService.java @@ -4,9 +4,9 @@ public interface ItemService { - Item addItem(Long idUser, ItemDtoInput itemDto); + Item addItem(Long idUser, ItemDto itemDto); - Item updateItem(Long idUser, Long idItem, ItemDtoInput itemDto); + Item updateItem(Long idUser, Long idItem, ItemDto itemDto); Item getItemById(Long idItem); diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java b/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java similarity index 86% rename from server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java rename to src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java index e939b0e..edaf54d 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java +++ b/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java @@ -1,6 +1,5 @@ package ru.practicum.shareit.item; -import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; @@ -8,8 +7,6 @@ import ru.practicum.shareit.exception.NotFoundException; import ru.practicum.shareit.exception.RestrictedAccessException; import ru.practicum.shareit.exception.ValidationException; -import ru.practicum.shareit.request.RequestMapper; -import ru.practicum.shareit.request.RequestRepository; import ru.practicum.shareit.user.User; import ru.practicum.shareit.user.UserMapper; import ru.practicum.shareit.user.UserRepository; @@ -20,41 +17,35 @@ @Slf4j @Service -@RequiredArgsConstructor public class ItemServiceImpl implements ItemService { private final ItemRepository itemRepository; private final UserRepository userRepository; private final CommentRepository commentRepository; - private final RequestRepository requestRepository; + + public ItemServiceImpl(ItemRepository itemRepository, UserRepository userRepository, CommentRepository commentRepository) { + this.itemRepository = itemRepository; + this.userRepository = userRepository; + this.commentRepository = commentRepository; + } /** * Метод для добавления новой вещи. * * @param idUser идентификатор пользователя - * @param inputItemDto объект ItemDtoInput, содержащий данные новой вещи + * @param itemDto объект ItemDto, содержащий данные новой вещи * @return объект Item, содержащий данные добавленной вещи * @throws ValidationException если идентификатор пользователя не указан * @throws NotFoundException если пользователь с указанным id не найден в БД */ @Override - public Item addItem(Long idUser, ItemDtoInput inputItemDto) { - ItemDto itemDto = new ItemDto(); - User user = userRepository.findById(idUser).orElse(null); - if (user == null) { + public Item addItem(Long idUser, ItemDto itemDto) { + if (userRepository.findById(idUser).isEmpty()) { String error = "Пользователь с id [ " + idUser + " ] не найден в БД при добавлении вещи."; log.info(error); throw new NotFoundException(error); } - itemDto.setOwner(user); - itemDto.setName(inputItemDto.getName()); - itemDto.setDescription(inputItemDto.getDescription()); - itemDto.setAvailable(inputItemDto.getAvailable()); - if (inputItemDto.getRequestId() == null) { - itemDto.setRequest(null); - } else { - itemDto.setRequest(RequestMapper.toRequestDto(requestRepository.findById(inputItemDto.getRequestId()).orElse(null))); - } + itemDto.setOwner(userRepository.findById(idUser).get()); Item result = ItemMapper.toItem(itemDto); itemRepository.save(result); log.info("Добавлена вещь [ {} ] пользователем [ {} ]", result.getId(), idUser); @@ -66,15 +57,14 @@ public Item addItem(Long idUser, ItemDtoInput inputItemDto) { * * @param idUser идентификатор пользователя * @param idItem идентификатор вещи - * @param itemDtoInput объект ItemDtoInput, содержащий новые данные вещи + * @param itemDto объект ItemDto, содержащий новые данные вещи * @return объект Item, содержащий обновленные данные вещи * @throws ValidationException если идентификатор пользователя не указан * @throws NotFoundException если пользователь или вещь с указанным id не найдены в БД * @throws RestrictedAccessException если пользователь не является владельцем вещи */ @Override - public Item updateItem(Long idUser, Long idItem, ItemDtoInput itemDtoInput) { - ItemDto itemDto = new ItemDto(); + public Item updateItem(Long idUser, Long idItem, ItemDto itemDto) { if (userRepository.findById(idUser).isEmpty()) { String error = "Пользователь с id [ " + idUser + " ] не найден в БД при изменение данных вещи."; log.info(error); @@ -93,21 +83,15 @@ public Item updateItem(Long idUser, Long idItem, ItemDtoInput itemDtoInput) { } itemDto.setId(idItem); itemDto.setOwner(oldItem.get().getOwner()); - itemDto.setRequest(RequestMapper.toRequestDto(oldItem.get().getRequest())); - if (itemDtoInput.getName() == null) { + itemDto.setRequest(oldItem.get().getRequest()); + if (itemDto.getName() == null) { itemDto.setName(oldItem.get().getName()); - } else { - itemDto.setName(itemDtoInput.getName()); } - if (itemDtoInput.getDescription() == null) { + if (itemDto.getDescription() == null) { itemDto.setDescription(oldItem.get().getDescription()); - } else { - itemDto.setDescription(itemDtoInput.getDescription()); } - if (itemDtoInput.getAvailable() == null) { + if (itemDto.getAvailable() == null) { itemDto.setAvailable(oldItem.get().getAvailable()); - } else { - itemDto.setAvailable(itemDtoInput.getAvailable()); } Item newItem = ItemMapper.toItem(itemDto); itemRepository.save(newItem); diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequestController.java b/src/main/java/ru/practicum/shareit/request/ItemRequestController.java new file mode 100644 index 0000000..064e2e9 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/request/ItemRequestController.java @@ -0,0 +1,12 @@ +package ru.practicum.shareit.request; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * TODO Sprint add-item-requests. + */ +@RestController +@RequestMapping(path = "/requests") +public class ItemRequestController { +} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestDto.java b/src/main/java/ru/practicum/shareit/request/ItemRequestDto.java similarity index 87% rename from server/src/main/java/ru/practicum/shareit/request/RequestDto.java rename to src/main/java/ru/practicum/shareit/request/ItemRequestDto.java index a49428d..16d55ee 100644 --- a/server/src/main/java/ru/practicum/shareit/request/RequestDto.java +++ b/src/main/java/ru/practicum/shareit/request/ItemRequestDto.java @@ -10,14 +10,11 @@ @Data @AllArgsConstructor @NoArgsConstructor -public class RequestDto { - - private Long id; +public class ItemRequestDto { private String description; private User requestor; private LocalDateTime created; - } diff --git a/server/src/main/java/ru/practicum/shareit/request/Request.java b/src/main/java/ru/practicum/shareit/request/Request.java similarity index 88% rename from server/src/main/java/ru/practicum/shareit/request/Request.java rename to src/main/java/ru/practicum/shareit/request/Request.java index 5947b30..2e67520 100644 --- a/server/src/main/java/ru/practicum/shareit/request/Request.java +++ b/src/main/java/ru/practicum/shareit/request/Request.java @@ -6,7 +6,6 @@ import ru.practicum.shareit.user.User; import java.time.LocalDateTime; -import java.util.List; @Getter @Setter @@ -28,9 +27,9 @@ public class Request { @JoinColumn(name = "requestor_id", nullable = false) private User requestor; + @OneToOne(mappedBy = "request") + private Item requestItem; + @Column(nullable = false) private LocalDateTime created; - - @OneToMany(mappedBy = "request") - private List items; } diff --git a/server/src/main/java/ru/practicum/shareit/user/User.java b/src/main/java/ru/practicum/shareit/user/User.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/user/User.java rename to src/main/java/ru/practicum/shareit/user/User.java diff --git a/server/src/main/java/ru/practicum/shareit/user/UserController.java b/src/main/java/ru/practicum/shareit/user/UserController.java similarity index 73% rename from server/src/main/java/ru/practicum/shareit/user/UserController.java rename to src/main/java/ru/practicum/shareit/user/UserController.java index 98c0885..38dff38 100644 --- a/server/src/main/java/ru/practicum/shareit/user/UserController.java +++ b/src/main/java/ru/practicum/shareit/user/UserController.java @@ -2,8 +2,10 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; - +import ru.practicum.shareit.validation.CreateObject; +import ru.practicum.shareit.validation.UpdateObject; import java.util.List; @@ -15,13 +17,12 @@ public class UserController { @PostMapping @ResponseStatus(HttpStatus.CREATED) - public UserDtoOutput addUser(@RequestBody UserDto userDto) { + public UserDtoOutput addUser(@Validated(CreateObject.class) @RequestBody UserDto userDto) { return UserMapper.toUserDtoOutput(userService.addUser(userDto)); } @PatchMapping("/{idUser}") - public UserDtoOutput updateUser(@PathVariable Long idUser, - @RequestBody UserDto userDto) { + public UserDtoOutput updateUser(@PathVariable Long idUser, @Validated(UpdateObject.class) @RequestBody UserDto userDto) { return UserMapper.toUserDtoOutput(userService.updateUser(idUser, userDto)); } diff --git a/gateway/src/main/java/ru/practicum/shareit/user/UserDtoInput.java b/src/main/java/ru/practicum/shareit/user/UserDto.java similarity index 59% rename from gateway/src/main/java/ru/practicum/shareit/user/UserDtoInput.java rename to src/main/java/ru/practicum/shareit/user/UserDto.java index 702908a..122a434 100644 --- a/gateway/src/main/java/ru/practicum/shareit/user/UserDtoInput.java +++ b/src/main/java/ru/practicum/shareit/user/UserDto.java @@ -6,19 +6,33 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.item.Comment; +import ru.practicum.shareit.item.Item; import ru.practicum.shareit.validation.CreateObject; +import ru.practicum.shareit.validation.UpdateObject; + +import java.util.List; @Data @AllArgsConstructor @NoArgsConstructor -public class UserDtoInput { +public class UserDto { + + private Long id; @NotNull(groups = {CreateObject.class}, message = "Имя должно быть указано.") @NotBlank(groups = {CreateObject.class}, message = "Имя не может быть пустым.") private String name; @NotNull(groups = {CreateObject.class}, message = "Email должен быть указан.") - @Email(groups = {CreateObject.class}, message = "Email должен быть указан корректно.") + @Email(groups = {CreateObject.class, UpdateObject.class}, message = "Email должен быть указан корректно.") private String email; + private List items; + + private List bookings; + + private List comments; + } diff --git a/server/src/main/java/ru/practicum/shareit/user/UserDtoOutput.java b/src/main/java/ru/practicum/shareit/user/UserDtoOutput.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/user/UserDtoOutput.java rename to src/main/java/ru/practicum/shareit/user/UserDtoOutput.java diff --git a/server/src/main/java/ru/practicum/shareit/user/UserDtoShort.java b/src/main/java/ru/practicum/shareit/user/UserDtoShort.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/user/UserDtoShort.java rename to src/main/java/ru/practicum/shareit/user/UserDtoShort.java diff --git a/server/src/main/java/ru/practicum/shareit/user/UserMapper.java b/src/main/java/ru/practicum/shareit/user/UserMapper.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/user/UserMapper.java rename to src/main/java/ru/practicum/shareit/user/UserMapper.java diff --git a/server/src/main/java/ru/practicum/shareit/user/UserRepository.java b/src/main/java/ru/practicum/shareit/user/UserRepository.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/user/UserRepository.java rename to src/main/java/ru/practicum/shareit/user/UserRepository.java diff --git a/server/src/main/java/ru/practicum/shareit/user/UserService.java b/src/main/java/ru/practicum/shareit/user/UserService.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/user/UserService.java rename to src/main/java/ru/practicum/shareit/user/UserService.java diff --git a/server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java b/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java similarity index 100% rename from server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java rename to src/main/java/ru/practicum/shareit/user/UserServiceImpl.java diff --git a/gateway/src/main/java/ru/practicum/shareit/validation/CreateObject.java b/src/main/java/ru/practicum/shareit/validation/CreateObject.java similarity index 100% rename from gateway/src/main/java/ru/practicum/shareit/validation/CreateObject.java rename to src/main/java/ru/practicum/shareit/validation/CreateObject.java diff --git a/gateway/src/main/java/ru/practicum/shareit/validation/UpdateObject.java b/src/main/java/ru/practicum/shareit/validation/UpdateObject.java similarity index 100% rename from gateway/src/main/java/ru/practicum/shareit/validation/UpdateObject.java rename to src/main/java/ru/practicum/shareit/validation/UpdateObject.java diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 0000000..faf6257 --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,20 @@ +spring: + application.name: shareit + main.banner-mode: off + datasource: + url: jdbc:postgresql://localhost:5432/shareit + driver-class-name: org.postgresql.Driver + username: shareituser + password: 12345678 + jpa: + show-sql: true + hibernate: + ddl-auto: update + properties: + hibernate.jdbc.time_zone: UTC + dialect: org.hibernate.dialect.PostgreSQL95Dialect + format_sql: true + sql: + init: + mode: always + diff --git a/server/src/main/resources/schema.sql b/src/main/resources/schema.sql similarity index 100% rename from server/src/main/resources/schema.sql rename to src/main/resources/schema.sql From c22333389956d270eaafca80b1df80ec6504b28c Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 21:49:42 +0300 Subject: [PATCH 07/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9F=D0=B5=D1=80=D0=B2=D0=B8=D1=87=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +- checkstyle.xml | 257 -- docker-compose.yml | 39 + gateway/Dockerfile | 5 + gateway/pom.xml | 70 + .../ru/practicum/shareit/ShareItGateway.java | 12 + .../shareit/booking/BookingClient.java | 58 + .../shareit/booking/BookingController.java | 100 + .../shareit/booking/BookingDtoInput.java | 28 + .../shareit/booking/BookingState.java | 16 + .../practicum/shareit/client/BaseClient.java | 121 + .../shareit/exception/ErrorHandler.java | 65 + .../exception/ValidationException.java | 7 + .../shareit/item/CommentDtoInput.java | 18 + .../ru/practicum/shareit/item/ItemClient.java | 54 + .../shareit/item/ItemController.java | 122 + .../practicum/shareit/item/ItemDtoInput.java | 28 + .../shareit/request/RequestClient.java | 41 + .../shareit/request/RequestController.java | 73 + .../shareit/request/RequestDtoInput.java | 18 + .../ru/practicum/shareit/user/UserClient.java | 46 + .../shareit/user/UserController.java | 81 + .../practicum/shareit/user/UserDtoInput.java | 24 + .../shareit/validation/CreateObject.java | 4 + .../shareit/validation/UpdateObject.java | 4 + .../src/main/resources/application.properties | 7 + pom.xml | 97 +- postman_for_shareit_14.json | 2725 ----------------- ...eit_15.json => postman_for_shareit_16.json | 1213 ++------ server/Dockerfile | 5 + server/pom.xml | 86 + .../ru/practicum/shareit/ShareItServer.java | 13 + .../ru/practicum/shareit/booking/Booking.java | 41 + .../shareit/booking/BookingController.java | 85 + .../practicum/shareit/booking/BookingDto.java | 28 + .../shareit/booking/BookingDtoInput.java | 20 + .../shareit/booking/BookingDtoOutput.java | 30 + .../shareit/booking/BookingDtoShort.java | 24 + .../shareit/booking/BookingMapper.java | 65 + .../shareit/booking/BookingRepository.java | 32 + .../shareit/booking/BookingService.java | 16 + .../shareit/booking/BookingServiceImpl.java | 267 ++ .../shareit/booking/BookingState.java | 5 + .../shareit/booking/BookingStatus.java | 7 + .../exception/DataConflictException.java | 7 + .../shareit/exception/ErrorHandler.java | 84 + .../shareit/exception/NotFoundException.java | 7 + .../exception/RestrictedAccessException.java | 7 + .../exception/ValidationException.java | 7 + .../ru/practicum/shareit/item/Comment.java | 36 + .../ru/practicum/shareit/item/CommentDto.java | 25 + .../shareit/item/CommentDtoOutput.java | 24 + .../shareit/item/CommentDtoShort.java | 24 + .../practicum/shareit/item/CommentMapper.java | 84 + .../shareit/item/CommentRepository.java | 9 + .../java/ru/practicum/shareit/item/Item.java | 52 + .../shareit/item/ItemController.java | 59 + .../ru/practicum/shareit/item/ItemDto.java | 37 + .../practicum/shareit/item/ItemDtoInput.java | 20 + .../practicum/shareit/item/ItemDtoOutput.java | 37 + .../shareit/item/ItemDtoRequest.java | 20 + .../practicum/shareit/item/ItemDtoShort.java | 20 + .../ru/practicum/shareit/item/ItemMapper.java | 174 ++ .../shareit/item/ItemRepository.java | 23 + .../practicum/shareit/item/ItemService.java | 21 + .../shareit/item/ItemServiceImpl.java | 234 ++ .../ru/practicum/shareit/request/Request.java | 36 + .../shareit/request/RequestController.java | 39 + .../practicum/shareit/request/RequestDto.java | 23 + .../shareit/request/RequestDtoInput.java | 14 + .../shareit/request/RequestDtoItems.java | 26 + .../shareit/request/RequestMapper.java | 62 + .../shareit/request/RequestRepository.java | 17 + .../shareit/request/RequestService.java | 15 + .../shareit/request/RequestServiceImpl.java | 107 + .../java/ru/practicum/shareit/user/User.java | 39 + .../shareit/user/UserController.java | 42 + .../ru/practicum/shareit/user/UserDto.java | 29 + .../practicum/shareit/user/UserDtoOutput.java | 29 + .../practicum/shareit/user/UserDtoShort.java | 18 + .../ru/practicum/shareit/user/UserMapper.java | 96 + .../shareit/user/UserRepository.java | 11 + .../practicum/shareit/user/UserService.java | 17 + .../shareit/user/UserServiceImpl.java | 132 + .../src/main/resources/application.properties | 17 + server/src/main/resources/schema.sql | 44 + 86 files changed, 3840 insertions(+), 4047 deletions(-) delete mode 100644 checkstyle.xml create mode 100644 docker-compose.yml create mode 100644 gateway/Dockerfile 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 create mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/booking/BookingState.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/exception/ValidationException.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/item/CommentDtoInput.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/ItemDtoInput.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/RequestDtoInput.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/UserDtoInput.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/validation/CreateObject.java create mode 100644 gateway/src/main/java/ru/practicum/shareit/validation/UpdateObject.java create mode 100644 gateway/src/main/resources/application.properties delete mode 100644 postman_for_shareit_14.json rename postman_for_shareit_15.json => postman_for_shareit_16.json (81%) create mode 100644 server/Dockerfile create mode 100644 server/pom.xml create mode 100644 server/src/main/java/ru/practicum/shareit/ShareItServer.java create mode 100644 server/src/main/java/ru/practicum/shareit/booking/Booking.java create mode 100644 server/src/main/java/ru/practicum/shareit/booking/BookingController.java create mode 100644 server/src/main/java/ru/practicum/shareit/booking/BookingDto.java create mode 100644 server/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java create mode 100644 server/src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java create mode 100644 server/src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java create mode 100644 server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java create mode 100644 server/src/main/java/ru/practicum/shareit/booking/BookingRepository.java create mode 100644 server/src/main/java/ru/practicum/shareit/booking/BookingService.java create mode 100644 server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java create mode 100644 server/src/main/java/ru/practicum/shareit/booking/BookingState.java create mode 100644 server/src/main/java/ru/practicum/shareit/booking/BookingStatus.java create mode 100644 server/src/main/java/ru/practicum/shareit/exception/DataConflictException.java create mode 100644 server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java create mode 100644 server/src/main/java/ru/practicum/shareit/exception/NotFoundException.java create mode 100644 server/src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java create mode 100644 server/src/main/java/ru/practicum/shareit/exception/ValidationException.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/Comment.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/CommentDto.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/CommentDtoShort.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/CommentMapper.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/CommentRepository.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/Item.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemController.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemDto.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemDtoShort.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemMapper.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemRepository.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemService.java create mode 100644 server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/Request.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestController.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestDto.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestDtoItems.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestMapper.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestRepository.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestService.java create mode 100644 server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java create mode 100644 server/src/main/java/ru/practicum/shareit/user/User.java create mode 100644 server/src/main/java/ru/practicum/shareit/user/UserController.java create mode 100644 server/src/main/java/ru/practicum/shareit/user/UserDto.java create mode 100644 server/src/main/java/ru/practicum/shareit/user/UserDtoOutput.java create mode 100644 server/src/main/java/ru/practicum/shareit/user/UserDtoShort.java create mode 100644 server/src/main/java/ru/practicum/shareit/user/UserMapper.java create mode 100644 server/src/main/java/ru/practicum/shareit/user/UserRepository.java create mode 100644 server/src/main/java/ru/practicum/shareit/user/UserService.java create mode 100644 server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java create mode 100644 server/src/main/resources/application.properties create mode 100644 server/src/main/resources/schema.sql diff --git a/README.md b/README.md index d4b18b6..bd0da80 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ _java-shareit_ # Шеринг вещей -### Согласно задания спринта № 14 (add-controllers) +### Согласно задания спринта № 16 (add-item-requests-and-gateway) ## выполнено Филипповских Сергеем _**Когорта-53**_ -_**Для проверки выполнения 14 спринта использовался postman: -[postman_for_shareit_14.json](/postman_for_shareit_14.json)**_ +_**Для проверки выполнения 16 спринта использовался postman: +[postman_for_shareit_16.json](/postman_for_shareit_16.json)**_ ![](/er_diagram_shareit.png) diff --git a/checkstyle.xml b/checkstyle.xml deleted file mode 100644 index c28a0d3..0000000 --- a/checkstyle.xml +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..abe6570 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,39 @@ +services: + gateway: + build: gateway + image: shareit-gateway + container_name: shareit-gateway + ports: + - "8080:8080" + depends_on: + - server + environment: + - SHAREIT_SERVER_URL=http://server:9090 + + server: + build: server + image: shareit-server + container_name: shareit-server + ports: + - "9090:9090" + depends_on: + - db + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/shareit + - SPRING_DATASOURCE_USERNAME=shareit + - SPRING_DATASOURCE_PASSWORD=shareit + + db: + image: postgres:16.1 + container_name: postgres + ports: + - "6541:5432" + environment: + - POSTGRES_PASSWORD=shareit + - POSTGRES_USER=shareit + - POSTGRES_DB=shareit + healthcheck: + test: pg_isready -q -d $$POSTGRES_DB -U $$POSTGRES_USER + timeout: 5s + interval: 5s + retries: 10 \ No newline at end of file diff --git a/gateway/Dockerfile b/gateway/Dockerfile new file mode 100644 index 0000000..0ff1817 --- /dev/null +++ b/gateway/Dockerfile @@ -0,0 +1,5 @@ +FROM eclipse-temurin:21-jre-jammy +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/gateway/pom.xml b/gateway/pom.xml new file mode 100644 index 0000000..f3394c1 --- /dev/null +++ b/gateway/pom.xml @@ -0,0 +1,70 @@ + + + 4.0.0 + + ru.practicum + shareit + 0.0.1-SNAPSHOT + + + shareit-gateway + 0.0.1-SNAPSHOT + + ShareIt Gateway + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.hibernate.validator + hibernate-validator + + + + org.apache.httpcomponents.client5 + httpclient5 + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + 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..0aa75c3 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java @@ -0,0 +1,12 @@ +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); + } + +} 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..7975693 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java @@ -0,0 +1,58 @@ +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.client.BaseClient; +import ru.practicum.shareit.exception.ValidationException; + +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 addBooking(Long userId, BookingDtoInput requestDto) { + if (requestDto.getStart().equals(requestDto.getEnd())) { + throw new ValidationException("Начало и окончание бронирования не может быть одним и тем же моментом."); + } + if (requestDto.getEnd().isBefore(requestDto.getStart())) { + throw new ValidationException("Окончание бронирования не может быть раньше его начала."); + } + return post("", userId, requestDto); + } + + public ResponseEntity updateByOwner(Long userId, Long bookingId, Boolean approved) { + Map parameters = Map.of( + "approved", approved + ); + return patch("/" + bookingId + "?approved={approved}", userId, parameters, null); + } + + public ResponseEntity getWithStatusById(Long bookingId, Long userId) { + return get("/" + bookingId, userId); + } + + public ResponseEntity getByUserId(Long userId, BookingState state) { + return get("?state=" + state, userId); + } + + public ResponseEntity getByOwnerId(Long userId, BookingState state) { + return get("/owner?state=" + state, userId); + } +} 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..6d368fb --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -0,0 +1,100 @@ +package ru.practicum.shareit.booking; + +import jakarta.validation.Valid; +import jakarta.validation.ValidationException; +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.*; + +@Controller +@RequestMapping(path = "/bookings") +@RequiredArgsConstructor +@Slf4j +@Validated +public class BookingController { + + private final BookingClient bookingClient; + + /** + * Создает бронирование. + * + * @param bookerId идентификатор пользователя, создающего бронирование + * @param bookingDto данные бронирования + * @return ResponseEntity с объектом, представляющим результат создания бронирования + */ + @PostMapping + public ResponseEntity addBooking(@RequestHeader("X-Sharer-User-Id") Long bookerId, + @Valid @RequestBody BookingDtoInput bookingDto) { + log.info("Создано бронирование {}, пользователем {}", bookingDto, bookerId); + return bookingClient.addBooking(bookerId, bookingDto); + } + + /** + * Обновляет бронирование владельцем. + * + * @param ownerId идентификатор владельца бронирования + * @param bookingId идентификатор бронирования + * @param approved флаг, указывающий, одобрено ли бронирование + * @return ResponseEntity с объектом, представляющим результат обновления бронирования + */ + @PatchMapping("/{bookingId}") + public ResponseEntity updateByOwner(@RequestHeader("X-Sharer-User-Id") Long ownerId, + @PathVariable Long bookingId, + @RequestParam Boolean approved) { + log.info("Обновление бронирования {}, владельцем {}, в состояние {}", bookingId, ownerId, approved); + return bookingClient.updateByOwner(ownerId, bookingId, approved); + } + + /** + * Получает данные о бронировании по его идентификатору. + * + * @param userId идентификатор пользователя, запрашивающего данные о бронировании + * @param bookingId идентификатор бронирования + * @return ResponseEntity с объектом, представляющим результат получения данных о бронировании + */ + @GetMapping("/{bookingId}") + public ResponseEntity getWithStatusById(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long bookingId) { + log.info("Получение данных о бронировании {}, пользователем {}", bookingId, userId); + return bookingClient.getWithStatusById( bookingId, userId); + } + + /** + * Получает все бронирования пользователя по его идентификатору и состоянию. + * + * @param userId идентификатор пользователя, запрашивающего данные о бронировании + * @param stateParam состояние бронирования + * @return ResponseEntity с объектом, представляющим результат получения данных о бронировании + */ + @GetMapping + public ResponseEntity getByUserId(@RequestHeader("X-Sharer-User-Id") Long userId, + @RequestParam(value = "state", + defaultValue = "ALL", required = false) String stateParam) { + BookingState state = BookingState.from(stateParam) + .orElseThrow(() -> + new ValidationException("Неизвестное состояние бронирования: " + stateParam)); + log.info("Получение всех бронирований пользователя{}, в состоянии {}", userId, state); + return bookingClient.getByUserId(userId, state); + } + + /** + * Получает все забронированные вещи пользователя по его идентификатору и состоянию. + * + * @param userId идентификатор пользователя, запрашивающего данные о бронировании + * @param stateParam состояние бронирования + * @return ResponseEntity с объектом, представляющим результат получения данных о бронировании + */ + @GetMapping("/owner") + public ResponseEntity getByOwnerId(@RequestHeader("X-Sharer-User-Id") Long userId, + @RequestParam(value = "state", defaultValue = "ALL", + required = false) String stateParam) { + BookingState state = BookingState.from(stateParam) + .orElseThrow(() -> + new ValidationException("Неизвестное состояние бронирования: " + stateParam)); + log.info("Получение всех забронированных вещей пользователя{}, в состоянии {}", userId, state); + return bookingClient.getByOwnerId(userId, state); + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java new file mode 100644 index 0000000..0441ea2 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java @@ -0,0 +1,28 @@ +package ru.practicum.shareit.booking; + +import jakarta.validation.constraints.Future; +import jakarta.validation.constraints.FutureOrPresent; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BookingDtoInput { + + @NotNull(message = "При создании брони должна быть информация о вещи.") + private Long itemId; + + @FutureOrPresent(message = "Дата начала бронирования не должна быть в прошлом") + @NotNull(message = "Дата бронирования не должна быть пустой") + private LocalDateTime start; + + @Future(message = "Дата завершения бронирования должна быть в будущем") + @NotNull(message = "Дата бронирования не должна быть пустой") + private LocalDateTime end; + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingState.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingState.java new file mode 100644 index 0000000..e633731 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingState.java @@ -0,0 +1,16 @@ +package ru.practicum.shareit.booking; + +import java.util.Optional; + +public enum BookingState { + ALL, CURRENT, PAST, FUTURE, WAITING, REJECTED; + + public static Optional from(String stringState) { + for (ru.practicum.shareit.booking.BookingState state : values()) { + if (state.name().equalsIgnoreCase(stringState)) { + return Optional.of(state); + } + } + return Optional.empty(); + } +} 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..1a2d33a --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java @@ -0,0 +1,121 @@ +package ru.practicum.shareit.client; + +import java.util.List; +import java.util.Map; + +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; + +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, 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(); + } +} 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..6e40c90 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -0,0 +1,65 @@ +package ru.practicum.shareit.exception; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.validation.BindingResult; +import org.springframework.validation.ObjectError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingRequestHeaderException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Slf4j +@RestControllerAdvice +public class ErrorHandler { + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleValidationException(final ValidationException e) { + log.error("{} - {}", HttpStatus.BAD_REQUEST, e.getMessage()); + return new ErrorResponse(e.getMessage()); + } + + + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleMethodArgumentNotValidException(final MethodArgumentNotValidException e) { + BindingResult bindingResult = e.getBindingResult(); + List allErrors = bindingResult.getAllErrors(); + String defaultMessage = allErrors.stream() + .map(error -> Objects.requireNonNull(error.getDefaultMessage())) + .collect(Collectors.joining(", ")); + log.error("{} - {}", HttpStatus.BAD_REQUEST, defaultMessage); + return new ErrorResponse(defaultMessage); + } + + + @ExceptionHandler + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ErrorResponse handleRunTimeException(final RuntimeException e) { + log.error("{} - {}", HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(e.getMessage()); + } + + @ExceptionHandler(MissingRequestHeaderException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleMissingRequestHeaderException(MissingRequestHeaderException e) { + log.error("{} - {}", HttpStatus.BAD_REQUEST, e.getMessage()); + return new ErrorResponse(e.getMessage()); + } + + @Getter + public static class ErrorResponse { + private final String error; + + public ErrorResponse(String error) { + this.error = error; + } + + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/exception/ValidationException.java b/gateway/src/main/java/ru/practicum/shareit/exception/ValidationException.java new file mode 100644 index 0000000..59043da --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/exception/ValidationException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class ValidationException extends RuntimeException { + public ValidationException(String message) { + super(message); + } +} diff --git a/gateway/src/main/java/ru/practicum/shareit/item/CommentDtoInput.java b/gateway/src/main/java/ru/practicum/shareit/item/CommentDtoInput.java new file mode 100644 index 0000000..d15069d --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/CommentDtoInput.java @@ -0,0 +1,18 @@ +package ru.practicum.shareit.item; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CommentDtoInput { + + @NotNull(message = "Комментарий должен быть указан.") + @NotBlank(message = "Комментарий не может быть пустым.") + private String text; + +} 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..ddf344c --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java @@ -0,0 +1,54 @@ +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; + +@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 addItem(Long userId, ItemDtoInput itemDto) { + return post("", userId, itemDto); + } + + public ResponseEntity updateItem(Long userId, Long idItem, ItemDtoInput itemDto) { + return patch("/" + idItem, userId, itemDto); + } + + public ResponseEntity getItemById(Long idItem, Long userId) { + return get("/" + idItem, userId); + } + + public ResponseEntity getAllItems(Long userId) { + return get("", userId); + } + + public ResponseEntity removeItem(Long userId, Long idItem) { + return delete("/" + idItem, userId); + } + + public ResponseEntity searchItems(String text) { + return get("/search?text=" + text); + } + + public ResponseEntity saveComment(Long userId, Long itemId, CommentDtoInput commentDto) { + return post("/" + itemId + "/comment", userId, commentDto); + } + +} 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..6de8007 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -0,0 +1,122 @@ +package ru.practicum.shareit.item; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +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.validation.CreateObject; +import ru.practicum.shareit.validation.UpdateObject; + +@Controller +@RequestMapping(path = "/items") +@RequiredArgsConstructor +@Slf4j +@Validated +public class ItemController { + private final ItemClient itemClient; + + /** + * Добавляет новую вещь. + * + * @param itemDto Данные вещи. + * @param idUser Идентификатор пользователя, добавляющего вещь. + * @return ResponseEntity с объектом, представляющим результат добавления вещи. + */ + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity addItem(@Validated(CreateObject.class) @RequestBody ItemDtoInput itemDto, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + log.info("Получен запрос пользователем {} на добавление вещи {}", idUser, itemDto); + return itemClient.addItem(idUser, itemDto); + } + + /** + * Обновляет вещь. + * + * @param idItem Идентификатор вещи, которую нужно обновить. + * @param itemDto Данные вещи. + * @param idUser Идентификатор пользователя, обновляющего вещь. + * @return ResponseEntity с объектом, представляющим результат обновления вещи. + */ + @PatchMapping("/{idItem}") + public ResponseEntity updateItem(@PathVariable Long idItem, + @Validated(UpdateObject.class) @RequestBody ItemDtoInput itemDto, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + log.info("Получен запрос пользователем {} на обновление вещи {}", idUser, itemDto); + return itemClient.updateItem(idUser, idItem, itemDto); + } + + /** + * Возвращает вещь по ее идентификатору. + * + * @param idItem Идентификатор вещи, которую нужно вернуть. + * @param idUser Идентификатор пользователя, запрашивающего вещь. + * @return ResponseEntity с объектом, представляющим вещь по ее идентификатору. + */ + @GetMapping("/{idItem}") + public ResponseEntity getItemById(@PathVariable Long idItem, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + log.info("Получен запрос пользователем {} на получение вещи с id={}", idUser, idItem); + return itemClient.getItemById(idItem, idUser); + } + + /** + * Возвращает все вещи пользователя. + * + * @param idUser Идентификатор пользователя, вещи которого нужно вернуть. + * @return ResponseEntity с объектом, представляющим все вещи пользователя. + */ + @GetMapping + public ResponseEntity getAllItemsByUser(@RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + log.info("Получен запрос пользователем {} на получение всех своих вещей", idUser); + return itemClient.getAllItems(idUser); + } + + /** + * Удаляет вещь по ее идентификатору. + * + * @param idItem Идентификатор вещи, которую нужно удалить. + * @param idUser Идентификатор пользователя, удаляющего вещь. + * @return ResponseEntity с объектом, представляющим результат удаления вещи. + */ + @DeleteMapping("/{idItem}") + public ResponseEntity removeItem(@PathVariable Long idItem, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + log.info("Получен запрос пользователем {} на удаление вещи с id={}", idUser, idItem); + return itemClient.removeItem(idUser, idItem); + } + + /** + * Ищет вещи по тексту. + * + * @param text Текст, по которому нужно искать вещи. + * @param idUser Идентификатор пользователя, инициирующего поиск. + * @return ResponseEntity с объектом, представляющим результат поиска вещей по тексту. + */ + @GetMapping("/search") + public ResponseEntity searchItemsByText(@RequestParam String text, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + log.info("Получен запрос пользователем {} на поиск вещей по тексту: {}", idUser, text); + return itemClient.searchItems(text); + } + + /** + * Добавляет комментарий к вещи. + * + * @param userId Идентификатор пользователя, добавляющего комментарий. + * @param itemId Идентификатор вещи, к которой добавляется комментарий. + * @param commentDto Данные комментария. + * @return ResponseEntity с объектом, представляющим результат добавления комментария. + */ + @PostMapping("/{itemId}/comment") + public ResponseEntity addCommentToItem(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long itemId, + @RequestBody CommentDtoInput commentDto) { + log.info("Получен запрос пользователем {} на добавление отзыва на вещь с id={} с текстом: {}", + userId, itemId, commentDto.getText()); + return itemClient.saveComment(userId, itemId, commentDto); + } +} diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java new file mode 100644 index 0000000..3173be9 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java @@ -0,0 +1,28 @@ +package ru.practicum.shareit.item; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.validation.CreateObject; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemDtoInput { + + @NotNull(groups = {CreateObject.class}, message = "Название вещи должно быть указано.") + @NotBlank(groups = {CreateObject.class}, message = "Название вещи не может быть пустым.") + private String name; + + @NotNull(groups = {CreateObject.class}, message = "Описание вещи должно быть указано.") + @NotBlank(groups = {CreateObject.class}, message = "Описание вещи не может быть пустым.") + private String description; + + @NotNull(groups = {CreateObject.class}, message = "Доступность вещи должна быть указана.") + private Boolean available; + + private Long requestId; + +} 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..ba2c019 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java @@ -0,0 +1,41 @@ +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; + +@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 addRequest(Long userId, RequestDtoInput requestDto) { + return post("", userId, requestDto); + } + + public ResponseEntity getRequests(Long userId) { + return get("", userId); + } + + public ResponseEntity getAllRequests(Long userId) { + return get("/all", userId); + } + + public ResponseEntity getRequestById(Long requestId) { + return get("/" + requestId); + } +} 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..6f1d1b4 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java @@ -0,0 +1,73 @@ +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.*; + +@Controller +@RequestMapping(path = "/requests") +@RequiredArgsConstructor +@Slf4j +@Validated +public class RequestController { + + private final RequestClient requestClient; + + /** + * Добавляет новый запрос. + * + * @param requestorId Идентификатор пользователя, добавляющего запрос. + * @param requestDto Данные запроса. + * @return ResponseEntity с объектом, представляющим результат добавления запроса. + */ + @PostMapping + public ResponseEntity addRequest(@RequestHeader("X-Sharer-User-Id") Long requestorId, + @Valid @RequestBody RequestDtoInput requestDto) { + log.info("Получен запрос на добавление запроса с параметрами: {}", requestDto); + return requestClient.addRequest(requestorId, requestDto); + } + + /** + * Возвращает расширенный список запросов конкретного пользователя. + * + * @param requestorId Идентификатор пользователя, запросы которого нужно вернуть. + * @return ResponseEntity с объектом, представляющим список запросов пользователя. + */ + @GetMapping + public ResponseEntity getRequests(@RequestHeader("X-Sharer-User-Id") Long requestorId) { + log.info("Получен запрос на получение расширенного списка запросов пользователя с id: {}", requestorId); + return requestClient.getRequests(requestorId); + } + + /** + * Возвращает все запросы (за исключением запросов самого пользователя). + * + * @param requestorId Идентификатор пользователя. + * @return ResponseEntity с объектом, представляющим все запросы. + */ + @GetMapping("/all") + public ResponseEntity getAllRequests(@RequestHeader("X-Sharer-User-Id") Long requestorId) { + log.info("Получен запрос на получение всех запросов, кроме запросов самого пользователя с id: {}", requestorId); + return requestClient.getAllRequests(requestorId); + } + + /** + * Возвращает запрос по его идентификатору. + * + * @param requestorId Идентификатор пользователя, который делает запрос. + * @param requestId Идентификатор запроса, который нужно вернуть. + * @return ResponseEntity с объектом, представляющим запрос по его идентификатору. + */ + @GetMapping("/{requestId}") + public ResponseEntity getRequestsById(@RequestHeader("X-Sharer-User-Id") Long requestorId, + @PathVariable Long requestId) { + log.info("Получен запрос на получение запроса с id: {} по идентификатору пользователя с id: {}", + requestId, requestorId); + return requestClient.getRequestById(requestId); + } + +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java b/gateway/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java new file mode 100644 index 0000000..90542a2 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java @@ -0,0 +1,18 @@ +package ru.practicum.shareit.request; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RequestDtoInput { + + @NotNull(message = "Запрос не может быть пустым.") + @NotBlank(message = "Запрос не может быть пустым.") + private String description; + +} 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..fe6a014 --- /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; + +@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 addUser( UserDtoInput userDto) { + return post("", userDto); + } + + public ResponseEntity updateUser(Long userId, UserDtoInput userDto) { + return patch("/" + userId, userDto); + } + + public ResponseEntity getUserById(Long userId) { + return get("/" + userId); + } + + public ResponseEntity getAllUsers() { + return get(API_PREFIX); + } + + public ResponseEntity removeUser(Long userId) { + return delete("/" + userId); + } + +} 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..5c5f256 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserController.java @@ -0,0 +1,81 @@ +package ru.practicum.shareit.user; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +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.validation.CreateObject; +import ru.practicum.shareit.validation.UpdateObject; + +@Controller +@RequestMapping("/users") +@RequiredArgsConstructor +@Slf4j +@Validated +public class UserController { + private final UserClient userClient; + + /** + * Добавляет нового пользователя. + * + * @param userDto данные пользователя + * @return ResponseEntity с объектом, представляющим результат добавления пользователя + */ + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public ResponseEntity addUser(@Validated(CreateObject.class) @RequestBody UserDtoInput userDto) { + log.info("Добавление нового пользователя {}", userDto); + return userClient.addUser(userDto); + } + + /** + * Обновляет данные пользователя. + * + * @param idUser идентификатор пользователя, данные которого нужно обновить + * @param userDto новые данные пользователя + * @return ResponseEntity с объектом, представляющим результат обновления данных пользователя + */ + @PatchMapping("/{idUser}") + public ResponseEntity updateUser(@PathVariable Long idUser, + @Validated(UpdateObject.class) @RequestBody UserDtoInput userDto) { + log.info("Обновление данных пользователя с id {} на основе данных {}", idUser, userDto); + return userClient.updateUser(idUser, userDto); + } + + /** + * Получает данные пользователя по его идентификатору. + * + * @param idUser идентификатор пользователя, данные которого нужно получить + * @return ResponseEntity с объектом, представляющим результат получения данных пользователя + */ + @GetMapping("/{idUser}") + public ResponseEntity getUserById(@PathVariable Long idUser) { + log.info("Получение данных пользователя с id {}", idUser); + return userClient.getUserById(idUser); + } + + /** + * Получает всех пользователей. + * + * @return ResponseEntity с объектом, представляющим результат получения всех пользователей + */ + @GetMapping + public ResponseEntity getAllUsers() { + log.info("Получение всех пользователей"); + return userClient.getAllUsers(); + } + + /** + * Удаляет пользователя по его идентификатору. + * + * @param idUser идентификатор пользователя, которого нужно удалить + */ + @DeleteMapping("/{idUser}") + public ResponseEntity removeUser(@PathVariable Long idUser) { + log.info("Удаление пользователя с id {}", idUser); + return userClient.removeUser(idUser); + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/user/UserDtoInput.java b/gateway/src/main/java/ru/practicum/shareit/user/UserDtoInput.java new file mode 100644 index 0000000..702908a --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserDtoInput.java @@ -0,0 +1,24 @@ +package ru.practicum.shareit.user; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.validation.CreateObject; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserDtoInput { + + @NotNull(groups = {CreateObject.class}, message = "Имя должно быть указано.") + @NotBlank(groups = {CreateObject.class}, message = "Имя не может быть пустым.") + private String name; + + @NotNull(groups = {CreateObject.class}, message = "Email должен быть указан.") + @Email(groups = {CreateObject.class}, message = "Email должен быть указан корректно.") + private String email; + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/validation/CreateObject.java b/gateway/src/main/java/ru/practicum/shareit/validation/CreateObject.java new file mode 100644 index 0000000..db15c62 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/validation/CreateObject.java @@ -0,0 +1,4 @@ +package ru.practicum.shareit.validation; + +public interface CreateObject { +} diff --git a/gateway/src/main/java/ru/practicum/shareit/validation/UpdateObject.java b/gateway/src/main/java/ru/practicum/shareit/validation/UpdateObject.java new file mode 100644 index 0000000..9a3949c --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/validation/UpdateObject.java @@ -0,0 +1,4 @@ +package ru.practicum.shareit.validation; + +public interface UpdateObject { +} diff --git a/gateway/src/main/resources/application.properties b/gateway/src/main/resources/application.properties new file mode 100644 index 0000000..2ee0851 --- /dev/null +++ b/gateway/src/main/resources/application.properties @@ -0,0 +1,7 @@ +logging.level.org.springframework.web.client.RestTemplate=DEBUG +#logging.level.org.apache.http=DEBUG +#logging.level.httpclient.wire=DEBUG + +server.port=8080 + +shareit-server.url=http://localhost:9090 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 2185679..79cba37 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,7 @@ ru.practicum shareit + pom 0.0.1-SNAPSHOT ShareIt @@ -19,73 +20,29 @@ 21 - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - org.postgresql - postgresql - - - - org.projectlombok - lombok - true - - - - org.springframework.boot - spring-boot-starter-test - test - - - - org.springframework.boot - spring-boot-starter-validation - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - + + gateway + server + - - - src/main/resources - true - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - + + org.springframework.boot + spring-boot-maven-plugin + + + true + + + + org.projectlombok + lombok + + + + org.apache.maven.plugins maven-surefire-plugin @@ -232,17 +189,5 @@ - - coverage - - - - org.jacoco - jacoco-maven-plugin - - - - - - + \ No newline at end of file diff --git a/postman_for_shareit_14.json b/postman_for_shareit_14.json deleted file mode 100644 index d04cf55..0000000 --- a/postman_for_shareit_14.json +++ /dev/null @@ -1,2725 +0,0 @@ -{ - "info": { - "_postman_id": "f4a82602-e802-4363-87b5-c1814ccd49db", - "name": "Sprint 14 ShareIt (add-controllers)", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "23073145", - "_collection_link": "https://universal-shadow-295426.postman.co/workspace/My-Workspace~4200f6aa-0504-44b1-8a1d-707d0dcbd5ce/collection/13708500-f4a82602-e802-4363-87b5-c1814ccd49db?action=share&source=collection_link&creator=23073145" - }, - "item": [ - { - "name": "users", - "item": [ - { - "name": "Create user", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " pm.collectionVariables.set(\"userName\", user.name);\r", - " pm.collectionVariables.set(\"userEmail\", user.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200 or 201\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([200,201]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"{{userEmail}}\"\n}" - }, - "url": { - "raw": "localhost:8080/users", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "users" - ] - } - }, - "response": [] - }, - { - "name": "Create user without email", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user2 = rnd.getUser();\r", - " pm.collectionVariables.set(\"userName\", user2.name);\r", - " pm.collectionVariables.set(\"userEmail\", user2.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\"\n}" - }, - "url": { - "raw": "localhost:8080/users", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "users" - ] - } - }, - "response": [] - }, - { - "name": "Create 2 users with same email", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user1 = rnd.getUser();\r", - " us = await api.addUser(user1);\r", - " user2 = rnd.getUser();\r", - " user2.email = user1.email;\r", - " pm.collectionVariables.set(\"userName\", user2.name);\r", - " pm.collectionVariables.set(\"userEmail\", user2.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 409\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([409, 500]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"{{userEmail}}\"\n}" - }, - "url": { - "raw": "localhost:8080/users", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "users" - ] - } - }, - "response": [] - }, - { - "name": "Create user with invalid email", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " pm.collectionVariables.set(\"userName\", user.name);\r", - " pm.collectionVariables.set(\"userEmail\", user.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"user.com\"\n}" - }, - "url": { - "raw": "localhost:8080/users", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "users" - ] - } - }, - "response": [] - }, - { - "name": "User update", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " us = await api.addUser(user);\r", - " pm.collectionVariables.set(\"userId\", us.id);\r", - " us = rnd.getUser()\r", - " pm.collectionVariables.set(\"userName\", us.name);\r", - " pm.collectionVariables.set(\"userEmail\", us.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {\r", - " pm.response.to.be.ok;\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "pm.test(\"Test user 'id' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('id');\r", - " var id = pm.collectionVariables.get(\"userId\");\r", - " pm.expect(jsonData.id, '\"id\" must be ' + id).to.eql(Number(id));\r", - "});\r", - "pm.test(\"Test user 'email' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('email');\r", - " var email = pm.collectionVariables.get(\"userEmail\");\r", - " pm.expect(jsonData.email, '\"email\" must be ' + email).to.eql(email);\r", - "});\r", - "pm.test(\"Test user 'name' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('name');\r", - " var name = pm.collectionVariables.get(\"userName\");\r", - " pm.expect(jsonData.name, '\"name\" must be ' + name).to.eql(name);\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"{{userEmail}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/users/{{userId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } - }, - "response": [] - }, - { - "name": "User update name", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " us = await api.addUser(user);\r", - " pm.collectionVariables.set(\"userId\", us.id);\r", - " us = rnd.getUser()\r", - " pm.collectionVariables.set(\"userName\", us.name);\r", - " pm.collectionVariables.set(\"userEmail\", us.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {\r", - " pm.response.to.be.ok;\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "pm.test(\"Test user 'id' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('id');\r", - " var id = pm.collectionVariables.get(\"userId\");\r", - " pm.expect(jsonData.id, '\"id\" must be ' + id).to.eql(Number(id));\r", - "});\r", - "pm.test(\"Test user 'name' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('name');\r", - " var name = pm.collectionVariables.get(\"userName\");\r", - " pm.expect(jsonData.name, '\"name\" must be ' + name).to.eql(name);\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/users/{{userId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } - }, - "response": [] - }, - { - "name": "User update email", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " us = await api.addUser(user);\r", - " pm.collectionVariables.set(\"userId\", us.id);\r", - " us = rnd.getUser()\r", - " pm.collectionVariables.set(\"userName\", us.name);\r", - " pm.collectionVariables.set(\"userEmail\", us.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {\r", - " pm.response.to.be.ok;\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "pm.test(\"Test user 'id' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('id');\r", - " var id = pm.collectionVariables.get(\"userId\");\r", - " pm.expect(jsonData.id, '\"id\" must be ' + id).to.eql(Number(id));\r", - "});\r", - "pm.test(\"Test user 'email' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('email');\r", - " var email = pm.collectionVariables.get(\"userEmail\");\r", - " pm.expect(jsonData.email, '\"email\" must be ' + email).to.eql(email);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"email\": \"{{userEmail}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/users/{{userId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } - }, - "response": [] - }, - { - "name": "User update with existing email", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " us = await api.addUser(user);\r", - " user2 = rnd.getUser();\r", - " us2 = await api.addUser(user2)\r", - " pm.collectionVariables.set(\"userId\", us2.id);\r", - " usa = rnd.getUser()\r", - " pm.collectionVariables.set(\"userName\", usa.name);\r", - " pm.collectionVariables.set(\"userEmail\", user.email);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 409\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([409, 500]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{userName}}\",\n \"email\": \"{{userEmail}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/users/{{userId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } - }, - "response": [] - }, - { - "name": "Get user", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " us = await api.addUser(user);\r", - " pm.collectionVariables.set(\"userId\", us.id);\r", - " pm.collectionVariables.set(\"userName\", us.name);\r", - " pm.collectionVariables.set(\"userEmail\", us.email);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {\r", - " pm.response.to.be.ok;\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "pm.test(\"Test user 'id' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('id');\r", - " var id = pm.collectionVariables.get(\"userId\");\r", - " pm.expect(jsonData.id, '\"id\" must be ' + id).to.eql(Number(id));\r", - "});\r", - "pm.test(\"Test user 'email' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('email');\r", - " var email = pm.collectionVariables.get(\"userEmail\");\r", - " pm.expect(jsonData.email, '\"email\" must be ' + email).to.eql(email);\r", - "});\r", - "pm.test(\"Test user 'name' field\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('name');\r", - " var name = pm.collectionVariables.get(\"userName\");\r", - " pm.expect(jsonData.name, '\"name\" must be ' + name).to.eql(name);\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - } - ], - "body": { - "mode": "raw", - "raw": "" - }, - "url": { - "raw": "{{baseUrl}}/users/{{userId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } - }, - "response": [] - }, - { - "name": "User delete", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([200,204]);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = rnd.getUser();\r", - " us = await api.addUser(user);\r", - " pm.collectionVariables.set(\"userId\", us.id);\r", - " pm.collectionVariables.set(\"userName\", us.name);\r", - " pm.collectionVariables.set(\"userEmail\", us.email);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "DELETE", - "header": [ - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/users/{{userId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "users", - "{{userId}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "items", - "item": [ - { - "name": "Item create", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200 or 201\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([200,201]);\r", - "});\r", - "pm.test(\"Response have body\", function () {\r", - " pm.response.to.be.withBody;\r", - " pm.response.to.be.json;\r", - "});\r", - "var item = pm.collectionVariables.get(\"item\");\r", - "\r", - "pm.test(\"Response data equal to request\", function () {\r", - " var jsonData = pm.response.json();\r", - " pm.expect(jsonData).to.have.property('id');\r", - " pm.expect(jsonData).to.have.property('name');\r", - " pm.expect(jsonData).to.have.property('description');\r", - " pm.expect(jsonData).to.have.property('available');\r", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);\r", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);\r", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "localhost:8080/items", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item create without X-Sharer-User-Id", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400 or 500\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([500, 400]);\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "disabled": true - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "localhost:8080/items", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item create with non-existent user", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id + '1');\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 404\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([404]);\r", - "});\r", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "localhost:8080/items", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item create without available field", - "event": [ - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", - "});" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "Content-Type", - "value": "application/json" - }, - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\"\n}" - }, - "url": { - "raw": "localhost:8080/items", - "host": [ - "localhost" - ], - "port": "8080", - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item create with empty name field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"\",\n \"description\": \"Аккумуляторная отвертка\",\n \"available\": true\n}" - }, - "url": { - "raw": "{{baseUrl}}/items", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item create with empty description field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"Отвертка\",\n \"available\": true\n}" - }, - "url": { - "raw": "{{baseUrl}}/items", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item update", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update without X-Sharer-User-Id", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 500\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([500, 400]);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text", - "disabled": true - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update with other user", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 404\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([404, 403]);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id + 1);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update available field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update description field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"description\": \"{{itemDescription}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update name field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item get", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = await api.addItem(rnd.getItem(), user.id)\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemId\", item.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Get all items from user", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test list item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 2').to.eql(2);", - "});", - "", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " await api.addItem(rnd.getItem(), user.id)\r", - " await api.addItem(rnd.getItem(), user.id)\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/items", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item search", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test list item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 1').to.eql(1);", - " pm.expect(jsonData[0].name.toUpperCase(), 'Name should include ' + pm.collectionVariables.get(\"searchString\")).to.eql(pm.collectionVariables.get(\"searchString\"))", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " it = rnd.getItem()\r", - " it.available = true\r", - " item = await api.addItem(it, user.id)\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemId\", item.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"searchString\", item.name.toUpperCase());\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/items/search?text={{searchString}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "search" - ], - "query": [ - { - "key": "text", - "value": "{{searchString}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Item search unavailable", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test list item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 0').to.eql(0);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " it = rnd.getItem()\r", - " it.available = false\r", - " item = await api.addItem(it, user.id)\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemId\", item.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"searchString\", item.name.toUpperCase());\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/items/search?text={{searchString}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "search" - ], - "query": [ - { - "key": "text", - "value": "{{searchString}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Item search empty", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test search item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 0').to.eql(0);", - "});" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/items/search?text=", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "search" - ], - "query": [ - { - "key": "text", - "value": "" - } - ] - } - }, - "response": [] - } - ] - } - ], - "event": [ - { - "listen": "prerequest", - "script": { - "type": "text/javascript", - "packages": {}, - "exec": [ - "API = class {\r", - " constructor(postman, verbose = false, baseUrl = \"http://localhost:8080\") {\r", - " this.baseUrl = baseUrl;\r", - " this.pm = postman;\r", - " this._verbose = verbose;\r", - " }\r", - "\r", - " async addUser(user, id=0, verbose=null) {\r", - " return this.post(\"/users\", user, id, \"Ошибка при добавлении нового пользователя: \", verbose);\r", - " }\r", - "\r", - " async addItem(item, id=0, verbose=null) {\r", - " return this.post(\"/items\", item, id, \"Ошибка при добавлении новой вещи: \", verbose);\r", - " }\r", - "\r", - " async addRequest(request, id=0, verbose=null) {\r", - " return this.post(\"/requests\", request, id, \"Ошибка при добавлении нового запроса: \", verbose);\r", - " }\r", - " \r", - " async post(path, body, id=0, errorText = \"Ошибка при выполнении post-запроса: \", verbose=null) {\r", - " return this.sendRequest(\"POST\", path, body, id, errorText, verbose);\r", - " }\r", - "\r", - " async patch(path, body = null, id=0, errorText = \"Ошибка при выполнении patch-запроса: \", verbose=null) {\r", - " return this.sendRequest(\"PATCH\", path, body, id, errorText, verbose);\r", - " }\r", - "\r", - " async get(path, body = null, id=0, errorText = \"Ошибка при выполнении get-запроса: \", verbose=null) {\r", - " return this.sendRequest(\"GET\", path, body, id, errorText, verbose);\r", - " }\r", - "\r", - " async put(path, body = null, id=0, errorText = \"Ошибка при выполнении put-запроса: \", verbose=null) {\r", - " return this.sendRequest(\"PUT\", path, body, id, errorText, verbose);\r", - " }\r", - "\r", - " async delete(path, body = null, id=0, errorText = \"Ошибка при выполнении delte-запроса: \", verbose=null) {\r", - " return this.sendRequest(\"DELETE\", path, body, id, errorText, verbose);\r", - " }\r", - "\r", - " async sendRequest(method, path, body=null, id=0, errorText = \"Ошибка при выполнении запроса: \", verbose=null) {\r", - " return new Promise((resolve, reject) => {\r", - " verbose = verbose == null ? this._verbose : verbose;\r", - " var req = {};\r", - " if (id == 0){\r", - " req = {\r", - " url: this.baseUrl + path,\r", - " method: method,\r", - " body: body == null ? \"\" : JSON.stringify(body),\r", - " header: { \"Content-Type\": \"application/json\"},\r", - " };\r", - " }else{\r", - " req = {\r", - " url: this.baseUrl + path,\r", - " method: method,\r", - " body: body == null ? \"\" : JSON.stringify(body),\r", - " header: [{\r", - " \"key\": \"X-Sharer-User-Id\",\r", - " \"value\": id,\r", - " \"type\": \"text\",\r", - " },\r", - " {\r", - " \"key\": \"Content-Type\",\r", - " \"name\": \"Content-Type\",\r", - " \"value\": \"application/json\",\r", - " \"type\": \"text\"\r", - " }]\r", - " };\r", - " }\r", - " if(verbose) {\r", - " console.log(\"Отправляю запрос: \", req);\r", - " }\r", - "\r", - " try {\r", - " this.pm.sendRequest(req, (error, response) => {\r", - " if(error || (response.code >= 400 && response.code <= 599)) {\r", - " let err = error ? error : JSON.stringify(response.json());\r", - " console.error(\"При выполнении запроса к серверу возникла ошибка.\\n\", err,\r", - " \"\\nДля отладки проблемы повторите такой же запрос к вашей программе \" + \r", - " \"на локальном компьютере. Данные запроса:\\n\", JSON.stringify(request));\r", - "\r", - " reject(new Error(errorText + err));\r", - " }\r", - " if(verbose) {\r", - " console.log(\"Результат обработки запроса: код состояния - \", response.code, \", тело: \", response.json());\r", - " }\r", - " if (response.stream.length === 0){\r", - " resolve(null);\r", - " }else{\r", - " resolve(response.json());\r", - " }\r", - " });\r", - " \r", - " } catch(err) {\r", - " if(verbose) {\r", - " console.error(errorText, err);\r", - " }\r", - " return Promise.reject(err);\r", - " }\r", - " });\r", - " }\r", - "};\r", - "\r", - "RandomUtils = class {\r", - " constructor() {}\r", - "\r", - " getUser() {\r", - " return {\r", - " name: pm.variables.replaceIn('{{$randomFullName}}'),\r", - " email: pm.variables.replaceIn('{{$randomEmail}}'),\r", - " };\r", - " }\r", - "\r", - " getRequest() {\r", - " return {\r", - " description: this.getWord(50)\r", - " };\r", - " }\r", - "\r", - " getItem() {\r", - " return {\r", - " name: this.getWord(10),\r", - " description: this.getWord(50),\r", - " available: pm.variables.replaceIn('{{$randomBoolean}}')\t\r", - " };\r", - " }\r", - "\r", - " getItemForRequest(id) {\r", - " return {\r", - " name: this.getWord(10),\r", - " description: this.getWord(50),\r", - " available: pm.variables.replaceIn('{{$randomBoolean}}'),\r", - " requestId: id\r", - " };\r", - " }\r", - "\r", - " getFilm(director=null) {\r", - " let date = new Date(new Date(1960, 0, 1).getTime() + Math.random() * (new Date(2010, 0, 1).getTime() - new Date(1960, 0, 1).getTime()));\r", - " var toReturn = {\r", - " name: this.getWord(15),\r", - " description: this.getWord(50),\r", - " releaseDate: date.toISOString().slice(0,10),\r", - " duration: Math.floor(Math.random() * (180 - 60 + 1) + 60),\r", - " mpa: { id: Math.floor(Math.random() * (5 - 1 + 1) + 1)},\r", - " genres: [{ id: Math.floor(Math.random() * (6 - 1 + 1) + 1)}]\r", - " };\r", - " if (director!==null)\r", - " toReturn.directors = [{ id: director.id}];\r", - " return toReturn;\r", - " }\r", - "\r", - "\r", - " getWord(length = 1) {\r", - " let result = '';\r", - " const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\r", - " const charactersLength = characters.length;\r", - " let counter = 0;\r", - " while (counter < length) {\r", - " result += characters.charAt(Math.floor(Math.random() * charactersLength));\r", - " counter += 1;\r", - " }\r", - " return result;\r", - " }\r", - "\r", - " getName(length = 1) {\r", - " let result = '';\r", - " const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';\r", - " const charactersLength = characters.length;\r", - " let counter = 0;\r", - " while (counter < length) {\r", - " result += characters.charAt(Math.floor(Math.random() * charactersLength));\r", - " counter += 1;\r", - " }\r", - " return result;\r", - " }\r", - "\r", - "}" - ] - } - }, - { - "listen": "test", - "script": { - "type": "text/javascript", - "packages": {}, - "exec": [ - "" - ] - } - } - ], - "variable": [ - { - "key": "baseUrl", - "value": "http://localhost:8080" - }, - { - "key": "userName", - "value": "" - }, - { - "key": "userEmail", - "value": "" - }, - { - "key": "userId", - "value": "1", - "type": "string" - }, - { - "key": "item", - "value": "" - }, - { - "key": "itemName", - "value": "" - }, - { - "key": "itemAvailable", - "value": "" - }, - { - "key": "itemDescription", - "value": "" - }, - { - "key": "itemId", - "value": "" - }, - { - "key": "searchString", - "value": "" - } - ] -} diff --git a/postman_for_shareit_15.json b/postman_for_shareit_16.json similarity index 81% rename from postman_for_shareit_15.json rename to postman_for_shareit_16.json index 26845fe..6e87b6b 100644 --- a/postman_for_shareit_15.json +++ b/postman_for_shareit_16.json @@ -1,10 +1,10 @@ { "info": { - "_postman_id": "069d256e-037e-4bd9-a5de-be6910726adc", - "name": "Sprint 15 ShareIt (add-bookings)", + "_postman_id": "7fc26681-0775-4307-885f-fd53560f0317", + "name": "Sprint 16 ShareIt (add-item-requests-and-gateway)", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "23073145", - "_collection_link": "https://universal-shadow-295426.postman.co/workspace/My-Workspace~4200f6aa-0504-44b1-8a1d-707d0dcbd5ce/collection/13708500-069d256e-037e-4bd9-a5de-be6910726adc?action=share&source=collection_link&creator=23073145" + "_collection_link": "https://universal-shadow-295426.postman.co/workspace/My-Workspace~4200f6aa-0504-44b1-8a1d-707d0dcbd5ce/collection/13708500-7fc26681-0775-4307-885f-fd53560f0317?action=share&source=collection_link&creator=23073145" }, "item": [ { @@ -921,10 +921,10 @@ ] }, { - "name": "items", + "name": "Item", "item": [ { - "name": "Item create", + "name": "Create Item", "event": [ { "listen": "prerequest", @@ -1027,7 +1027,7 @@ "response": [] }, { - "name": "Item create without X-Sharer-User-Id", + "name": "Create Item on request", "event": [ { "listen": "prerequest", @@ -1038,13 +1038,18 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", + " user1 = await api.addUser(rnd.getUser());\r", + " var request = await api.addRequest(rnd.getRequest(), user1.id);\r", + " pm.collectionVariables.set(\"requestId\", request.id);\r", + " user2 = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user2.id);\r", + "\r", " item = rnd.getItem();\r", " pm.collectionVariables.set(\"item\", item);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", + "\r", "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", @@ -1075,10 +1080,25 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Status code is 400 or 500\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([500, 400]);\r", + "pm.test(\"Status code is 200 or 201\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([200,201]);\r", "});\r", - "" + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "var item = pm.collectionVariables.get(\"item\");\r", + "\r", + "pm.test(\"Response data equal to request\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('id');\r", + " pm.expect(jsonData).to.have.property('name');\r", + " pm.expect(jsonData).to.have.property('description');\r", + " pm.expect(jsonData).to.have.property('available');\r", + " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);\r", + " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);\r", + " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());\r", + "});" ], "type": "text/javascript", "packages": {} @@ -1094,13 +1114,12 @@ }, { "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "disabled": true + "value": "{{userId}}" } ], "body": { "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}},\n \"requestId\": {{requestId}}\n}" }, "url": { "raw": "localhost:8080/items", @@ -1116,7 +1135,7 @@ "response": [] }, { - "name": "Item create with non-existent user", + "name": "Create Item without name on request", "event": [ { "listen": "prerequest", @@ -1127,13 +1146,18 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", + " user1 = await api.addUser(rnd.getUser());\r", + " var request = await api.addRequest(rnd.getRequest(), user1.id);\r", + " pm.collectionVariables.set(\"requestId\", request.id);\r", + " user2 = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user2.id);\r", + "\r", " item = rnd.getItem();\r", " pm.collectionVariables.set(\"item\", item);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id + '1');\r", + "\r", "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", @@ -1164,10 +1188,17 @@ "listen": "test", "script": { "exec": [ - "pm.test(\"Status code is 404\", function () {\r", - " pm.expect(pm.response.code).to.be.oneOf([404]);\r", + "pm.test(\"Status code is 400\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", "});\r", - "" + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "pm.test(\"Response data have error\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('error');\r", + "});" ], "type": "text/javascript", "packages": {} @@ -1188,7 +1219,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" + "raw": "{\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}},\n \"requestId\": {{requestId}}\n}" }, "url": { "raw": "localhost:8080/items", @@ -1204,7 +1235,7 @@ "response": [] }, { - "name": "Item create without available field", + "name": "Create Item without description on request", "event": [ { "listen": "prerequest", @@ -1215,13 +1246,18 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", + " user1 = await api.addUser(rnd.getUser());\r", + " var request = await api.addRequest(rnd.getRequest(), user1.id);\r", + " pm.collectionVariables.set(\"requestId\", request.id);\r", + " user2 = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user2.id);\r", + "\r", " item = rnd.getItem();\r", " pm.collectionVariables.set(\"item\", item);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", + "\r", "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", @@ -1254,6 +1290,14 @@ "exec": [ "pm.test(\"Status code is 400\", function () {\r", " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "pm.test(\"Response data have error\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('error');\r", "});" ], "type": "text/javascript", @@ -1275,7 +1319,7 @@ ], "body": { "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\"\n}" + "raw": "{\n \"name\": \"{{itemName}}\",\n \"available\": {{itemAvailable}},\n \"requestId\": {{requestId}}\n}" }, "url": { "raw": "localhost:8080/items", @@ -1291,140 +1335,8 @@ "response": [] }, { - "name": "Item create with empty name field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"\",\n \"description\": \"Аккумуляторная отвертка\",\n \"available\": true\n}" - }, - "url": { - "raw": "{{baseUrl}}/items", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item create with empty description field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 400\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([400, 500]);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"Отвертка\",\n \"available\": true\n}" - }, - "url": { - "raw": "{{baseUrl}}/items", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items" - ] - } - }, - "response": [] - }, - { - "name": "Item update", + "name": "Create Item without available on request", "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, { "listen": "prerequest", "script": { @@ -1434,17 +1346,19 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", + " user1 = await api.addUser(rnd.getUser());\r", + " var request = await api.addRequest(rnd.getRequest(), user1.id);\r", + " pm.collectionVariables.set(\"requestId\", request.id);\r", + " user2 = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user2.id);\r", + "\r", " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", + " pm.collectionVariables.set(\"item\", item);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", " pm.collectionVariables.set(\"itemDescription\", item.description);\r", "\r", + "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -1469,694 +1383,65 @@ "type": "text/javascript", "packages": {} } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update without X-Sharer-User-Id", - "event": [ { "listen": "test", "script": { "exec": [ - "pm.test(\"Status code is 500\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([500, 400]);", + "pm.test(\"Status code is 400\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([400, 500]);\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "pm.test(\"Response data have error\", function () {\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('error');\r", "});" ], "type": "text/javascript", "packages": {} } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text", - "disabled": true - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update with other user", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 404\", function () {", - " pm.expect(pm.response.code).to.be.oneOf([404, 403]);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id + 1);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update available field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"available\": {{itemAvailable}}\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update description field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"description\": \"{{itemDescription}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item update name field", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = rnd.getItem();\r", - " var it = await api.addItem(item, user.id)\r", - " item = rnd.getItem();\r", - " pm.collectionVariables.set(\"item\", item); \r", - " pm.collectionVariables.set(\"itemId\", it.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - "\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } } ], "request": { - "method": "PATCH", + "method": "POST", "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, { "key": "Content-Type", - "value": "application/json", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "body": { - "mode": "raw", - "raw": "{\n \"name\": \"{{itemName}}\"\n}" - }, - "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "{{itemId}}" - ] - } - }, - "response": [] - }, - { - "name": "Item get", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Response have body\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - "});", - "var item = pm.collectionVariables.get(\"item\");", - "", - "pm.test(\"Response data equal to request\", function () {", - " var jsonData = pm.response.json();", - " pm.expect(jsonData).to.have.property('id');", - " pm.expect(jsonData).to.have.property('name');", - " pm.expect(jsonData).to.have.property('description');", - " pm.expect(jsonData).to.have.property('available');", - " pm.expect(jsonData.name, `\"name\" must be ${item.name}`).to.eql(item.name);", - " pm.expect(jsonData.description, `\"description\" must be ${item.description}`).to.eql(item.description);", - " pm.expect(jsonData.available.toString(), `\"available\" must be ${item.available}`).to.eql(item.available.toString());", - "});" - ], - "type": "text/javascript", - "packages": {} - } - }, - { - "listen": "prerequest", - "script": { - "exec": [ - "const main = async () => {\r", - " const api = new API(pm);\r", - " const rnd = new RandomUtils();\r", - "\r", - " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " item = await api.addItem(rnd.getItem(), user.id)\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemId\", item.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", - " } catch(err) {\r", - " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", - " }\r", - "\r", - "};\r", - "\r", - "const interval = setInterval(() => {}, 1000);\r", - "\r", - "setTimeout(async () => \r", - " {\r", - " try {\r", - " await main();\r", - " } catch (e) {\r", - " console.error(e);\r", - " } finally {\r", - " clearInterval(interval);\r", - " }\r", - " }, \r", - " 100 \r", - ");" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" + "value": "application/json" }, - { - "key": "Accept", - "value": "*/*", - "type": "text" + { + "key": "X-Sharer-User-Id", + "value": "{{userId}}" } ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"{{itemName}}\",\n \"description\": \"{{itemDescription}}\",\n \"requestId\": {{requestId}}\n}" + }, "url": { - "raw": "{{baseUrl}}/items/{{itemId}}", + "raw": "localhost:8080/items", "host": [ - "{{baseUrl}}" + "localhost" ], + "port": "8080", "path": [ - "items", - "{{itemId}}" + "items" ] } }, "response": [] - }, + } + ] + }, + { + "name": "requests", + "item": [ { - "name": "Get all items from user", + "name": "Create request", "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test list item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 2').to.eql(2);", - "});", - "", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, { "listen": "prerequest", "script": { @@ -2166,10 +1451,11 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", + " request1 = rnd.getRequest();\r", + " pm.collectionVariables.set(\"requestDescription\", request1.description);\r", " user = await api.addUser(rnd.getUser());\r", " pm.collectionVariables.set(\"userId\", user.id);\r", - " await api.addItem(rnd.getItem(), user.id)\r", - " await api.addItem(rnd.getItem(), user.id)\r", + "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -2194,57 +1480,65 @@ "type": "text/javascript", "packages": {} } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200 or 201\", function () {\r", + " pm.expect(pm.response.code).to.be.oneOf([200,201]);\r", + "});\r", + "pm.test(\"Response have body\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + "});\r", + "\r", + "pm.test(\"Response data equal to request\", function () {\r", + " var description = pm.collectionVariables.get(\"requestDescription\");\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('id');\r", + " pm.expect(jsonData).to.have.property('description');\r", + " pm.expect(jsonData).to.have.property('created');\r", + " pm.expect(jsonData.description, `\"description\" must be ${description}`).to.eql(description);\r", + "});" + ], + "type": "text/javascript", + "packages": {} + } } ], "request": { - "method": "GET", + "method": "POST", "header": [ { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" }, { - "key": "Accept", - "value": "*/*", - "type": "text" + "key": "X-Sharer-User-Id", + "value": "{{userId}}" } ], + "body": { + "mode": "raw", + "raw": "{ \n \"description\": \"{{requestDescription}}\"\n}" + }, "url": { - "raw": "{{baseUrl}}/items", + "raw": "localhost:8080/requests", "host": [ - "{{baseUrl}}" + "localhost" ], + "port": "8080", "path": [ - "items" + "requests" ] } }, "response": [] }, { - "name": "Item search", + "name": "Get user requests", "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test list item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 1').to.eql(1);", - " pm.expect(jsonData[0].name.toUpperCase(), 'Name should include ' + pm.collectionVariables.get(\"searchString\")).to.eql(pm.collectionVariables.get(\"searchString\"))", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, { "listen": "prerequest", "script": { @@ -2256,15 +1550,9 @@ " try {\r", " user = await api.addUser(rnd.getUser());\r", " pm.collectionVariables.set(\"userId\", user.id);\r", - " it = rnd.getItem()\r", - " it.available = true\r", - " item = await api.addItem(it, user.id)\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemId\", item.id);\r", - " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"searchString\", item.name.toUpperCase());\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", + " request1 = await api.addRequest(rnd.getRequest(), user.id);\r", + " request2 = await api.addRequest(rnd.getRequest(), user.id);\r", + "\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -2289,63 +1577,54 @@ "type": "text/javascript", "packages": {} } + }, + { + "listen": "test", + "script": { + "exec": [ + "pm.test(\"Status code is 200\", function () {\r", + " pm.response.to.be.ok;\r", + "});\r", + "pm.test(\"User requests amount\", function () {\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData.length, 'List length must be 2').to.eql(2);\r", + "});" + ], + "type": "text/javascript", + "packages": {} + } } ], "request": { "method": "GET", "header": [ { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" }, { - "key": "Accept", - "value": "*/*", - "type": "text" + "key": "X-Sharer-User-Id", + "value": "{{userId}}" } ], "url": { - "raw": "{{baseUrl}}/items/search?text={{searchString}}", + "raw": "localhost:8080/requests", "host": [ - "{{baseUrl}}" + "localhost" ], + "port": "8080", "path": [ - "items", - "search" - ], - "query": [ - { - "key": "text", - "value": "{{searchString}}" - } + "requests" ] } }, "response": [] }, { - "name": "Item search unavailable", + "name": "Get user request by id", "event": [ - { - "listen": "test", - "script": { - "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test list item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 0').to.eql(0);", - "});", - "" - ], - "type": "text/javascript", - "packages": {} - } - }, { "listen": "prerequest", "script": { @@ -2355,17 +1634,13 @@ " const rnd = new RandomUtils();\r", "\r", " try {\r", - " user = await api.addUser(rnd.getUser());\r", - " pm.collectionVariables.set(\"userId\", user.id);\r", - " it = rnd.getItem()\r", - " it.available = false\r", - " item = await api.addItem(it, user.id)\r", - " pm.collectionVariables.set(\"item\", item);\r", - " pm.collectionVariables.set(\"itemId\", item.id);\r", + " user1 = await api.addUser(rnd.getUser());\r", + " user2 = await api.addUser(rnd.getUser());\r", + " pm.collectionVariables.set(\"userId\", user2.id);\r", + " req = await api.addRequest(rnd.getRequest(), user1.id);\r", + " pm.collectionVariables.set(\"requestId\", req.id);\r", + " item = await api.addItem(rnd.getItemForRequest(req.id), user2.id);\r", " pm.collectionVariables.set(\"itemName\", item.name);\r", - " pm.collectionVariables.set(\"searchString\", item.name.toUpperCase());\r", - " pm.collectionVariables.set(\"itemAvailable\", item.available);\r", - " pm.collectionVariables.set(\"itemDescription\", item.description);\r", " } catch(err) {\r", " console.error(\"Ошибка при подготовке тестовых данных.\", err);\r", " }\r", @@ -2390,59 +1665,28 @@ "type": "text/javascript", "packages": {} } - } - ], - "request": { - "method": "GET", - "header": [ - { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" - }, - { - "key": "Accept", - "value": "*/*", - "type": "text" - } - ], - "url": { - "raw": "{{baseUrl}}/items/search?text={{searchString}}", - "host": [ - "{{baseUrl}}" - ], - "path": [ - "items", - "search" - ], - "query": [ - { - "key": "text", - "value": "{{searchString}}" - } - ] - } - }, - "response": [] - }, - { - "name": "Item search empty", - "event": [ + }, { "listen": "test", "script": { "exec": [ - "pm.test(\"Status code is 200\", function () {", - " pm.response.to.be.ok;", - "});", - "pm.test(\"Test search item response\", function () {", - " pm.response.to.be.withBody;", - " pm.response.to.be.json;", - " var jsonData = pm.response.json();", - " pm.expect(jsonData.length, 'List length must be 0').to.eql(0);", + "pm.test(\"Status code is 200\", function () {\r", + " pm.response.to.be.ok;\r", + "});\r", + "pm.test(\"User requests amount\", function () {\r", + " var name = pm.collectionVariables.get(\"itemName\");\r", + " pm.response.to.be.withBody;\r", + " pm.response.to.be.json;\r", + " var jsonData = pm.response.json();\r", + " pm.expect(jsonData).to.have.property('id');\r", + " pm.expect(jsonData).to.have.property('description');\r", + " pm.expect(jsonData).to.have.property('created');\r", + " pm.expect(jsonData).to.have.property('items');\r", + " pm.expect(jsonData.items[0].name, `\"item name\" must be ${name}`).to.eql(name);\r", "});" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -2450,30 +1694,23 @@ "method": "GET", "header": [ { - "key": "X-Sharer-User-Id", - "value": "{{userId}}", - "type": "text" + "key": "Content-Type", + "value": "application/json" }, { - "key": "Accept", - "value": "*/*", - "type": "text" + "key": "X-Sharer-User-Id", + "value": "{{userId}}" } ], "url": { - "raw": "{{baseUrl}}/items/search?text=", + "raw": "localhost:8080/requests/{{requestId}}", "host": [ - "{{baseUrl}}" + "localhost" ], + "port": "8080", "path": [ - "items", - "search" - ], - "query": [ - { - "key": "text", - "value": "" - } + "requests", + "{{requestId}}" ] } }, @@ -4739,10 +3976,6 @@ } ], "variable": [ - { - "key": "baseUrl", - "value": "http://localhost:8080" - }, { "key": "userName", "value": "" @@ -4753,11 +3986,7 @@ }, { "key": "userId", - "value": "" - }, - { - "key": "item", - "value": "" + "value": "1" }, { "key": "itemName", @@ -4772,13 +4001,21 @@ "value": "" }, { - "key": "itemId", + "key": "item", + "value": "" + }, + { + "key": "requestId", "value": "" }, { - "key": "searchString", + "key": "requestDescription", "value": "" }, + { + "key": "baseUrl", + "value": "localhost:8080" + }, { "key": "start", "value": "" @@ -4787,6 +4024,10 @@ "key": "end", "value": "" }, + { + "key": "itemId", + "value": "" + }, { "key": "user1", "value": "" @@ -4797,7 +4038,7 @@ }, { "key": "bookingId", - "value": "" + "value": "1" }, { "key": "booking", diff --git a/server/Dockerfile b/server/Dockerfile new file mode 100644 index 0000000..0ff1817 --- /dev/null +++ b/server/Dockerfile @@ -0,0 +1,5 @@ +FROM eclipse-temurin:21-jre-jammy +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/server/pom.xml b/server/pom.xml new file mode 100644 index 0000000..566db3e --- /dev/null +++ b/server/pom.xml @@ -0,0 +1,86 @@ + + + 4.0.0 + + ru.practicum + shareit + 0.0.1-SNAPSHOT + + + shareit-server + 0.0.1-SNAPSHOT + + ShareIt Server + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.postgresql + postgresql + runtime + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + coverage + + + + org.jacoco + jacoco-maven-plugin + + + + + + + diff --git a/server/src/main/java/ru/practicum/shareit/ShareItServer.java b/server/src/main/java/ru/practicum/shareit/ShareItServer.java new file mode 100644 index 0000000..303541d --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/ShareItServer.java @@ -0,0 +1,13 @@ +package ru.practicum.shareit; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ShareItServer { + + public static void main(String[] args) { + SpringApplication.run(ShareItServer.class, args); + } + +} diff --git a/server/src/main/java/ru/practicum/shareit/booking/Booking.java b/server/src/main/java/ru/practicum/shareit/booking/Booking.java new file mode 100644 index 0000000..ccfe012 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/Booking.java @@ -0,0 +1,41 @@ +package ru.practicum.shareit.booking; + +import jakarta.persistence.*; +import lombok.*; +import ru.practicum.shareit.item.Item; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; + +@Getter +@Setter +@ToString +@RequiredArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(of = {"id"}) +@Entity +@Table(name = "bookings") +public class Booking { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(name = "start_date", nullable = false) + private LocalDateTime start; + + @Column(name = "end_date", nullable = false) + private LocalDateTime end; + + @ManyToOne + @JoinColumn(name = "item_id", nullable = false) + private Item item; + + @ManyToOne + @JoinColumn(name = "booker_id", nullable = false) + private User booker; + + @Column(length = 10) + @Enumerated(EnumType.STRING) + private BookingStatus status; + +} diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingController.java b/server/src/main/java/ru/practicum/shareit/booking/BookingController.java new file mode 100644 index 0000000..fd62891 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -0,0 +1,85 @@ +package ru.practicum.shareit.booking; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping(path = "/bookings") +@RequiredArgsConstructor +public class BookingController { + + private final BookingService bookingService; + + /** + * • Добавление нового запроса на бронирование. Запрос может быть создан любым пользователем, + * а затем подтверждён владельцем вещи. Эндпоинт — POST /bookings. + * После создания запрос находится в статусе WAITING — «ожидает подтверждения». + */ + @PostMapping + public BookingDtoOutput addBooking(@RequestHeader("X-Sharer-User-Id") Long bookerId, + @RequestBody BookingDtoInput bookingDto) { + return BookingMapper.toBookingDtoOutput(bookingService.addBooking(bookerId, bookingDto)); + } + + /** + * • Подтверждение или отклонение запроса на бронирование. Может быть выполнено только владельцем вещи. + * Затем статус бронирования становится либо APPROVED, либо REJECTED. + * Эндпоинт — PATCH /bookings/{bookingId}?approved={approved}, параметр approved может принимать + * значения true или false. + * + * @param ownerId ID владельца вещи. + * @param bookingId ID брони. + * @param approved True - подтверждено, False - отклонено. + * @return Обновленная бронь. + */ + @PatchMapping("/{bookingId}") + public BookingDtoOutput updateByOwner(@RequestHeader("X-Sharer-User-Id") Long ownerId, + @PathVariable Long bookingId, + @RequestParam Boolean approved) { + return BookingMapper.toBookingDtoOutput(bookingService.updateBooking(ownerId, bookingId, approved)); + } + + /** + * • Получение данных о конкретном бронировании (включая его статус). + * Может быть выполнено либо автором бронирования, либо владельцем вещи, + * к которой относится бронирование. + * Эндпоинт — GET /bookings/{bookingId}. + */ + @GetMapping("/{bookingId}") + public BookingDtoOutput getWithStatusById(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long bookingId) { + return BookingMapper.toBookingDtoOutput(bookingService.getWithStatusById(userId, bookingId)); + } + + /** + * • Получение списка всех бронирований текущего пользователя. + * Эндпоинт — GET /bookings?state={state}. + * Параметр state необязательный и по умолчанию равен ALL (англ. «все»). + * Также он может принимать значения CURRENT (англ. «текущие»), PAST (англ. «завершённые»), + * FUTURE (англ. «будущие»), WAITING (англ. «ожидающие подтверждения»), REJECTED (англ. «отклонённые»). + * Бронирования должны возвращаться отсортированными по дате от более новых к более старым. + */ + @GetMapping + public List getByUserId(@RequestHeader("X-Sharer-User-Id") Long userId, + @RequestParam(value = "state", + defaultValue = "ALL", required = false) String state) { + return bookingService.getByUserId(userId, state) + .stream().map(BookingMapper::toBookingDtoOutput).toList(); + } + + /** + * • Получение списка бронирований для всех вещей текущего пользователя. + * Эндпоинт — GET /bookings/owner?state={state}. + * Этот запрос имеет смысл для владельца хотя бы одной вещи. + * Работа параметра state аналогична его работе в предыдущем сценарии. + */ + @GetMapping("/owner") + public List getByOwnerId(@RequestHeader("X-Sharer-User-Id") Long userId, + @RequestParam(value = "state", defaultValue = "ALL", + required = false) String state) { + return bookingService.getByOwnerId(userId, state) + .stream().map(BookingMapper::toBookingDtoOutput).toList(); + } +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingDto.java b/server/src/main/java/ru/practicum/shareit/booking/BookingDto.java new file mode 100644 index 0000000..07632f8 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingDto.java @@ -0,0 +1,28 @@ +package ru.practicum.shareit.booking; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.item.Item; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BookingDto { + + private Long id; + + private LocalDateTime start; + + private LocalDateTime end; + + private Item item; + + private User booker; + + private BookingStatus status; + +} diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java b/server/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java new file mode 100644 index 0000000..382f6a2 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java @@ -0,0 +1,20 @@ +package ru.practicum.shareit.booking; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BookingDtoInput { + + private Long itemId; + + private LocalDateTime start; + + private LocalDateTime end; + +} diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java b/server/src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java new file mode 100644 index 0000000..c3b3476 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java @@ -0,0 +1,30 @@ +package ru.practicum.shareit.booking; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.item.ItemDtoShort; +import ru.practicum.shareit.user.UserDtoShort; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BookingDtoOutput { + + private Long id; + + private LocalDateTime start; + + private LocalDateTime end; + + private ItemDtoShort item; + + private UserDtoShort booker; + + @JsonProperty("status") + private BookingStatus status; + +} diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java b/server/src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java new file mode 100644 index 0000000..de9f8b7 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java @@ -0,0 +1,24 @@ +package ru.practicum.shareit.booking; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BookingDtoShort { + + private Long id; + + private LocalDateTime start; + + private LocalDateTime end; + + @JsonProperty("status") + private BookingStatus status; + +} diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java b/server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java new file mode 100644 index 0000000..adf3f3d --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java @@ -0,0 +1,65 @@ +package ru.practicum.shareit.booking; + +import ru.practicum.shareit.item.ItemMapper; +import ru.practicum.shareit.user.UserMapper; + +public class BookingMapper { + + public static BookingDtoOutput toBookingDtoOutput(Booking booking) { + if (booking == null) { + return null; + } + BookingDtoOutput bookingDto = new BookingDtoOutput(); + + bookingDto.setId(booking.getId()); + bookingDto.setStart(booking.getStart()); + bookingDto.setEnd(booking.getEnd()); + bookingDto.setItem(ItemMapper.toItemDtoShort(booking.getItem())); + bookingDto.setBooker(UserMapper.toUserDtoShort(booking.getBooker())); + bookingDto.setStatus(booking.getStatus()); + + return bookingDto; + } + + public static BookingDtoShort toBookingDtoShort(Booking booking) { + if (booking == null) { + return null; + } + BookingDtoShort bookingDto = new BookingDtoShort(); + + bookingDto.setId(booking.getId()); + bookingDto.setStart(booking.getStart()); + bookingDto.setEnd(booking.getEnd()); + bookingDto.setStatus(booking.getStatus()); + + return bookingDto; + } + + public static Booking toBooking(BookingDto bookingDto) { + Booking booking = new Booking(); + + if (bookingDto.getId() != null) { + if (bookingDto.getId() > 0) { + booking.setId(bookingDto.getId()); + } + } + if (bookingDto.getStart() != null) { + booking.setStart(bookingDto.getStart()); + } + if (bookingDto.getEnd() != null) { + booking.setEnd(bookingDto.getEnd()); + } + if (bookingDto.getItem() != null) { + booking.setItem(bookingDto.getItem()); + } + if (bookingDto.getBooker() != null) { + booking.setBooker(bookingDto.getBooker()); + } + if (bookingDto.getStatus() != null) { + booking.setStatus(bookingDto.getStatus()); + } + + return booking; + } + +} diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingRepository.java b/server/src/main/java/ru/practicum/shareit/booking/BookingRepository.java new file mode 100644 index 0000000..72ad8ac --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingRepository.java @@ -0,0 +1,32 @@ +package ru.practicum.shareit.booking; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; +import java.util.List; + +@Repository +public interface BookingRepository extends JpaRepository { + + List findAllByBookerOrderByStartDesc(User bookerFromDb); + + List findAllByBookerAndStartBeforeAndEndAfterOrderByStartDesc(User bookerFromDb, LocalDateTime nowDateTime, LocalDateTime nowDateTime1); + + List findAllByBookerAndEndIsBeforeOrderByStartDesc(User bookerFromDb, LocalDateTime nowDateTime); + + List findAllByBookerAndStartIsAfterOrderByStartDesc(User bookerFromDb, LocalDateTime nowDateTime); + + List findAllByBookerAndStatusEqualsOrderByStartDesc(User bookerFromDb, BookingState bookingState); + + List findAllByItem_OwnerOrderByStartDesc(User bookerFromDb); + + List findAllByItem_OwnerAndStartIsBeforeAndEndIsAfterOrderByStartDesc(User bookerFromDb, LocalDateTime nowDateTime, LocalDateTime nowDateTime1); + + List findAllByItem_OwnerAndEndIsBeforeOrderByStartDesc(User bookerFromDb, LocalDateTime nowDateTime); + + List findAllByItem_OwnerAndStartIsAfterOrderByStartDesc(User bookerFromDb, LocalDateTime nowDateTime); + + List findAllByItem_OwnerAndStatusEqualsOrderByStartDesc(User bookerFromDb, BookingState bookingState); +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingService.java b/server/src/main/java/ru/practicum/shareit/booking/BookingService.java new file mode 100644 index 0000000..9a731d5 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingService.java @@ -0,0 +1,16 @@ +package ru.practicum.shareit.booking; + +import java.util.List; + +public interface BookingService { + + Booking addBooking(Long bookerId, BookingDtoInput bookingDto); + + Booking updateBooking(Long ownerId, Long bookingId, Boolean approved); + + Booking getWithStatusById(Long userId, Long bookingId); + + List getByUserId(Long userId, String state); + + List getByOwnerId(Long userId, String state); +} diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java b/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java new file mode 100644 index 0000000..d0f22f3 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java @@ -0,0 +1,267 @@ +package ru.practicum.shareit.booking; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.exception.RestrictedAccessException; +import ru.practicum.shareit.exception.ValidationException; +import ru.practicum.shareit.item.Item; +import ru.practicum.shareit.item.ItemRepository; +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.UserRepository; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +@Slf4j +public class BookingServiceImpl implements BookingService { + private final BookingRepository bookingRepository; + private final ItemRepository itemRepository; + private final UserRepository userRepository; + + /** + * Создание брони в БД. + * + * @param bookerId пользователь, пытающийся забронировать вещь. + * @param inputBookingDto создаваемая бронь. + * @return бронь из БД. + */ + @Override + public Booking addBooking(Long bookerId, BookingDtoInput inputBookingDto) { + Item itemFromDB = itemRepository.findById(inputBookingDto.getItemId()) + .orElseThrow(() -> new NotFoundException("При создании бронирования не найдена " + + "вещь с ID = " + inputBookingDto.getItemId() + " в БД.")); + BookingDto bookingDto = new BookingDto(); + bookingDto.setStart(inputBookingDto.getStart()); + bookingDto.setEnd(inputBookingDto.getEnd()); + bookingDto.setItem(itemFromDB); + User bookerFromDb = userRepository.findById(bookerId) + .orElseThrow(() -> new NotFoundException("При " + + "создании бронирования не найден пользователь с ID = " + bookerId + " в БД.")); + validateBooking(bookingDto, itemFromDB, bookerFromDb); + if (!itemFromDB.getAvailable()) { + throw new ValidationException("Вещь нельзя забронировать, поскольку available = false."); + } + bookingDto.setStatus(BookingStatus.WAITING); + bookingDto.setItem(itemFromDB); + bookingDto.setBooker(bookerFromDb); + Booking result = bookingRepository.save(BookingMapper.toBooking(bookingDto)); + log.info("Создано бронирование с ID = [ {} ].", result.getId()); + return result; + } + + /** + * Обновить бронь в БД. + * + * @param ownerId хозяин вещи. + * @param bookingId ID брони. + * @param approved True - подтверждение со стороны хозяина вещи, + * False - отклонено хозяином вещи. + * @return обновлённая бронь. + */ + @Override + public Booking updateBooking(Long ownerId, Long bookingId, Boolean approved) { + Booking bookingFromBd = bookingRepository.findById(bookingId).orElseThrow(() -> new NotFoundException( + "При обновлении бронирования не найдено бронирование с ID = '" + bookingId + "' в БД.")); + if (bookingFromBd.getStatus().equals(BookingStatus.APPROVED) && approved) { + String message = "Данное бронирование уже было обработано и имеет статус '" + + bookingFromBd.getStatus() + "'."; + log.info(message); + throw new ValidationException(message); + } + User ownerFromDb = userRepository.findById(ownerId).orElseThrow(() -> new RestrictedAccessException("При " + + "обновлении бронирования не найден пользователь с ID = '" + ownerId + "' в БД.")); + List items = ownerFromDb.getItems(); + for (Item i : ownerFromDb.getItems()) { + if (i.getId().equals(bookingFromBd.getItem().getId())) { + bookingFromBd.setStatus(approved ? BookingStatus.APPROVED : BookingStatus.REJECTED); + Booking result = bookingRepository.save(bookingFromBd); + log.info("Бронирование с ID = [ {} ] обновлено.", bookingId); + return result; + } + } + String message = "При обновлении брони у хозяина вещи эта вещь не найдена. Ошибка в запросе."; + log.info(message); + throw new NotFoundException(message); + } + + /** + * • Получение данных о конкретном бронировании (включая его статус). + * Может быть выполнено либо автором бронирования, либо владельцем вещи, + * к которой относится бронирование. + * + * @param userId ID пользователя, делающего запрос. + * @param bookingId ID брони. + */ + @Override + public Booking getWithStatusById(Long userId, Long bookingId) { + Booking booking = bookingRepository.findById(bookingId) + .orElseThrow(() -> new NotFoundException("Бронирование с ID = '" + bookingId + + "не найдено в БД при его получении.")); + if (userId.equals(booking.getBooker().getId()) || userId.equals(booking.getItem().getOwner().getId())) { + return booking; + } + throw new NotFoundException("Ошибка при получении брони с ID = '" + bookingId + + "'. Пользователь с ID = '" + userId + + "' не является ни хозяином, ни пользователем, забронировавшим вещь."); + } + + /** + * • Получение списка всех бронирований текущего пользователя. + * Параметр state необязательный и по умолчанию равен ALL (англ. «все»). + * Также он может принимать значения CURRENT (англ. «текущие»), PAST (англ. «завершённые»), + * FUTURE (англ. «будущие»), WAITING (англ. «ожидающие подтверждения»), REJECTED (англ. «отклонённые»). + * Бронирования должны возвращаться отсортированными по дате от более новых к более старым. + * + * @param userId ID пользователя. + * @param state статус бронирования. + */ + @Override + public List getByUserId(Long userId, String state) { + final LocalDateTime nowDateTime = LocalDateTime.now(); + BookingState bookingState; + + if (state.isBlank()) { + bookingState = BookingState.ALL; + } else { + try { + bookingState = BookingState.valueOf(state); + } catch (IllegalArgumentException ex) { + throw new ValidationException("Неизвестное состояние бронирования."); + } + } + User bookerFromDb = userRepository.findById(userId).orElseThrow(() -> new NotFoundException("При " + + "получении списка бронирований не найден пользователь (арендующий) с ID = " + userId + " в БД.")); + List result = new ArrayList<>(); + + switch (bookingState) { + case ALL: { + result = bookingRepository.findAllByBookerOrderByStartDesc(bookerFromDb); + break; + } + case CURRENT: { + result = bookingRepository.findAllByBookerAndStartBeforeAndEndAfterOrderByStartDesc( + bookerFromDb, nowDateTime, nowDateTime); + break; + } + case PAST: { + result = bookingRepository.findAllByBookerAndEndIsBeforeOrderByStartDesc( + bookerFromDb, nowDateTime); + break; + } + case FUTURE: { + result = bookingRepository.findAllByBookerAndStartIsAfterOrderByStartDesc( + bookerFromDb, nowDateTime); + break; + } + case WAITING: { + result = bookingRepository.findAllByBookerAndStatusEqualsOrderByStartDesc( + bookerFromDb, BookingState.WAITING); + break; + } + case REJECTED: { + result = bookingRepository.findAllByBookerAndStatusEqualsOrderByStartDesc( + bookerFromDb, BookingState.REJECTED); + break; + } + default: { + throw new ValidationException("Неизвестное состояние бронирования."); + } + } + + return result; + } + + /** + * • Получение списка бронирований для всех вещей текущего пользователя, то есть хозяина вещей. + * + * @param userId ID хозяина вещей. + * @param state Параметр state необязательный и по умолчанию равен ALL (англ. «все»). + * Также он может принимать значения CURRENT (англ. «текущие»), PAST (англ. «завершённые»), + * FUTURE (англ. «будущие»), WAITING (англ. «ожидающие подтверждения»), + * REJECTED (англ. «отклонённые»). + * @return Бронирования должны возвращаться отсортированными по дате от более новых к более старым. + */ + @Override + public List getByOwnerId(Long userId, String state) { + final LocalDateTime nowDateTime = LocalDateTime.now(); + BookingState bookingState; + + try { + bookingState = BookingState.valueOf(state); + } catch (IllegalArgumentException ex) { + throw new ValidationException("Неизвестное состояние бронирования."); + } + User bookerFromDb = userRepository.findById(userId).orElseThrow(() -> new NotFoundException("При " + + "получении списка бронирований не найден хозяин с ID = " + userId + " в БД.")); + List result = new ArrayList<>(); + + switch (bookingState) { + case ALL: { + result = bookingRepository.findAllByItem_OwnerOrderByStartDesc(bookerFromDb); + break; + } + case CURRENT: { + result = bookingRepository.findAllByItem_OwnerAndStartIsBeforeAndEndIsAfterOrderByStartDesc( + bookerFromDb, nowDateTime, nowDateTime); + break; + } + case PAST: { + result = bookingRepository.findAllByItem_OwnerAndEndIsBeforeOrderByStartDesc( + bookerFromDb, nowDateTime); + break; + } + case FUTURE: { + result = bookingRepository.findAllByItem_OwnerAndStartIsAfterOrderByStartDesc( + bookerFromDb, nowDateTime); + break; + } + case WAITING: { + result = bookingRepository.findAllByItem_OwnerAndStatusEqualsOrderByStartDesc( + bookerFromDb, BookingState.WAITING); + break; + } + case REJECTED: { + result = bookingRepository.findAllByItem_OwnerAndStatusEqualsOrderByStartDesc( + bookerFromDb, BookingState.REJECTED); + break; + } + default: { + throw new ValidationException("Неизвестное состояние бронирования."); + } + } + + return result; + } + + /** + * Проверка при создании бронирования вещи. + * + * @param bookingDto бронь. + * @param item вещь. + * @param booker пользователь. + */ + private void validateBooking(BookingDto bookingDto, Item item, User booker) { + if (item.getOwner().equals(booker)) { + String message = "Создать бронь на свою вещь нельзя."; + log.info(message); + throw new ValidationException(message); + } + + List bookings = item.getBookings(); + if (!bookings.isEmpty()) { + for (Booking b : bookings) { + if (!(b.getEnd().isBefore(bookingDto.getStart()) || + b.getStart().isAfter(bookingDto.getStart()))) { + String message = "Найдено пересечение дат бронирования на вещь с name = " + item.getName() + "."; + log.debug(message); + throw new ValidationException(message); + } + } + } + } +} diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingState.java b/server/src/main/java/ru/practicum/shareit/booking/BookingState.java new file mode 100644 index 0000000..76499ce --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingState.java @@ -0,0 +1,5 @@ +package ru.practicum.shareit.booking; + +public enum BookingState { + ALL, CURRENT, PAST, FUTURE, WAITING, REJECTED +} diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingStatus.java b/server/src/main/java/ru/practicum/shareit/booking/BookingStatus.java new file mode 100644 index 0000000..22928de --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingStatus.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.booking; + +public enum BookingStatus { + WAITING, + APPROVED, + REJECTED +} diff --git a/server/src/main/java/ru/practicum/shareit/exception/DataConflictException.java b/server/src/main/java/ru/practicum/shareit/exception/DataConflictException.java new file mode 100644 index 0000000..0ef9caf --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/DataConflictException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class DataConflictException extends RuntimeException { + public DataConflictException(String message) { + super(message); + } +} 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..eb16ec6 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -0,0 +1,84 @@ +package ru.practicum.shareit.exception; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.validation.BindingResult; +import org.springframework.validation.ObjectError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.MissingRequestHeaderException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +@Slf4j +@RestControllerAdvice +public class ErrorHandler { + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleValidationException(final ValidationException e) { + log.error("{} - {}", HttpStatus.BAD_REQUEST, e.getMessage()); + return new ErrorResponse(e.getMessage()); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.CONFLICT) + public ErrorResponse handleDataConflictException(final DataConflictException e) { + log.error("{} - {}", HttpStatus.CONFLICT, e.getMessage()); + return new ErrorResponse(e.getMessage()); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.FORBIDDEN) + public ErrorResponse handleRestrictedAccessException(final RestrictedAccessException e) { + log.error("{} - {}", HttpStatus.FORBIDDEN, e.getMessage()); + return new ErrorResponse(e.getMessage()); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleMethodArgumentNotValidException(final MethodArgumentNotValidException e) { + BindingResult bindingResult = e.getBindingResult(); + List allErrors = bindingResult.getAllErrors(); + String defaultMessage = allErrors.stream() + .map(error -> Objects.requireNonNull(error.getDefaultMessage())) + .collect(Collectors.joining(", ")); + log.error("{} - {}", HttpStatus.BAD_REQUEST, defaultMessage); + return new ErrorResponse(defaultMessage); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.NOT_FOUND) + public ErrorResponse handleNotFoundException(final NotFoundException e) { + log.error("{} - {}", HttpStatus.NOT_FOUND, e.getMessage()); + return new ErrorResponse(e.getMessage()); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ErrorResponse handleRunTimeException(final RuntimeException e) { + log.error("{} - {}", HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); + return new ErrorResponse(e.getMessage()); + } + + @ExceptionHandler(MissingRequestHeaderException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleMissingRequestHeaderException(MissingRequestHeaderException e) { + log.error("{} - {}", HttpStatus.BAD_REQUEST, e.getMessage()); + return new ErrorResponse(e.getMessage()); + } + + @Getter + public static class ErrorResponse { + private final String error; + + public ErrorResponse(String error) { + this.error = error; + } + + } +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/exception/NotFoundException.java b/server/src/main/java/ru/practicum/shareit/exception/NotFoundException.java new file mode 100644 index 0000000..98610d2 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/NotFoundException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class NotFoundException extends RuntimeException { + public NotFoundException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java b/server/src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java new file mode 100644 index 0000000..09df8b9 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class RestrictedAccessException extends RuntimeException { + public RestrictedAccessException(String message) { + super(message); + } +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/exception/ValidationException.java b/server/src/main/java/ru/practicum/shareit/exception/ValidationException.java new file mode 100644 index 0000000..59043da --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/ValidationException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class ValidationException extends RuntimeException { + public ValidationException(String message) { + super(message); + } +} diff --git a/server/src/main/java/ru/practicum/shareit/item/Comment.java b/server/src/main/java/ru/practicum/shareit/item/Comment.java new file mode 100644 index 0000000..107457e --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/Comment.java @@ -0,0 +1,36 @@ +package ru.practicum.shareit.item; + +import jakarta.persistence.*; +import lombok.*; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; + +@Getter +@Setter +@RequiredArgsConstructor +@AllArgsConstructor +@ToString +@EqualsAndHashCode(of = {"id"}) +@Entity +@Table(name = "comments") +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(length = 250) + private String text; + + @ManyToOne + @JoinColumn(name = "item_id", nullable = false) + private Item item; + + @ManyToOne + @JoinColumn(name = "user_id") + private User user; + + @Column(name = "created", nullable = false) + private LocalDateTime created; + +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/item/CommentDto.java b/server/src/main/java/ru/practicum/shareit/item/CommentDto.java new file mode 100644 index 0000000..fc9956b --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/CommentDto.java @@ -0,0 +1,25 @@ +package ru.practicum.shareit.item; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CommentDto { + + private Long id; + + private String text; + + private Item item; + + private User user; + + private LocalDateTime created; + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java b/server/src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java new file mode 100644 index 0000000..2e76ce5 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java @@ -0,0 +1,24 @@ +package ru.practicum.shareit.item; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CommentDtoOutput { + + private Long id; + + private String text; + + private ItemDtoShort item; + + private String authorName; + + private LocalDateTime created; + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/CommentDtoShort.java b/server/src/main/java/ru/practicum/shareit/item/CommentDtoShort.java new file mode 100644 index 0000000..2e4fa12 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/CommentDtoShort.java @@ -0,0 +1,24 @@ +package ru.practicum.shareit.item; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CommentDtoShort { + + private Long id; + + private String text; + + private ItemDtoShort item; + + private String authorName; + + private LocalDateTime created; + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java b/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java new file mode 100644 index 0000000..60d09b9 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java @@ -0,0 +1,84 @@ +package ru.practicum.shareit.item; + +import java.time.LocalDateTime; + +public class CommentMapper { + + public static CommentDto toCommentDto(Comment comment) { + if (comment == null) { + return null; + } + CommentDto commentDto = new CommentDto(); + + commentDto.setId(comment.getId()); + commentDto.setText(comment.getText()); + commentDto.setItem(comment.getItem()); + commentDto.setUser(comment.getUser()); + commentDto.setCreated(comment.getCreated()); + + return commentDto; + } + + public static CommentDtoOutput toCommentDtoOutput(Comment comment) { + if (comment == null) { + return null; + } + CommentDtoOutput commentDto = new CommentDtoOutput(); + + commentDto.setId(comment.getId()); + commentDto.setText(comment.getText()); + commentDto.setItem(ItemMapper.toItemDtoShort(comment.getItem())); + commentDto.setAuthorName(comment.getUser().getName()); + commentDto.setCreated(comment.getCreated()); + + return commentDto; + } + + public static CommentDtoShort toCommentDtoShort(Comment comment) { + if (comment == null) { + return null; + } + CommentDtoShort commentDto = new CommentDtoShort(); + + commentDto.setId(comment.getId()); + commentDto.setText(comment.getText()); + commentDto.setItem(ItemMapper.toItemDtoShort(comment.getItem())); + commentDto.setAuthorName(comment.getUser().getName()); + commentDto.setCreated(comment.getCreated()); + + return commentDto; + } + + public static Comment toComment(CommentDto commentDto) { + Comment comment = new Comment(); + + if (commentDto.getId() != null) { + if (commentDto.getId() > 0) { + comment.setId(commentDto.getId()); + } + } + if (commentDto.getText() != null) { + if (!commentDto.getText().trim().isEmpty()) { + comment.setText(commentDto.getText().trim()); + } + } + if (commentDto.getItem().getId() != null) { + if (commentDto.getItem().getId() > 0) { + comment.setItem(commentDto.getItem()); + } + } + if (commentDto.getUser().getId() != null) { + if (commentDto.getUser().getId() > 0) { + comment.setUser(commentDto.getUser()); + } + } + if (commentDto.getCreated() != null) { + if (!commentDto.getCreated().isBefore(LocalDateTime.now())) { + comment.setCreated(commentDto.getCreated()); + } + } + + return comment; + } + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/CommentRepository.java b/server/src/main/java/ru/practicum/shareit/item/CommentRepository.java new file mode 100644 index 0000000..55f2ae5 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/CommentRepository.java @@ -0,0 +1,9 @@ +package ru.practicum.shareit.item; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CommentRepository extends JpaRepository { + +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/item/Item.java b/server/src/main/java/ru/practicum/shareit/item/Item.java new file mode 100644 index 0000000..bd80d79 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/Item.java @@ -0,0 +1,52 @@ +package ru.practicum.shareit.item; + +import com.fasterxml.jackson.annotation.JsonIdentityInfo; +import com.fasterxml.jackson.annotation.ObjectIdGenerators; +import jakarta.persistence.*; +import lombok.*; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.request.Request; +import ru.practicum.shareit.user.User; + +import java.util.List; + +@Getter +@Setter +@RequiredArgsConstructor +@AllArgsConstructor +@ToString +@EqualsAndHashCode(of = {"id"}) +@JsonIdentityInfo( + generator = ObjectIdGenerators.PropertyGenerator.class, + property = "id") +@Entity +@Table(name = "items") +public class Item { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(length = 100) + private String name; + + @Column(length = 250) + private String description; + + @Column(name = "is_available", nullable = false) + private Boolean available; + + @ManyToOne + @JoinColumn(name = "owner_id", nullable = false) + private User owner; + + @ManyToOne + @JoinColumn(name = "request_id") + private Request request; + + @OneToMany(mappedBy = "item") + private List comments; + + @OneToMany(mappedBy = "item") + private List bookings; + +} \ No newline at end of file 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..c95d869 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -0,0 +1,59 @@ +package ru.practicum.shareit.item; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import java.util.List; + +@RestController +@RequestMapping("/items") +@RequiredArgsConstructor +public class ItemController { + private final ItemService itemService; + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public ItemDtoOutput addItem(@RequestBody ItemDtoInput itemDto, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + return ItemMapper.toItemDtoOutput(itemService.addItem(idUser, itemDto), idUser); + } + + @PatchMapping("/{idItem}") + public ItemDtoOutput updateItem(@PathVariable Long idItem, + @RequestBody ItemDtoInput itemDto, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + return ItemMapper.toItemDtoOutput(itemService.updateItem(idUser, idItem, itemDto), idUser); + } + + @GetMapping("/{idItem}") + public ItemDtoOutput getItemById(@PathVariable Long idItem, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + return ItemMapper.toItemDtoOutput(itemService.getItemById(idItem), idUser); + } + + @GetMapping + public List getAllItemsByUser(@RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + return itemService.getAllItems(idUser).stream() + .map(item -> ItemMapper.toItemDtoOutput(item, idUser)).toList(); + } + + @DeleteMapping("/{idItem}") + public void removeItem(@PathVariable Long idItem, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + itemService.removeItem(idUser, idItem); + } + + @GetMapping("/search") + public List searchItemsByText(@RequestParam String text, + @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { + return itemService.searchItems(text).stream() + .map(item -> ItemMapper.toItemDtoOutput(item, idUser)).toList(); + } + + @PostMapping("/{itemId}/comment") + public CommentDtoOutput addCommentToItem(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long itemId, + @RequestBody CommentDto commentDto) { + return CommentMapper.toCommentDtoOutput((itemService.saveComment(userId, itemId, commentDto))); + } +} diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDto.java b/server/src/main/java/ru/practicum/shareit/item/ItemDto.java new file mode 100644 index 0000000..4f5f206 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemDto.java @@ -0,0 +1,37 @@ +package ru.practicum.shareit.item; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.request.RequestDto; +import ru.practicum.shareit.user.User; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemDto { + + private Long id; + + private String name; + + private String description; + + private Boolean available; + + private User owner; + + private RequestDto request; + + private List comments; + + private List bookings; + + private Booking lastBooking; + + private Booking nextBooking; + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java b/server/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java new file mode 100644 index 0000000..97209f5 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemDtoInput.java @@ -0,0 +1,20 @@ +package ru.practicum.shareit.item; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemDtoInput { + + private String name; + + private String description; + + private Boolean available; + + private Long requestId; + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java b/server/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java new file mode 100644 index 0000000..c68c82f --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java @@ -0,0 +1,37 @@ +package ru.practicum.shareit.item; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.BookingDtoShort; +import ru.practicum.shareit.request.RequestDto; +import ru.practicum.shareit.user.UserDtoShort; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemDtoOutput { + + private Long id; + + private String name; + + private String description; + + private Boolean available; + + private UserDtoShort owner; + + private RequestDto request; + + private List comments; + + private List bookings; + + private BookingDtoShort lastBooking; + + private BookingDtoShort nextBooking; + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java b/server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java new file mode 100644 index 0000000..95ffbee --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java @@ -0,0 +1,20 @@ +package ru.practicum.shareit.item; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemDtoRequest { + + private Long idItem; + + private String name; + + private Long idOwner; + + private String Owner; + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDtoShort.java b/server/src/main/java/ru/practicum/shareit/item/ItemDtoShort.java new file mode 100644 index 0000000..8dc3627 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemDtoShort.java @@ -0,0 +1,20 @@ +package ru.practicum.shareit.item; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemDtoShort { + + private Long id; + + private String name; + + private String description; + + private Boolean available; + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemMapper.java b/server/src/main/java/ru/practicum/shareit/item/ItemMapper.java new file mode 100644 index 0000000..9f81b4e --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemMapper.java @@ -0,0 +1,174 @@ +package ru.practicum.shareit.item; + +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.booking.BookingMapper; +import ru.practicum.shareit.booking.BookingStatus; +import ru.practicum.shareit.request.RequestMapper; +import ru.practicum.shareit.user.UserMapper; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +public class ItemMapper { + + public static ItemDto toItemDto(Item item) { + if (item == null) { + return null; + } + ItemDto itemDto = new ItemDto(); + + itemDto.setId(item.getId()); + itemDto.setName(item.getName()); + itemDto.setDescription(item.getDescription()); + itemDto.setAvailable(item.getAvailable()); + itemDto.setOwner(item.getOwner()); + itemDto.setRequest(RequestMapper.toRequestDto(item.getRequest())); + itemDto.setComments(item.getComments()); + itemDto.setBookings(item.getBookings()); + + return itemDto; + } + + public static ItemDtoOutput toItemDtoOutput(Item item, Long userId) { + if (item == null) { + return null; + } + ItemDtoOutput itemDto = new ItemDtoOutput(); + + itemDto.setId(item.getId()); + itemDto.setName(item.getName()); + itemDto.setDescription(item.getDescription()); + itemDto.setAvailable(item.getAvailable()); + itemDto.setOwner(UserMapper.toUserDtoShort(item.getOwner())); + itemDto.setRequest(RequestMapper.toRequestDto(item.getRequest())); + List comments = Optional.ofNullable(item.getComments()).orElse(Collections.emptyList()); + if (!comments.isEmpty()) { + itemDto.setComments(comments.stream().map(CommentMapper::toCommentDtoShort).toList()); + } else { + itemDto.setComments(Collections.emptyList()); + } + List bookings = Optional.ofNullable(item.getBookings()).orElse(Collections.emptyList()); + if (!bookings.isEmpty()) { + if (itemDto.getOwner().getId().equals(userId)) { + itemDto.setBookings(bookings.stream().map(BookingMapper::toBookingDtoShort).toList()); + itemDto.setLastBooking(BookingMapper.toBookingDtoShort(findLastBooking(bookings))); + itemDto.setNextBooking(BookingMapper.toBookingDtoShort(findNextBooking(bookings))); + } else { + itemDto.setBookings(Collections.emptyList()); + itemDto.setLastBooking(null); + itemDto.setNextBooking(null); + } + } else { + itemDto.setBookings(Collections.emptyList()); + itemDto.setLastBooking(null); + itemDto.setNextBooking(null); + } + + return itemDto; + } + + public static ItemDtoShort toItemDtoShort(Item item) { + if (item == null) { + return null; + } + ItemDtoShort itemDto = new ItemDtoShort(); + + itemDto.setId(item.getId()); + itemDto.setName(item.getName()); + itemDto.setDescription(item.getDescription()); + itemDto.setAvailable(item.getAvailable()); + + return itemDto; + } + + public static ItemDtoRequest toItemDtoRequest(Item item) { + if (item == null) { + return null; + } + ItemDtoRequest itemDto = new ItemDtoRequest(); + + itemDto.setIdItem(item.getId()); + itemDto.setName(item.getName()); + itemDto.setIdOwner(item.getOwner().getId()); + itemDto.setOwner(item.getOwner().getName()); + + return itemDto; + } + + public static Item toItem(ItemDto itemDto) { + Item item = new Item(); + + if (itemDto.getId() != null) { + if (itemDto.getId() > 0) { + item.setId(itemDto.getId()); + } + } + if (itemDto.getName() != null) { + item.setName(itemDto.getName().trim()); + } + if (itemDto.getDescription() != null) { + item.setDescription(itemDto.getDescription().trim()); + } + if (itemDto.getAvailable() != null) { + item.setAvailable(itemDto.getAvailable()); + } + if (itemDto.getOwner().getId() != null) { + if (itemDto.getOwner().getId() > 0) { + item.setOwner(itemDto.getOwner()); + } + } + if (itemDto.getRequest() != null) { + if (itemDto.getRequest().getId() > 0) { + item.setRequest(RequestMapper.toRequest(itemDto.getRequest())); + } + } + if (itemDto.getComments() != null) { + if (!itemDto.getComments().isEmpty()) { + item.setComments(itemDto.getComments()); + } + } + if (itemDto.getBookings() != null) { + if (!itemDto.getBookings().isEmpty()) { + item.setBookings(itemDto.getBookings()); + } + } + + return item; + } + + /** + * Метод поиска первой аренды после текущего момента времени. + * + * @param bookings список бронирований. + * @return следующее бронирование после текущего момента времени. + */ + private static Booking findNextBooking(List bookings) { + return Optional.ofNullable(bookings) + .filter(list -> !list.isEmpty()) + .flatMap(list -> list.stream() + .filter(b -> b.getStart().isAfter(LocalDateTime.now())) + .filter(b -> b.getStatus().equals(BookingStatus.APPROVED) || + b.getStatus().equals(BookingStatus.WAITING)) + .min(Comparator.comparing(Booking::getStart))) + .orElse(null); + } + + /** + * Метод поиска последней аренды до текущего момента времени. + * + * @param bookings список бронирований. + * @return последнее бронирование до текущего момента времени. + */ + private static Booking findLastBooking(List bookings) { + return Optional.ofNullable(bookings) + .filter(list -> !list.isEmpty()) + .flatMap(list -> list.stream() + .filter(b -> b.getEnd().isBefore(LocalDateTime.now())) + .filter(b -> b.getStatus().equals(BookingStatus.APPROVED)) + .max(Comparator.comparing(Booking::getEnd))) + .orElse(null); + } +} 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..7a9bd2d --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemRepository.java @@ -0,0 +1,23 @@ +package ru.practicum.shareit.item; + +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.practicum.shareit.user.User; + +import java.util.List; + +@Repository +public interface ItemRepository extends JpaRepository { + List findAllByOwner(User owner, Sort name); + + List findByNameContainingIgnoreCaseAndAvailableTrueOrDescriptionContainingIgnoreCaseAndAvailableTrue( + String name, String description, Sort by); + + default List findAllByText(String text) { + return + findByNameContainingIgnoreCaseAndAvailableTrueOrDescriptionContainingIgnoreCaseAndAvailableTrue( + text, text, Sort.by("name")); + } + +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemService.java b/server/src/main/java/ru/practicum/shareit/item/ItemService.java new file mode 100644 index 0000000..c72f72e --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemService.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.item; + +import java.util.List; + +public interface ItemService { + + Item addItem(Long idUser, ItemDtoInput itemDto); + + Item updateItem(Long idUser, Long idItem, ItemDtoInput itemDto); + + Item getItemById(Long idItem); + + void removeItem(Long idUser, Long idItem); + + List getAllItems(Long idUser); + + List searchItems(String text); + + Comment saveComment(Long userId, Long itemId, CommentDto inputCommentDto); + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java b/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java new file mode 100644 index 0000000..e939b0e --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java @@ -0,0 +1,234 @@ +package ru.practicum.shareit.item; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.exception.RestrictedAccessException; +import ru.practicum.shareit.exception.ValidationException; +import ru.practicum.shareit.request.RequestMapper; +import ru.practicum.shareit.request.RequestRepository; +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.UserMapper; +import ru.practicum.shareit.user.UserRepository; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ItemServiceImpl implements ItemService { + + private final ItemRepository itemRepository; + private final UserRepository userRepository; + private final CommentRepository commentRepository; + private final RequestRepository requestRepository; + + /** + * Метод для добавления новой вещи. + * + * @param idUser идентификатор пользователя + * @param inputItemDto объект ItemDtoInput, содержащий данные новой вещи + * @return объект Item, содержащий данные добавленной вещи + * @throws ValidationException если идентификатор пользователя не указан + * @throws NotFoundException если пользователь с указанным id не найден в БД + */ + @Override + public Item addItem(Long idUser, ItemDtoInput inputItemDto) { + ItemDto itemDto = new ItemDto(); + User user = userRepository.findById(idUser).orElse(null); + if (user == null) { + String error = "Пользователь с id [ " + idUser + " ] не найден в БД при добавлении вещи."; + log.info(error); + throw new NotFoundException(error); + } + itemDto.setOwner(user); + itemDto.setName(inputItemDto.getName()); + itemDto.setDescription(inputItemDto.getDescription()); + itemDto.setAvailable(inputItemDto.getAvailable()); + if (inputItemDto.getRequestId() == null) { + itemDto.setRequest(null); + } else { + itemDto.setRequest(RequestMapper.toRequestDto(requestRepository.findById(inputItemDto.getRequestId()).orElse(null))); + } + Item result = ItemMapper.toItem(itemDto); + itemRepository.save(result); + log.info("Добавлена вещь [ {} ] пользователем [ {} ]", result.getId(), idUser); + return result; + } + + /** + * Метод для обновления данных вещи. + * + * @param idUser идентификатор пользователя + * @param idItem идентификатор вещи + * @param itemDtoInput объект ItemDtoInput, содержащий новые данные вещи + * @return объект Item, содержащий обновленные данные вещи + * @throws ValidationException если идентификатор пользователя не указан + * @throws NotFoundException если пользователь или вещь с указанным id не найдены в БД + * @throws RestrictedAccessException если пользователь не является владельцем вещи + */ + @Override + public Item updateItem(Long idUser, Long idItem, ItemDtoInput itemDtoInput) { + ItemDto itemDto = new ItemDto(); + if (userRepository.findById(idUser).isEmpty()) { + String error = "Пользователь с id [ " + idUser + " ] не найден в БД при изменение данных вещи."; + log.info(error); + throw new NotFoundException(error); + } + Optional oldItem = itemRepository.findById(idItem); + if (oldItem.isEmpty()) { + String error = "Вещь с id [ " + idItem + " ] не найдена в БД при изменение данных вещи."; + log.info(error); + throw new NotFoundException(error); + } + if (!idUser.equals(oldItem.get().getOwner().getId())) { + String error = "Пользователь с id [ " + idUser + " ] не является владельцем вещи с id [ " + idItem + " ]."; + log.info(error); + throw new RestrictedAccessException(error); + } + itemDto.setId(idItem); + itemDto.setOwner(oldItem.get().getOwner()); + itemDto.setRequest(RequestMapper.toRequestDto(oldItem.get().getRequest())); + if (itemDtoInput.getName() == null) { + itemDto.setName(oldItem.get().getName()); + } else { + itemDto.setName(itemDtoInput.getName()); + } + if (itemDtoInput.getDescription() == null) { + itemDto.setDescription(oldItem.get().getDescription()); + } else { + itemDto.setDescription(itemDtoInput.getDescription()); + } + if (itemDtoInput.getAvailable() == null) { + itemDto.setAvailable(oldItem.get().getAvailable()); + } else { + itemDto.setAvailable(itemDtoInput.getAvailable()); + } + Item newItem = ItemMapper.toItem(itemDto); + itemRepository.save(newItem); + log.info("Обновлены данные вещи [ {} ]", ItemMapper.toItemDtoShort(newItem)); + return newItem; + } + + /** + * Метод для получения данных вещи по идентификатору. + * + * @param idItem идентификатор вещи + * @return объект Item, содержащий данные вещи + * @throws NotFoundException если вещь с указанным id не найдена в БД + */ + @Override + public Item getItemById(Long idItem) { + Optional oldItem = itemRepository.findById(idItem); + if (oldItem.isEmpty()) { + String error = "Вещь с id [ " + idItem + " ] не найдена в БД при запросе данных вещи."; + log.info(error); + throw new NotFoundException(error); + } + log.info("По вещи с id [ {} ] успешно получены данные.", idItem); + return oldItem.get(); + } + + /** + * Метод для удаления вещи по идентификатору. + * + * @param idUser идентификатор пользователя + * @param idItem идентификатор вещи + * @throws ValidationException если идентификатор пользователя не указан + * @throws NotFoundException если пользователь или вещь с указанным id не найдены в БД + */ + @Override + public void removeItem(Long idUser, Long idItem) { + if (userRepository.findById(idUser).isEmpty()) { + String error = "Пользователь с id [ " + idUser + " ] не найден в БД при удалении данных о вещи."; + log.info(error); + throw new NotFoundException(error); + } + if (itemRepository.findById(idItem).isEmpty()) { + String error = "Вещь с id [ " + idItem + " ] не найдена в БД при удалении данных о вещи."; + log.info(error); + throw new NotFoundException(error); + } + itemRepository.delete(itemRepository.findById(idItem).get()); + log.info("Вещь с id [ {} ] успешно удалена.", idItem); + } + + /** + * Метод для получения списка всех вещей пользователя. + * + * @param idUser идентификатор пользователя + * @return список всех вещей пользователя, отсортированных по имени + * @throws ValidationException если идентификатор пользователя не указан + * @throws NotFoundException если пользователь с указанным id не найден в БД + */ + @Override + public List getAllItems(Long idUser) { + Optional user = userRepository.findById(idUser); + if (user.isEmpty()) { + String error = "Пользователь с id [ " + idUser + " ] не найден в БД при изменение данных вещи."; + log.info(error); + throw new NotFoundException(error); + } + List result = itemRepository.findAllByOwner(user.get(), Sort.by("name")); + log.info("Получен список всех вещей пользователя [ {} ] : [ {} ]", UserMapper.toUserDtoShort(user.get()), result.size()); + return result; + } + + /** + * Метод для поиска вещей по поисковому запросу. + * + * @param text поисковый запрос + * @return список вещей, в названии или описании которых присутствует поисковый запрос + */ + @Override + public List searchItems(String text) { + if (text.isBlank()) { + return List.of(); + } + List result = itemRepository.findAllByText(text); + log.info("Получен список вещей по поисковому запросу [ {} ] : [ {} ]", text, result.size()); + return result; + } + + /** + * Добавить комментарий к вещи пользователем, действительно бравшим вещь в аренду. + * + * @param bookerId ID пользователя, добавляющего комментарий. + * @param itemId ID вещи, которой оставляется комментарий. + */ + @Override + public Comment saveComment(Long bookerId, Long itemId, CommentDto commentDto) { + + User userFromBd = userRepository.findById(bookerId).orElseThrow(() -> + new NotFoundException("Ошибка при сохранении комментария к вещи с ID = " + itemId + + " пользователя с ID = " + bookerId + " в БД. В БД отсутствует запись о пользователе.")); + Item itemFromBd = itemRepository.findById(itemId).orElseThrow(() -> + new NotFoundException("Ошибка при сохранении комментария к вещи с ID = " + itemId + + " пользователя с ID = " + bookerId + " в БД. В БД отсутствует запись о вещи.")); + List bookings = itemFromBd.getBookings(); + boolean isBooker = false; + for (Booking b : bookings) { + if (b.getBooker().getId().equals(bookerId) && b.getEnd().isBefore(LocalDateTime.now())) { + isBooker = true; + break; + } + } + if (!isBooker) { + throw new ValidationException("Ошибка при сохранении комментария к вещи с ID = " + itemId + + " пользователя с ID = " + bookerId + " в БД. Пользователь не арендовал эту вещь."); + } + commentDto.setItem(itemFromBd); + commentDto.setUser(userFromBd); + commentDto.setCreated(LocalDateTime.now()); + Comment result = CommentMapper.toComment(commentDto); + result = commentRepository.save(result); + log.info("Комментарий к вещи успешно добавлен. Данные комментария: {}", CommentMapper.toCommentDtoShort(result)); + return result; + } + +} diff --git a/server/src/main/java/ru/practicum/shareit/request/Request.java b/server/src/main/java/ru/practicum/shareit/request/Request.java new file mode 100644 index 0000000..5947b30 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/Request.java @@ -0,0 +1,36 @@ +package ru.practicum.shareit.request; + +import jakarta.persistence.*; +import lombok.*; +import ru.practicum.shareit.item.Item; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; +import java.util.List; + +@Getter +@Setter +@RequiredArgsConstructor +@AllArgsConstructor +@ToString +@EqualsAndHashCode(of = {"id"}) +@Entity +@Table(name = "requests") +public class Request { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(length = 250) + private String description; + + @ManyToOne + @JoinColumn(name = "requestor_id", nullable = false) + private User requestor; + + @Column(nullable = false) + private LocalDateTime created; + + @OneToMany(mappedBy = "request") + private List items; +} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestController.java b/server/src/main/java/ru/practicum/shareit/request/RequestController.java new file mode 100644 index 0000000..d3c49ec --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestController.java @@ -0,0 +1,39 @@ +package ru.practicum.shareit.request; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * TODO Sprint add-item-requests. + */ +@RestController +@RequestMapping(path = "/requests") +@RequiredArgsConstructor +public class RequestController { + + private final RequestService requestService; + + @PostMapping + public RequestDto addRequest(@RequestHeader("X-Sharer-User-Id") Long requestorId, + @RequestBody RequestDtoInput requestDto) { + return RequestMapper.toRequestDto(requestService.addRequest(requestorId, requestDto)); + } + + @GetMapping + public List getRequests(@RequestHeader("X-Sharer-User-Id") Long requestorId) { + return requestService.getRequests(requestorId).stream().map(RequestMapper::toRequestDtoItems).toList(); + } + + @GetMapping("/all") + public List getAllRequests(@RequestHeader("X-Sharer-User-Id") Long requestorId) { + return requestService.getAllRequests(requestorId).stream().map(RequestMapper::toRequestDto).toList(); + } + + @GetMapping("/{requestId}") + public RequestDtoItems getRequestsById(@PathVariable Long requestId) { + return RequestMapper.toRequestDtoItems(requestService.getRequestById(requestId)); + } + +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestDto.java b/server/src/main/java/ru/practicum/shareit/request/RequestDto.java new file mode 100644 index 0000000..a49428d --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestDto.java @@ -0,0 +1,23 @@ +package ru.practicum.shareit.request; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RequestDto { + + private Long id; + + private String description; + + private User requestor; + + private LocalDateTime created; + +} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java b/server/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java new file mode 100644 index 0000000..8d44bc6 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestDtoInput.java @@ -0,0 +1,14 @@ +package ru.practicum.shareit.request; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RequestDtoInput { + + private String description; + +} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestDtoItems.java b/server/src/main/java/ru/practicum/shareit/request/RequestDtoItems.java new file mode 100644 index 0000000..178918b --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestDtoItems.java @@ -0,0 +1,26 @@ +package ru.practicum.shareit.request; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.item.ItemDtoRequest; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class RequestDtoItems { + + private Long id; + + private String description; + + private User requestor; + + private LocalDateTime created; + + List items; +} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestMapper.java b/server/src/main/java/ru/practicum/shareit/request/RequestMapper.java new file mode 100644 index 0000000..9608ce2 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestMapper.java @@ -0,0 +1,62 @@ +package ru.practicum.shareit.request; + +import ru.practicum.shareit.item.ItemMapper; + +public class RequestMapper { + + public static RequestDto toRequestDto(Request request) { + if (request == null) { + return null; + } + RequestDto requestDto = new RequestDto(); + + requestDto.setId(request.getId()); + requestDto.setDescription(request.getDescription()); + requestDto.setRequestor(request.getRequestor()); + requestDto.setCreated(request.getCreated()); + + return requestDto; + } + + public static RequestDtoItems toRequestDtoItems(Request request) { + if (request == null) { + return null; + } + RequestDtoItems requestDto = new RequestDtoItems(); + + requestDto.setId(request.getId()); + requestDto.setDescription(request.getDescription()); + requestDto.setRequestor(request.getRequestor()); + requestDto.setCreated(request.getCreated()); + requestDto.setItems(request.getItems().stream().map(ItemMapper::toItemDtoRequest).toList()); + + return requestDto; + } + + + public static Request toRequest(RequestDto requestDto) { + Request request = new Request(); + + if (requestDto.getId() != null) { + if (requestDto.getId() > 0) { + request.setId(requestDto.getId()); + } + } + if (requestDto.getDescription() != null) { + if (!requestDto.getDescription().trim().isEmpty()) { + request.setDescription(requestDto.getDescription().trim()); + } + } + if (requestDto.getRequestor().getId() != null) { + if (requestDto.getRequestor().getId() > 0) { + request.setRequestor(requestDto.getRequestor()); + } + } + if (requestDto.getCreated() != null) { + request.setCreated(requestDto.getCreated()); + } + + return request; + } + +} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestRepository.java b/server/src/main/java/ru/practicum/shareit/request/RequestRepository.java new file mode 100644 index 0000000..db723d3 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestRepository.java @@ -0,0 +1,17 @@ +package ru.practicum.shareit.request; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.practicum.shareit.user.User; + +import java.util.List; + +@Repository +public interface RequestRepository extends JpaRepository { + + List findAllByRequestorOrderByCreatedDesc(User requestor); + + List findAllByOrderByCreatedDesc(); + + List findAllByRequestorNotOrderByCreatedDesc(User requestor); +} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestService.java b/server/src/main/java/ru/practicum/shareit/request/RequestService.java new file mode 100644 index 0000000..6e3bba3 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestService.java @@ -0,0 +1,15 @@ +package ru.practicum.shareit.request; + +import java.util.List; + +public interface RequestService { + + Request addRequest(Long requestorId, RequestDtoInput requestDto); + + List getRequests(Long requestorId); + + List getAllRequests(Long requestorId); + + Request getRequestById(Long requestId); + +} diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java b/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java new file mode 100644 index 0000000..60dae61 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java @@ -0,0 +1,107 @@ +package ru.practicum.shareit.request; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.item.ItemRepository; +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.UserRepository; + +import java.time.LocalDateTime; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +public class RequestServiceImpl implements RequestService { + + private final RequestRepository requestRepository; + private final UserRepository userRepository; + + /** + * Добавляет новый запрос в базу данных. + * + * @param requestorId идентификатор пользователя, который создает запрос + * @param requestDto объект, содержащий данные для создания запроса + * @return созданный запрос + * @throws NotFoundException если пользователь с указанным идентификатором не найден в базе данных + */ + @Override + public Request addRequest(Long requestorId, RequestDtoInput requestDto) { + User requestor = new User(); + Request request = new Request(); + requestor = userRepository.findById(requestorId).orElse(null); + if (requestor == null) { + String error = "Пользователь с id [ " + requestorId + " ] не найден в БД при добавлении запроса."; + log.info(error); + throw new NotFoundException(error); + } + request.setDescription(requestDto.getDescription()); + request.setRequestor(requestor); + request.setCreated(LocalDateTime.now()); + request = requestRepository.save(request); + log.info("Запрос [ {} ] добавлен в БД.",request); + return request; + } + + /** + * Возвращает список запросов, созданных определенным пользователем, отсортированный по дате создания в обратном порядке. + * + * @param requestorId идентификатор пользователя, чьи запросы нужно получить + * @return список запросов, созданных пользователем + */ + @Override + public List getRequests(Long requestorId) { + User requestor = new User(); + requestor = userRepository.findById(requestorId).orElse(null); + if (requestor == null) { + log.info("Пользователь с id [ {} ] не найден в БД при получении своего списка запросов - вернулся пустой список.", requestorId); + return List.of(); + } + List requests = requestRepository.findAllByRequestorOrderByCreatedDesc(requestor); + log.info("Список запросов пользователя [ {} ] получен из БД в количестве [ {} ].", requestorId, requests.size()); + return requests; + } + + /** + * Возвращает список всех запросов, отсортированных по дате создания в обратном порядке. + * Если передан идентификатор пользователя, то возвращаются все запросы, + * кроме тех, которые были созданы этим пользователем. + * + * @param requestorId идентификатор пользователя, чьи запросы нужно исключить из списка (может быть null) + * @return список всех запросов, отсортированных по дате создания в обратном порядке + */ + @Override + public List getAllRequests(Long requestorId) { + User requestor = new User(); + requestor = userRepository.findById(requestorId).orElse(null); + if (requestor == null) { + List requests = requestRepository.findAllByOrderByCreatedDesc(); + log.info("Список доступных запросов получен из БД в количестве [ {} ].", requests.size()); + return requests; + } + List requests = requestRepository.findAllByRequestorNotOrderByCreatedDesc(requestor); + log.info("Список доступных запросов ( кроме запросов пользователя [ {} ] ) получен из БД в количестве [ {} ].", + requestorId, requests.size()); + return requests; + } + + /** + * Возвращает запрос по его идентификатору. + * + * @param requestId идентификатор запроса + * @return запрос с указанным идентификатором + * @throws NotFoundException если запрос с указанным идентификатором не найден в базе данных + */ + @Override + public Request getRequestById(Long requestId) { + Request request = requestRepository.findById(requestId).orElse(null); + if (request == null) { + String error = "Запрос с id [ " + requestId + " ] не найден в БД при получении."; + log.info(error); + throw new NotFoundException(error); + } + return request; + } +} diff --git a/server/src/main/java/ru/practicum/shareit/user/User.java b/server/src/main/java/ru/practicum/shareit/user/User.java new file mode 100644 index 0000000..07d790c --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/User.java @@ -0,0 +1,39 @@ +package ru.practicum.shareit.user; + +import jakarta.persistence.*; +import lombok.*; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.item.Comment; +import ru.practicum.shareit.item.Item; + +import java.util.List; + +@Getter +@Setter +@ToString +@RequiredArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(of = {"id"}) +@Entity +@Table(name = "users") +public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(length = 100) + private String name; + + @Column(length = 50, unique = true) + private String email; + + @OneToMany(mappedBy = "owner") + private List items; + + @OneToMany(mappedBy = "booker") + private List bookings; + + @OneToMany(mappedBy = "user") + private List comments; + +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/user/UserController.java b/server/src/main/java/ru/practicum/shareit/user/UserController.java new file mode 100644 index 0000000..98c0885 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/UserController.java @@ -0,0 +1,42 @@ +package ru.practicum.shareit.user; + +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + + +import java.util.List; + +@RestController +@RequestMapping("/users") +@RequiredArgsConstructor +public class UserController { + private final UserService userService; + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public UserDtoOutput addUser(@RequestBody UserDto userDto) { + return UserMapper.toUserDtoOutput(userService.addUser(userDto)); + } + + @PatchMapping("/{idUser}") + public UserDtoOutput updateUser(@PathVariable Long idUser, + @RequestBody UserDto userDto) { + return UserMapper.toUserDtoOutput(userService.updateUser(idUser, userDto)); + } + + @GetMapping("/{idUser}") + public UserDtoOutput getUserById(@PathVariable Long idUser) { + return UserMapper.toUserDtoOutput(userService.getUserById(idUser)); + } + + @GetMapping + public List getAllUsers() { + return userService.getAllUsers().stream().map(UserMapper::toUserDtoOutput).toList(); + } + + @DeleteMapping("/{idUser}") + public void removeUser(@PathVariable Long idUser) { + userService.removeUser(idUser); + } +} \ No newline at end of file diff --git a/server/src/main/java/ru/practicum/shareit/user/UserDto.java b/server/src/main/java/ru/practicum/shareit/user/UserDto.java new file mode 100644 index 0000000..924c595 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/UserDto.java @@ -0,0 +1,29 @@ +package ru.practicum.shareit.user; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.item.Comment; +import ru.practicum.shareit.item.Item; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserDto { + + private Long id; + + private String name; + + private String email; + + private List items; + + private List bookings; + + private List comments; + +} diff --git a/server/src/main/java/ru/practicum/shareit/user/UserDtoOutput.java b/server/src/main/java/ru/practicum/shareit/user/UserDtoOutput.java new file mode 100644 index 0000000..fd7a87b --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/UserDtoOutput.java @@ -0,0 +1,29 @@ +package ru.practicum.shareit.user; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.BookingDtoShort; +import ru.practicum.shareit.item.CommentDtoShort; +import ru.practicum.shareit.item.ItemDtoShort; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserDtoOutput { + + private Long id; + + private String name; + + private String email; + + private List items; + + private List bookings; + + private List comments; + +} diff --git a/server/src/main/java/ru/practicum/shareit/user/UserDtoShort.java b/server/src/main/java/ru/practicum/shareit/user/UserDtoShort.java new file mode 100644 index 0000000..7bb1143 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/UserDtoShort.java @@ -0,0 +1,18 @@ +package ru.practicum.shareit.user; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserDtoShort { + + private Long id; + + private String name; + + private String email; + +} diff --git a/server/src/main/java/ru/practicum/shareit/user/UserMapper.java b/server/src/main/java/ru/practicum/shareit/user/UserMapper.java new file mode 100644 index 0000000..400b2ec --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/UserMapper.java @@ -0,0 +1,96 @@ +package ru.practicum.shareit.user; + +import ru.practicum.shareit.booking.BookingMapper; +import ru.practicum.shareit.item.ItemMapper; +import ru.practicum.shareit.item.CommentMapper; + +import java.util.Collections; +import java.util.Optional; + +public class UserMapper { + + public static UserDto toUserDto(User user) { + if (user == null) { + return null; + } + UserDto userDto = new UserDto(); + + userDto.setId(user.getId()); + userDto.setName(user.getName()); + userDto.setEmail(user.getEmail()); + userDto.setItems(user.getItems()); + userDto.setBookings(user.getBookings()); + userDto.setComments(user.getComments()); + + return userDto; + } + + public static UserDtoOutput toUserDtoOutput(User user) { + if (user == null) { + return null; + } + UserDtoOutput userDto = new UserDtoOutput(); + + userDto.setId(user.getId()); + userDto.setName(user.getName()); + userDto.setEmail(user.getEmail()); + userDto.setItems(Optional.ofNullable(user.getItems()) + .map(items -> items.stream().map(ItemMapper::toItemDtoShort).toList()) + .orElse(Collections.emptyList())); + userDto.setBookings(Optional.ofNullable(user.getBookings()) + .map(bookings -> bookings.stream().map(BookingMapper::toBookingDtoShort).toList()) + .orElse(Collections.emptyList())); + userDto.setComments(Optional.ofNullable(user.getComments()) + .map(comments -> comments.stream().map(CommentMapper::toCommentDtoShort).toList()) + .orElse(Collections.emptyList())); + + return userDto; + } + + public static UserDtoShort toUserDtoShort(User user) { + if (user == null) { + return null; + } + UserDtoShort userDto = new UserDtoShort(); + + userDto.setId(user.getId()); + userDto.setName(user.getName()); + userDto.setEmail(user.getEmail()); + + return userDto; + } + + public static User toUser(UserDto userDto) { + User user = new User(); + + if (userDto.getId() != null) { + if (userDto.getId() > 0) { + user.setId(userDto.getId()); + } + } + if (userDto.getName() != null) { + user.setName(userDto.getName().trim()); + } + if (userDto.getEmail() != null) { + user.setEmail(userDto.getEmail().trim()); + } + if (userDto.getItems() != null) { + if (!userDto.getItems().isEmpty()) { + user.setItems(userDto.getItems()); + } + } + if (userDto.getBookings() != null) { + if (!userDto.getBookings().isEmpty()) { + user.setBookings(userDto.getBookings()); + } + } + if (userDto.getComments() != null) { + if (!userDto.getComments().isEmpty()) { + user.setComments(userDto.getComments()); + } + } + + return user; + } + +} diff --git a/server/src/main/java/ru/practicum/shareit/user/UserRepository.java b/server/src/main/java/ru/practicum/shareit/user/UserRepository.java new file mode 100644 index 0000000..2b0e794 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/UserRepository.java @@ -0,0 +1,11 @@ +package ru.practicum.shareit.user; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface UserRepository extends JpaRepository { + Optional getUserByEmail(String email); +} diff --git a/server/src/main/java/ru/practicum/shareit/user/UserService.java b/server/src/main/java/ru/practicum/shareit/user/UserService.java new file mode 100644 index 0000000..678b79d --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/UserService.java @@ -0,0 +1,17 @@ +package ru.practicum.shareit.user; + +import java.util.List; + +public interface UserService { + + User addUser(UserDto userDto); + + User updateUser(Long idUser, UserDto userDto); + + User getUserById(Long idUser); + + void removeUser(Long idUser); + + List getAllUsers(); + +} diff --git a/server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java b/server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java new file mode 100644 index 0000000..0ca64a0 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java @@ -0,0 +1,132 @@ +package ru.practicum.shareit.user; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import ru.practicum.shareit.exception.DataConflictException; +import ru.practicum.shareit.exception.NotFoundException; + +import java.util.List; +import java.util.Optional; + +@Slf4j +@Service +public class UserServiceImpl implements UserService { + + private final UserRepository userRepository; + + public UserServiceImpl(UserRepository userRepository) { + this.userRepository = userRepository; + } + + /** + * Метод для добавления нового пользователя. + * + * @param userDto объект UserDto, содержащий данные нового пользователя + * @return объект User, содержащий данные добавленного пользователя + * @throws DataConflictException если пользователь с указанным email уже существует в БД + */ + @Override + public User addUser(UserDto userDto) { + Optional oldUser = userRepository.getUserByEmail(userDto.getEmail()); + if (oldUser.isPresent()) { + String error = "Пользователь с email [ " + userDto.getEmail() + " ] уже существует в БД. " + + "Добавление пользователя невозможно."; + log.error(error); + throw new DataConflictException(error); + } + User result = userRepository.save(UserMapper.toUser(userDto)); + log.info("Добавлен пользователь [ {} ]", result); + return result; + } + + /** + * Метод для обновления данных пользователя. + * + * @param idUser идентификатор пользователя + * @param userDto объект UserDto, содержащий новые данные пользователя + * @return объект User, содержащий обновленные данные пользователя + * @throws DataConflictException если пользователь с указанным email уже существует в БД + * @throws NotFoundException если пользователь с указанным id не найден в БД + */ + @Override + public User updateUser(Long idUser, UserDto userDto) { + Optional oldUser = userRepository.getUserByEmail(userDto.getEmail()); + if (oldUser.isPresent()) { + String error = "Пользователь с email [ " + userDto.getEmail() + " ] уже существует в БД. " + + "Обновление данных пользователя невозможно."; + log.error(error); + throw new DataConflictException(error); + } + userDto.setId(idUser); + oldUser = userRepository.findById(idUser); + if (oldUser.isPresent()) { + if (userDto.getName() == null) { + userDto.setName(oldUser.get().getName()); + } + if (userDto.getEmail() == null) { + userDto.setEmail(oldUser.get().getEmail()); + } + } else { + String error = "Пользователь с id [ " + idUser + " ] не найден в БД при обновлении данных пользователя."; + log.info(error); + throw new NotFoundException(error); + } + User newUser = UserMapper.toUser(userDto); + userRepository.save(newUser); + log.info("Обновлен пользователь [ {} ]", newUser); + return newUser; + } + + + /** + * Метод для получения данных пользователя по идентификатору. + * + * @param idUser идентификатор пользователя + * @return объект User, содержащий данные пользователя + * @throws NotFoundException если пользователь с указанным id не найден в БД + */ + @Override + public User getUserById(Long idUser) { + Optional oldUser = userRepository.findById(idUser); + if (oldUser.isEmpty()) { + String error = "Пользователь с id [ " + idUser + " ] не найден в БД при запросе данных пользователя."; + log.info(error); + throw new NotFoundException(error); + } + log.info("По id [ {} ] успешно получены данные пользователя [ {} ].", idUser, oldUser.get()); + return oldUser.get(); + } + + /** + * Метод для получения списка всех пользователей. + * + * @return список всех пользователей, отсортированных по имени + */ + @Override + public List getAllUsers() { + List result = userRepository.findAll(Sort.by("name")); + log.info("Получен список всех пользователей : [ {} ]", result); + return result; + } + + /** + * Метод для удаления пользователя по идентификатору. + * + * @param idUser идентификатор пользователя + * @throws NotFoundException если пользователь с указанным id не найден в БД + */ + @Override + public void removeUser(Long idUser) { + Optional oldUser = userRepository.findById(idUser); + if (oldUser.isEmpty()) { + String error = "Пользователь с id [ " + idUser + " ] не найден в БД при удалении пользователя."; + log.info(error); + throw new NotFoundException(error); + } + userRepository.delete(oldUser.get()); + log.info("По id [ {} ] успешно удален пользователь.", idUser); + // log.info("По id [ {} ] успешно удален пользователь [ {} ].", idUser, oldUser.get()); + } + +} \ No newline at end of file diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties new file mode 100644 index 0000000..0f5ef57 --- /dev/null +++ b/server/src/main/resources/application.properties @@ -0,0 +1,17 @@ +server.port=9090 + +spring.jpa.hibernate.ddl-auto=none +spring.jpa.properties.hibernate.format_sql=true +spring.sql.init.mode=always + +#--- +spring.datasource.driverClassName=org.postgresql.Driver +spring.datasource.url=jdbc:postgresql://localhost:5432/shareit +spring.datasource.username=shareituser +spring.datasource.password=12345678 +#--- +spring.config.activate.on-profile=test +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.url=jdbc:h2:mem:shareit +spring.datasource.username=shareit +spring.datasource.password=shareit \ No newline at end of file diff --git a/server/src/main/resources/schema.sql b/server/src/main/resources/schema.sql new file mode 100644 index 0000000..0f68bd1 --- /dev/null +++ b/server/src/main/resources/schema.sql @@ -0,0 +1,44 @@ +DROP TABLE IF EXISTS requests CASCADE; +DROP TABLE IF EXISTS comments CASCADE; +DROP TABLE IF EXISTS bookings CASCADE; +DROP TABLE IF EXISTS users CASCADE; +DROP TABLE IF EXISTS items CASCADE; + +CREATE TABLE IF NOT EXISTS users ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(100), + email VARCHAR(50) UNIQUE +); + +CREATE TABLE IF NOT EXISTS requests ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + description VARCHAR(250) NOT NULL, + requestor_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE, + created TIMESTAMP WITHOUT TIME ZONE DEFAULT (now()) +); + +CREATE TABLE IF NOT EXISTS items ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(100), + description VARCHAR(250), + is_available BOOL DEFAULT FALSE, + owner_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE, + request_id BIGINT DEFAULT NULL REFERENCES requests (id) ON DELETE SET NULL ON UPDATE CASCADE +); + +CREATE TABLE IF NOT EXISTS bookings ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + start_date TIMESTAMP WITHOUT TIME ZONE, + end_date TIMESTAMP WITHOUT TIME ZONE, + item_id BIGINT NOT NULL REFERENCES items (id) ON DELETE CASCADE ON UPDATE CASCADE, + booker_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE, + status VARCHAR(10) DEFAULT 'WAITING' +); + +CREATE TABLE IF NOT EXISTS comments ( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + text VARCHAR(250), + item_id BIGINT NOT NULL REFERENCES items (id) ON DELETE CASCADE ON UPDATE CASCADE, + user_id BIGINT DEFAULT NULL REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE, + created TIMESTAMP WITHOUT TIME ZONE DEFAULT (now()) +); From 4973f4c7dd1df29c67d013dba811bb51639caede Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 22:03:09 +0300 Subject: [PATCH 08/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9F=D0=B5=D1=80=D0=B2=D0=B8=D1=87=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- checkstyle.xml | 257 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 checkstyle.xml diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 0000000..05d5086 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From e4004bb11b7423bc9ff4582da83d4037c2ceb9f3 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 22:06:24 +0300 Subject: [PATCH 09/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9F=D0=B5=D1=80=D0=B2=D0=B8=D1=87=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- checkstyle.xml | 257 ------------------------------------------------- pom.xml | 6 -- 2 files changed, 263 deletions(-) delete mode 100644 checkstyle.xml diff --git a/checkstyle.xml b/checkstyle.xml deleted file mode 100644 index 05d5086..0000000 --- a/checkstyle.xml +++ /dev/null @@ -1,257 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/pom.xml b/pom.xml index 79cba37..8d3b637 100644 --- a/pom.xml +++ b/pom.xml @@ -56,12 +56,6 @@ org.apache.maven.plugins maven-checkstyle-plugin 3.1.2 - - checkstyle.xml - true - true - true - From b1631e92cdfd20fb58c51a4f7d7a91ba42904924 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 22:21:35 +0300 Subject: [PATCH 10/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9F=D0=B5=D1=80=D0=B2=D0=B8=D1=87=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- checkstyle.xml | 257 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 checkstyle.xml diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 0000000..c28a0d3 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 4ad323cb0c8ab8f6a4cb894677e97127ce8139b9 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 22:24:34 +0300 Subject: [PATCH 11/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9F=D0=B5=D1=80=D0=B2=D0=B8=D1=87=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pom.xml b/pom.xml index 8d3b637..79cba37 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,12 @@ org.apache.maven.plugins maven-checkstyle-plugin 3.1.2 + + checkstyle.xml + true + true + true + From 62ccf08d09ec440f0140b353eaf3a8aa56397220 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 22:29:35 +0300 Subject: [PATCH 12/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/practicum/shareit/booking/BookingController.java | 2 +- gateway/src/main/java/ru/practicum/shareit/user/UserClient.java | 2 +- 2 files changed, 2 insertions(+), 2 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 6d368fb..7ca00ea 100644 --- a/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -59,7 +59,7 @@ public ResponseEntity updateByOwner(@RequestHeader("X-Sharer-User-Id") L public ResponseEntity getWithStatusById(@RequestHeader("X-Sharer-User-Id") Long userId, @PathVariable Long bookingId) { log.info("Получение данных о бронировании {}, пользователем {}", bookingId, userId); - return bookingClient.getWithStatusById( bookingId, userId); + return bookingClient.getWithStatusById(bookingId, userId); } /** 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 fe6a014..6f2ce19 100644 --- a/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java @@ -23,7 +23,7 @@ public UserClient(@Value("${shareit-server.url}") String serverUrl, RestTemplate ); } - public ResponseEntity addUser( UserDtoInput userDto) { + public ResponseEntity addUser(UserDtoInput userDto) { return post("", userDto); } From d3061bf4acc1a0d1ee606bb6aee66d0f31269ecc Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 22:32:29 +0300 Subject: [PATCH 13/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index bd0da80..aca9947 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ _java-shareit_ # Шеринг вещей -### Согласно задания спринта № 16 (add-item-requests-and-gateway) +### Согласно задания спринта № 16 ( add-item-requests-and-gateway ) ## выполнено Филипповских Сергеем _**Когорта-53**_ @@ -9,5 +9,3 @@ _**Для проверки выполнения 16 спринта использ [postman_for_shareit_16.json](/postman_for_shareit_16.json)**_ -![](/er_diagram_shareit.png) - From b651afc20b8ca07429494b2eac922486f356044d Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 22:37:26 +0300 Subject: [PATCH 14/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java | 2 +- .../java/ru/practicum/shareit/request/RequestServiceImpl.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java b/server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java index 95ffbee..8718bf3 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemDtoRequest.java @@ -15,6 +15,6 @@ public class ItemDtoRequest { private Long idOwner; - private String Owner; + private String owner; } diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java b/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java index 60dae61..6677b00 100644 --- a/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java @@ -4,7 +4,6 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import ru.practicum.shareit.exception.NotFoundException; -import ru.practicum.shareit.item.ItemRepository; import ru.practicum.shareit.user.User; import ru.practicum.shareit.user.UserRepository; From 570a225a782efed27e6dffca7385f5cfb8f92cfb Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 23:15:52 +0300 Subject: [PATCH 15/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/main/resources/application.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties index 0f5ef57..7996ba1 100644 --- a/server/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -2,8 +2,8 @@ server.port=9090 spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.format_sql=true +spring.jpa.properties.hibernate.jdbc.time_zone=UTC spring.sql.init.mode=always - #--- spring.datasource.driverClassName=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5432/shareit From 646872432298a535443c328bb5ae99ae615fb1c0 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 23:22:27 +0300 Subject: [PATCH 16/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/ru/practicum/shareit/item/CommentMapper.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java b/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java index 60d09b9..fc1c83c 100644 --- a/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java +++ b/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java @@ -73,9 +73,7 @@ public static Comment toComment(CommentDto commentDto) { } } if (commentDto.getCreated() != null) { - if (!commentDto.getCreated().isBefore(LocalDateTime.now())) { - comment.setCreated(commentDto.getCreated()); - } + comment.setCreated(commentDto.getCreated()); } return comment; From e643020fd1cde309e951d5c21565893beb87811e Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 23:24:48 +0300 Subject: [PATCH 17/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/ru/practicum/shareit/item/CommentMapper.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java b/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java index fc1c83c..cdced80 100644 --- a/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java +++ b/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java @@ -1,7 +1,5 @@ package ru.practicum.shareit.item; -import java.time.LocalDateTime; - public class CommentMapper { public static CommentDto toCommentDto(Comment comment) { From 913a4c5c9c37a517cd7f0475dd05a96661d4f5a6 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 23:38:44 +0300 Subject: [PATCH 18/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/main/resources/application.properties | 1 - 1 file changed, 1 deletion(-) diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties index 7996ba1..a064818 100644 --- a/server/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -2,7 +2,6 @@ server.port=9090 spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.format_sql=true -spring.jpa.properties.hibernate.jdbc.time_zone=UTC spring.sql.init.mode=always #--- spring.datasource.driverClassName=org.postgresql.Driver From 447c4bfa62cd4a7b523d1f3c20c680c568674e85 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 23:46:27 +0300 Subject: [PATCH 19/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/src/main/resources/application.properties | 1 + 1 file changed, 1 insertion(+) diff --git a/server/src/main/resources/application.properties b/server/src/main/resources/application.properties index a064818..79c5c1d 100644 --- a/server/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -2,6 +2,7 @@ server.port=9090 spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.format_sql=true +spring.jpa.properties.hibernate.jdbc.time_zone=UTC+3 spring.sql.init.mode=always #--- spring.datasource.driverClassName=org.postgresql.Driver From 0c1f1a977dcff94ed24fb5a6c8500e5c5aaa94ed Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sat, 26 Apr 2025 23:53:28 +0300 Subject: [PATCH 20/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/ru/practicum/shareit/ShareItApp.java | 13 - .../ru/practicum/shareit/booking/Booking.java | 41 --- .../shareit/booking/BookingController.java | 86 ------ .../practicum/shareit/booking/BookingDto.java | 28 -- .../shareit/booking/BookingDtoInput.java | 28 -- .../shareit/booking/BookingDtoOutput.java | 30 -- .../shareit/booking/BookingDtoShort.java | 24 -- .../shareit/booking/BookingMapper.java | 65 ---- .../shareit/booking/BookingRepository.java | 32 -- .../shareit/booking/BookingService.java | 16 - .../shareit/booking/BookingServiceImpl.java | 280 ------------------ .../shareit/booking/BookingState.java | 5 - .../shareit/booking/BookingStatus.java | 7 - .../exception/DataConflictException.java | 7 - .../shareit/exception/ErrorHandler.java | 84 ------ .../shareit/exception/NotFoundException.java | 7 - .../exception/RestrictedAccessException.java | 7 - .../exception/ValidationException.java | 7 - .../ru/practicum/shareit/item/Comment.java | 36 --- .../ru/practicum/shareit/item/CommentDto.java | 30 -- .../shareit/item/CommentDtoOutput.java | 24 -- .../shareit/item/CommentDtoShort.java | 24 -- .../practicum/shareit/item/CommentMapper.java | 84 ------ .../shareit/item/CommentRepository.java | 9 - .../java/ru/practicum/shareit/item/Item.java | 52 ---- .../shareit/item/ItemController.java | 62 ---- .../ru/practicum/shareit/item/ItemDto.java | 45 --- .../practicum/shareit/item/ItemDtoOutput.java | 37 --- .../practicum/shareit/item/ItemDtoShort.java | 20 -- .../ru/practicum/shareit/item/ItemMapper.java | 159 ---------- .../shareit/item/ItemRepository.java | 23 -- .../practicum/shareit/item/ItemService.java | 21 -- .../shareit/item/ItemServiceImpl.java | 218 -------------- .../request/ItemRequestController.java | 12 - .../shareit/request/ItemRequestDto.java | 20 -- .../ru/practicum/shareit/request/Request.java | 35 --- .../java/ru/practicum/shareit/user/User.java | 39 --- .../shareit/user/UserController.java | 43 --- .../ru/practicum/shareit/user/UserDto.java | 38 --- .../practicum/shareit/user/UserDtoOutput.java | 29 -- .../practicum/shareit/user/UserDtoShort.java | 18 -- .../ru/practicum/shareit/user/UserMapper.java | 96 ------ .../shareit/user/UserRepository.java | 11 - .../practicum/shareit/user/UserService.java | 17 -- .../shareit/user/UserServiceImpl.java | 132 --------- .../shareit/validation/CreateObject.java | 4 - .../shareit/validation/UpdateObject.java | 4 - src/main/resources/application.yaml | 20 -- src/main/resources/schema.sql | 44 --- 49 files changed, 2173 deletions(-) delete mode 100644 src/main/java/ru/practicum/shareit/ShareItApp.java delete mode 100644 src/main/java/ru/practicum/shareit/booking/Booking.java delete mode 100644 src/main/java/ru/practicum/shareit/booking/BookingController.java delete mode 100644 src/main/java/ru/practicum/shareit/booking/BookingDto.java delete mode 100644 src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java delete mode 100644 src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java delete mode 100644 src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java delete mode 100644 src/main/java/ru/practicum/shareit/booking/BookingMapper.java delete mode 100644 src/main/java/ru/practicum/shareit/booking/BookingRepository.java delete mode 100644 src/main/java/ru/practicum/shareit/booking/BookingService.java delete mode 100644 src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java delete mode 100644 src/main/java/ru/practicum/shareit/booking/BookingState.java delete mode 100644 src/main/java/ru/practicum/shareit/booking/BookingStatus.java delete mode 100644 src/main/java/ru/practicum/shareit/exception/DataConflictException.java delete mode 100644 src/main/java/ru/practicum/shareit/exception/ErrorHandler.java delete mode 100644 src/main/java/ru/practicum/shareit/exception/NotFoundException.java delete mode 100644 src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java delete mode 100644 src/main/java/ru/practicum/shareit/exception/ValidationException.java delete mode 100644 src/main/java/ru/practicum/shareit/item/Comment.java delete mode 100644 src/main/java/ru/practicum/shareit/item/CommentDto.java delete mode 100644 src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java delete mode 100644 src/main/java/ru/practicum/shareit/item/CommentDtoShort.java delete mode 100644 src/main/java/ru/practicum/shareit/item/CommentMapper.java delete mode 100644 src/main/java/ru/practicum/shareit/item/CommentRepository.java delete mode 100644 src/main/java/ru/practicum/shareit/item/Item.java delete mode 100644 src/main/java/ru/practicum/shareit/item/ItemController.java delete mode 100644 src/main/java/ru/practicum/shareit/item/ItemDto.java delete mode 100644 src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java delete mode 100644 src/main/java/ru/practicum/shareit/item/ItemDtoShort.java delete mode 100644 src/main/java/ru/practicum/shareit/item/ItemMapper.java delete mode 100644 src/main/java/ru/practicum/shareit/item/ItemRepository.java delete mode 100644 src/main/java/ru/practicum/shareit/item/ItemService.java delete mode 100644 src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java delete mode 100644 src/main/java/ru/practicum/shareit/request/ItemRequestController.java delete mode 100644 src/main/java/ru/practicum/shareit/request/ItemRequestDto.java delete mode 100644 src/main/java/ru/practicum/shareit/request/Request.java delete mode 100644 src/main/java/ru/practicum/shareit/user/User.java delete mode 100644 src/main/java/ru/practicum/shareit/user/UserController.java delete mode 100644 src/main/java/ru/practicum/shareit/user/UserDto.java delete mode 100644 src/main/java/ru/practicum/shareit/user/UserDtoOutput.java delete mode 100644 src/main/java/ru/practicum/shareit/user/UserDtoShort.java delete mode 100644 src/main/java/ru/practicum/shareit/user/UserMapper.java delete mode 100644 src/main/java/ru/practicum/shareit/user/UserRepository.java delete mode 100644 src/main/java/ru/practicum/shareit/user/UserService.java delete mode 100644 src/main/java/ru/practicum/shareit/user/UserServiceImpl.java delete mode 100644 src/main/java/ru/practicum/shareit/validation/CreateObject.java delete mode 100644 src/main/java/ru/practicum/shareit/validation/UpdateObject.java delete mode 100644 src/main/resources/application.yaml delete mode 100644 src/main/resources/schema.sql diff --git a/src/main/java/ru/practicum/shareit/ShareItApp.java b/src/main/java/ru/practicum/shareit/ShareItApp.java deleted file mode 100644 index a10a87d..0000000 --- a/src/main/java/ru/practicum/shareit/ShareItApp.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.practicum.shareit; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -public class ShareItApp { - - public static void main(String[] args) { - SpringApplication.run(ShareItApp.class, args); - } - -} diff --git a/src/main/java/ru/practicum/shareit/booking/Booking.java b/src/main/java/ru/practicum/shareit/booking/Booking.java deleted file mode 100644 index ccfe012..0000000 --- a/src/main/java/ru/practicum/shareit/booking/Booking.java +++ /dev/null @@ -1,41 +0,0 @@ -package ru.practicum.shareit.booking; - -import jakarta.persistence.*; -import lombok.*; -import ru.practicum.shareit.item.Item; -import ru.practicum.shareit.user.User; - -import java.time.LocalDateTime; - -@Getter -@Setter -@ToString -@RequiredArgsConstructor -@AllArgsConstructor -@EqualsAndHashCode(of = {"id"}) -@Entity -@Table(name = "bookings") -public class Booking { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(name = "start_date", nullable = false) - private LocalDateTime start; - - @Column(name = "end_date", nullable = false) - private LocalDateTime end; - - @ManyToOne - @JoinColumn(name = "item_id", nullable = false) - private Item item; - - @ManyToOne - @JoinColumn(name = "booker_id", nullable = false) - private User booker; - - @Column(length = 10) - @Enumerated(EnumType.STRING) - private BookingStatus status; - -} diff --git a/src/main/java/ru/practicum/shareit/booking/BookingController.java b/src/main/java/ru/practicum/shareit/booking/BookingController.java deleted file mode 100644 index 40df0bc..0000000 --- a/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ /dev/null @@ -1,86 +0,0 @@ -package ru.practicum.shareit.booking; - -import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@RestController -@RequestMapping(path = "/bookings") -@RequiredArgsConstructor -public class BookingController { - - private final BookingService bookingService; - - /** - * • Добавление нового запроса на бронирование. Запрос может быть создан любым пользователем, - * а затем подтверждён владельцем вещи. Эндпоинт — POST /bookings. - * После создания запрос находится в статусе WAITING — «ожидает подтверждения». - */ - @PostMapping - public BookingDtoOutput addBooking(@RequestHeader("X-Sharer-User-Id") Long bookerId, - @Valid @RequestBody BookingDtoInput bookingDto) { - return BookingMapper.toBookingDtoOutput(bookingService.addBooking(bookerId, bookingDto)); - } - - /** - * • Подтверждение или отклонение запроса на бронирование. Может быть выполнено только владельцем вещи. - * Затем статус бронирования становится либо APPROVED, либо REJECTED. - * Эндпоинт — PATCH /bookings/{bookingId}?approved={approved}, параметр approved может принимать - * значения true или false. - * - * @param ownerId ID владельца вещи. - * @param bookingId ID брони. - * @param approved True - подтверждено, False - отклонено. - * @return Обновленная бронь. - */ - @PatchMapping("/{bookingId}") - public BookingDtoOutput updateByOwner(@RequestHeader("X-Sharer-User-Id") Long ownerId, - @PathVariable Long bookingId, - @RequestParam Boolean approved) { - return BookingMapper.toBookingDtoOutput(bookingService.updateBooking(ownerId, bookingId, approved)); - } - - /** - * • Получение данных о конкретном бронировании (включая его статус). - * Может быть выполнено либо автором бронирования, либо владельцем вещи, - * к которой относится бронирование. - * Эндпоинт — GET /bookings/{bookingId}. - */ - @GetMapping("/{bookingId}") - public BookingDtoOutput getWithStatusById(@RequestHeader("X-Sharer-User-Id") Long userId, - @PathVariable Long bookingId) { - return BookingMapper.toBookingDtoOutput(bookingService.getWithStatusById(userId, bookingId)); - } - - /** - * • Получение списка всех бронирований текущего пользователя. - * Эндпоинт — GET /bookings?state={state}. - * Параметр state необязательный и по умолчанию равен ALL (англ. «все»). - * Также он может принимать значения CURRENT (англ. «текущие»), PAST (англ. «завершённые»), - * FUTURE (англ. «будущие»), WAITING (англ. «ожидающие подтверждения»), REJECTED (англ. «отклонённые»). - * Бронирования должны возвращаться отсортированными по дате от более новых к более старым. - */ - @GetMapping - public List getByUserId(@RequestHeader("X-Sharer-User-Id") Long userId, - @RequestParam(value = "state", - defaultValue = "ALL", required = false) String state) { - return bookingService.getByUserId(userId, state) - .stream().map(BookingMapper::toBookingDtoOutput).toList(); - } - - /** - * • Получение списка бронирований для всех вещей текущего пользователя. - * Эндпоинт — GET /bookings/owner?state={state}. - * Этот запрос имеет смысл для владельца хотя бы одной вещи. - * Работа параметра state аналогична его работе в предыдущем сценарии. - */ - @GetMapping("/owner") - public List getByOwnerId(@RequestHeader("X-Sharer-User-Id") Long userId, - @RequestParam(value = "state", defaultValue = "ALL", - required = false) String state) { - return bookingService.getByOwnerId(userId, state) - .stream().map(BookingMapper::toBookingDtoOutput).toList(); - } -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/booking/BookingDto.java b/src/main/java/ru/practicum/shareit/booking/BookingDto.java deleted file mode 100644 index 07632f8..0000000 --- a/src/main/java/ru/practicum/shareit/booking/BookingDto.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.practicum.shareit.booking; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.shareit.item.Item; -import ru.practicum.shareit.user.User; - -import java.time.LocalDateTime; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class BookingDto { - - private Long id; - - private LocalDateTime start; - - private LocalDateTime end; - - private Item item; - - private User booker; - - private BookingStatus status; - -} diff --git a/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java b/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java deleted file mode 100644 index b39e330..0000000 --- a/src/main/java/ru/practicum/shareit/booking/BookingDtoInput.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.practicum.shareit.booking; - -import jakarta.validation.constraints.FutureOrPresent; -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.shareit.validation.CreateObject; - -import java.time.LocalDateTime; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class BookingDtoInput { - - @NotNull(groups = {CreateObject.class}, message = "При создании брони должна быть информация о вещи.") - private Long itemId; - - @FutureOrPresent(groups = {CreateObject.class}, message = "Дата не должна быть в прошлом") - @NotNull(groups = {CreateObject.class}, message = "Дата не должна быть пустой") - private LocalDateTime start; - - @FutureOrPresent(groups = {CreateObject.class}, message = "Дата не должна быть в прошлом") - @NotNull(groups = {CreateObject.class}, message = "Дата не должна быть пустой") - private LocalDateTime end; - -} diff --git a/src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java b/src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java deleted file mode 100644 index c3b3476..0000000 --- a/src/main/java/ru/practicum/shareit/booking/BookingDtoOutput.java +++ /dev/null @@ -1,30 +0,0 @@ -package ru.practicum.shareit.booking; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.shareit.item.ItemDtoShort; -import ru.practicum.shareit.user.UserDtoShort; - -import java.time.LocalDateTime; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class BookingDtoOutput { - - private Long id; - - private LocalDateTime start; - - private LocalDateTime end; - - private ItemDtoShort item; - - private UserDtoShort booker; - - @JsonProperty("status") - private BookingStatus status; - -} diff --git a/src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java b/src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java deleted file mode 100644 index de9f8b7..0000000 --- a/src/main/java/ru/practicum/shareit/booking/BookingDtoShort.java +++ /dev/null @@ -1,24 +0,0 @@ -package ru.practicum.shareit.booking; - -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class BookingDtoShort { - - private Long id; - - private LocalDateTime start; - - private LocalDateTime end; - - @JsonProperty("status") - private BookingStatus status; - -} diff --git a/src/main/java/ru/practicum/shareit/booking/BookingMapper.java b/src/main/java/ru/practicum/shareit/booking/BookingMapper.java deleted file mode 100644 index adf3f3d..0000000 --- a/src/main/java/ru/practicum/shareit/booking/BookingMapper.java +++ /dev/null @@ -1,65 +0,0 @@ -package ru.practicum.shareit.booking; - -import ru.practicum.shareit.item.ItemMapper; -import ru.practicum.shareit.user.UserMapper; - -public class BookingMapper { - - public static BookingDtoOutput toBookingDtoOutput(Booking booking) { - if (booking == null) { - return null; - } - BookingDtoOutput bookingDto = new BookingDtoOutput(); - - bookingDto.setId(booking.getId()); - bookingDto.setStart(booking.getStart()); - bookingDto.setEnd(booking.getEnd()); - bookingDto.setItem(ItemMapper.toItemDtoShort(booking.getItem())); - bookingDto.setBooker(UserMapper.toUserDtoShort(booking.getBooker())); - bookingDto.setStatus(booking.getStatus()); - - return bookingDto; - } - - public static BookingDtoShort toBookingDtoShort(Booking booking) { - if (booking == null) { - return null; - } - BookingDtoShort bookingDto = new BookingDtoShort(); - - bookingDto.setId(booking.getId()); - bookingDto.setStart(booking.getStart()); - bookingDto.setEnd(booking.getEnd()); - bookingDto.setStatus(booking.getStatus()); - - return bookingDto; - } - - public static Booking toBooking(BookingDto bookingDto) { - Booking booking = new Booking(); - - if (bookingDto.getId() != null) { - if (bookingDto.getId() > 0) { - booking.setId(bookingDto.getId()); - } - } - if (bookingDto.getStart() != null) { - booking.setStart(bookingDto.getStart()); - } - if (bookingDto.getEnd() != null) { - booking.setEnd(bookingDto.getEnd()); - } - if (bookingDto.getItem() != null) { - booking.setItem(bookingDto.getItem()); - } - if (bookingDto.getBooker() != null) { - booking.setBooker(bookingDto.getBooker()); - } - if (bookingDto.getStatus() != null) { - booking.setStatus(bookingDto.getStatus()); - } - - return booking; - } - -} diff --git a/src/main/java/ru/practicum/shareit/booking/BookingRepository.java b/src/main/java/ru/practicum/shareit/booking/BookingRepository.java deleted file mode 100644 index 72ad8ac..0000000 --- a/src/main/java/ru/practicum/shareit/booking/BookingRepository.java +++ /dev/null @@ -1,32 +0,0 @@ -package ru.practicum.shareit.booking; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; -import ru.practicum.shareit.user.User; - -import java.time.LocalDateTime; -import java.util.List; - -@Repository -public interface BookingRepository extends JpaRepository { - - List findAllByBookerOrderByStartDesc(User bookerFromDb); - - List findAllByBookerAndStartBeforeAndEndAfterOrderByStartDesc(User bookerFromDb, LocalDateTime nowDateTime, LocalDateTime nowDateTime1); - - List findAllByBookerAndEndIsBeforeOrderByStartDesc(User bookerFromDb, LocalDateTime nowDateTime); - - List findAllByBookerAndStartIsAfterOrderByStartDesc(User bookerFromDb, LocalDateTime nowDateTime); - - List findAllByBookerAndStatusEqualsOrderByStartDesc(User bookerFromDb, BookingState bookingState); - - List findAllByItem_OwnerOrderByStartDesc(User bookerFromDb); - - List findAllByItem_OwnerAndStartIsBeforeAndEndIsAfterOrderByStartDesc(User bookerFromDb, LocalDateTime nowDateTime, LocalDateTime nowDateTime1); - - List findAllByItem_OwnerAndEndIsBeforeOrderByStartDesc(User bookerFromDb, LocalDateTime nowDateTime); - - List findAllByItem_OwnerAndStartIsAfterOrderByStartDesc(User bookerFromDb, LocalDateTime nowDateTime); - - List findAllByItem_OwnerAndStatusEqualsOrderByStartDesc(User bookerFromDb, BookingState bookingState); -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/booking/BookingService.java b/src/main/java/ru/practicum/shareit/booking/BookingService.java deleted file mode 100644 index 9a731d5..0000000 --- a/src/main/java/ru/practicum/shareit/booking/BookingService.java +++ /dev/null @@ -1,16 +0,0 @@ -package ru.practicum.shareit.booking; - -import java.util.List; - -public interface BookingService { - - Booking addBooking(Long bookerId, BookingDtoInput bookingDto); - - Booking updateBooking(Long ownerId, Long bookingId, Boolean approved); - - Booking getWithStatusById(Long userId, Long bookingId); - - List getByUserId(Long userId, String state); - - List getByOwnerId(Long userId, String state); -} diff --git a/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java b/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java deleted file mode 100644 index 991eba2..0000000 --- a/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java +++ /dev/null @@ -1,280 +0,0 @@ -package ru.practicum.shareit.booking; - -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import ru.practicum.shareit.exception.NotFoundException; -import ru.practicum.shareit.exception.RestrictedAccessException; -import ru.practicum.shareit.exception.ValidationException; -import ru.practicum.shareit.item.Item; -import ru.practicum.shareit.item.ItemRepository; -import ru.practicum.shareit.user.User; -import ru.practicum.shareit.user.UserRepository; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; - -@Service -@RequiredArgsConstructor -@Slf4j -public class BookingServiceImpl implements BookingService { - private final BookingRepository bookingRepository; - private final ItemRepository itemRepository; - private final UserRepository userRepository; - - /** - * Создание брони в БД. - * - * @param bookerId пользователь, пытающийся забронировать вещь. - * @param inputBookingDto создаваемая бронь. - * @return бронь из БД. - */ - @Override - public Booking addBooking(Long bookerId, BookingDtoInput inputBookingDto) { - Item itemFromDB = itemRepository.findById(inputBookingDto.getItemId()) - .orElseThrow(() -> new NotFoundException("При создании бронирования не найдена " + - "вещь с ID = " + inputBookingDto.getItemId() + " в БД.")); - BookingDto bookingDto = new BookingDto(); - bookingDto.setStart(inputBookingDto.getStart()); - bookingDto.setEnd(inputBookingDto.getEnd()); - bookingDto.setItem(itemFromDB); - User bookerFromDb = userRepository.findById(bookerId) - .orElseThrow(() -> new NotFoundException("При " + - "создании бронирования не найден пользователь с ID = " + bookerId + " в БД.")); - validateBooking(bookingDto, itemFromDB, bookerFromDb); - if (!itemFromDB.getAvailable()) { - throw new ValidationException("Вещь нельзя забронировать, поскольку available = false."); - } - bookingDto.setStatus(BookingStatus.WAITING); - bookingDto.setItem(itemFromDB); - bookingDto.setBooker(bookerFromDb); - Booking result = bookingRepository.save(BookingMapper.toBooking(bookingDto)); - log.info("Создано бронирование с ID = [ {} ].", result.getId()); - return result; - } - - /** - * Обновить бронь в БД. - * - * @param ownerId хозяин вещи. - * @param bookingId ID брони. - * @param approved True - подтверждение со стороны хозяина вещи, - * False - отклонено хозяином вещи. - * @return обновлённая бронь. - */ - @Override - public Booking updateBooking(Long ownerId, Long bookingId, Boolean approved) { - Booking bookingFromBd = bookingRepository.findById(bookingId).orElseThrow(() -> new NotFoundException( - "При обновлении бронирования не найдено бронирование с ID = '" + bookingId + "' в БД.")); - if (bookingFromBd.getStatus().equals(BookingStatus.APPROVED) && approved) { - String message = "Данное бронирование уже было обработано и имеет статус '" - + bookingFromBd.getStatus() + "'."; - log.info(message); - throw new ValidationException(message); - } - User ownerFromDb = userRepository.findById(ownerId).orElseThrow(() -> new RestrictedAccessException("При " + - "обновлении бронирования не найден пользователь с ID = '" + ownerId + "' в БД.")); - List items = ownerFromDb.getItems(); - for (Item i : ownerFromDb.getItems()) { - if (i.getId().equals(bookingFromBd.getItem().getId())) { - bookingFromBd.setStatus(approved ? BookingStatus.APPROVED : BookingStatus.REJECTED); - Booking result = bookingRepository.save(bookingFromBd); - log.info("Бронирование с ID = [ {} ] обновлено.", bookingId); - return result; - } - } - String message = "При обновлении брони у хозяина вещи эта вещь не найдена. Ошибка в запросе."; - log.info(message); - throw new NotFoundException(message); - } - - /** - * • Получение данных о конкретном бронировании (включая его статус). - * Может быть выполнено либо автором бронирования, либо владельцем вещи, - * к которой относится бронирование. - * - * @param userId ID пользователя, делающего запрос. - * @param bookingId ID брони. - */ - @Override - public Booking getWithStatusById(Long userId, Long bookingId) { - Booking booking = bookingRepository.findById(bookingId) - .orElseThrow(() -> new NotFoundException("Бронирование с ID = '" + bookingId - + "не найдено в БД при его получении.")); - if (userId.equals(booking.getBooker().getId()) || userId.equals(booking.getItem().getOwner().getId())) { - return booking; - } - throw new NotFoundException("Ошибка при получении брони с ID = '" + bookingId - + "'. Пользователь с ID = '" + userId - + "' не является ни хозяином, ни пользователем, забронировавшим вещь."); - } - - /** - * • Получение списка всех бронирований текущего пользователя. - * Параметр state необязательный и по умолчанию равен ALL (англ. «все»). - * Также он может принимать значения CURRENT (англ. «текущие»), PAST (англ. «завершённые»), - * FUTURE (англ. «будущие»), WAITING (англ. «ожидающие подтверждения»), REJECTED (англ. «отклонённые»). - * Бронирования должны возвращаться отсортированными по дате от более новых к более старым. - * - * @param userId ID пользователя. - * @param state статус бронирования. - */ - @Override - public List getByUserId(Long userId, String state) { - final LocalDateTime nowDateTime = LocalDateTime.now(); - BookingState bookingState; - - if (state.isBlank()) { - bookingState = BookingState.ALL; - } else { - try { - bookingState = BookingState.valueOf(state); - } catch (IllegalArgumentException ex) { - throw new ValidationException("Неизвестное состояние бронирования."); - } - } - User bookerFromDb = userRepository.findById(userId).orElseThrow(() -> new NotFoundException("При " + - "получении списка бронирований не найден пользователь (арендующий) с ID = " + userId + " в БД.")); - List result = new ArrayList<>(); - - switch (bookingState) { - case ALL: { - result = bookingRepository.findAllByBookerOrderByStartDesc(bookerFromDb); - break; - } - case CURRENT: { - result = bookingRepository.findAllByBookerAndStartBeforeAndEndAfterOrderByStartDesc( - bookerFromDb, nowDateTime, nowDateTime); - break; - } - case PAST: { - result = bookingRepository.findAllByBookerAndEndIsBeforeOrderByStartDesc( - bookerFromDb, nowDateTime); - break; - } - case FUTURE: { - result = bookingRepository.findAllByBookerAndStartIsAfterOrderByStartDesc( - bookerFromDb, nowDateTime); - break; - } - case WAITING: { - result = bookingRepository.findAllByBookerAndStatusEqualsOrderByStartDesc( - bookerFromDb, BookingState.WAITING); - break; - } - case REJECTED: { - result = bookingRepository.findAllByBookerAndStatusEqualsOrderByStartDesc( - bookerFromDb, BookingState.REJECTED); - break; - } - default: { - throw new ValidationException("Неизвестное состояние бронирования."); - } - } - - return result; - } - - /** - * • Получение списка бронирований для всех вещей текущего пользователя, то есть хозяина вещей. - * - * @param userId ID хозяина вещей. - * @param state Параметр state необязательный и по умолчанию равен ALL (англ. «все»). - * Также он может принимать значения CURRENT (англ. «текущие»), PAST (англ. «завершённые»), - * FUTURE (англ. «будущие»), WAITING (англ. «ожидающие подтверждения»), - * REJECTED (англ. «отклонённые»). - * @return Бронирования должны возвращаться отсортированными по дате от более новых к более старым. - */ - @Override - public List getByOwnerId(Long userId, String state) { - final LocalDateTime nowDateTime = LocalDateTime.now(); - BookingState bookingState; - - try { - bookingState = BookingState.valueOf(state); - } catch (IllegalArgumentException ex) { - throw new ValidationException("Неизвестное состояние бронирования."); - } - User bookerFromDb = userRepository.findById(userId).orElseThrow(() -> new NotFoundException("При " + - "получении списка бронирований не найден хозяин с ID = " + userId + " в БД.")); - List result = new ArrayList<>(); - - switch (bookingState) { - case ALL: { - result = bookingRepository.findAllByItem_OwnerOrderByStartDesc(bookerFromDb); - System.out.println(result); - break; - } - case CURRENT: { - result = bookingRepository.findAllByItem_OwnerAndStartIsBeforeAndEndIsAfterOrderByStartDesc( - bookerFromDb, nowDateTime, nowDateTime); - break; - } - case PAST: { - result = bookingRepository.findAllByItem_OwnerAndEndIsBeforeOrderByStartDesc( - bookerFromDb, nowDateTime); - break; - } - case FUTURE: { - result = bookingRepository.findAllByItem_OwnerAndStartIsAfterOrderByStartDesc( - bookerFromDb, nowDateTime); - break; - } - case WAITING: { - result = bookingRepository.findAllByItem_OwnerAndStatusEqualsOrderByStartDesc( - bookerFromDb, BookingState.WAITING); - break; - } - case REJECTED: { - result = bookingRepository.findAllByItem_OwnerAndStatusEqualsOrderByStartDesc( - bookerFromDb, BookingState.REJECTED); - break; - } - default: { - throw new ValidationException("Неизвестное состояние бронирования."); - } - } - - return result; - } - - /** - * Проверка при создании бронирования вещи. - * - * @param bookingDto бронь. - * @param item вещь. - * @param booker пользователь. - */ - private void validateBooking(BookingDto bookingDto, Item item, User booker) { - if (item.getOwner().equals(booker)) { - String message = "Создать бронь на свою вещь нельзя."; - log.info(message); - throw new ValidationException(message); - } - - if (bookingDto.getStart().equals(bookingDto.getEnd())) { - String message = "Начало и окончание бронирования не может быть одним и тем же временем."; - log.info(message); - throw new ValidationException(message); - } - - if (bookingDto.getEnd().isBefore(bookingDto.getStart())) { - String message = "Окончание бронирования не может быть раньше его начала."; - log.info(message); - throw new ValidationException(message); - } - - List bookings = item.getBookings(); - if (!bookings.isEmpty()) { - for (Booking b : bookings) { - if (!(b.getEnd().isBefore(bookingDto.getStart()) || - b.getStart().isAfter(bookingDto.getStart()))) { - String message = "Найдено пересечение дат бронирования на вещь с name = " + item.getName() + "."; - log.debug(message); - throw new ValidationException(message); - } - } - } - } -} diff --git a/src/main/java/ru/practicum/shareit/booking/BookingState.java b/src/main/java/ru/practicum/shareit/booking/BookingState.java deleted file mode 100644 index 75be8b4..0000000 --- a/src/main/java/ru/practicum/shareit/booking/BookingState.java +++ /dev/null @@ -1,5 +0,0 @@ -package ru.practicum.shareit.booking; - -public enum BookingState { - ALL, CURRENT, PAST, FUTURE, WAITING, REJECTED; -} diff --git a/src/main/java/ru/practicum/shareit/booking/BookingStatus.java b/src/main/java/ru/practicum/shareit/booking/BookingStatus.java deleted file mode 100644 index 22928de..0000000 --- a/src/main/java/ru/practicum/shareit/booking/BookingStatus.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.shareit.booking; - -public enum BookingStatus { - WAITING, - APPROVED, - REJECTED -} diff --git a/src/main/java/ru/practicum/shareit/exception/DataConflictException.java b/src/main/java/ru/practicum/shareit/exception/DataConflictException.java deleted file mode 100644 index 0ef9caf..0000000 --- a/src/main/java/ru/practicum/shareit/exception/DataConflictException.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.shareit.exception; - -public class DataConflictException extends RuntimeException { - public DataConflictException(String message) { - super(message); - } -} 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 eb16ec6..0000000 --- a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java +++ /dev/null @@ -1,84 +0,0 @@ -package ru.practicum.shareit.exception; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.validation.BindingResult; -import org.springframework.validation.ObjectError; -import org.springframework.web.bind.MethodArgumentNotValidException; -import org.springframework.web.bind.MissingRequestHeaderException; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestControllerAdvice; - -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -@Slf4j -@RestControllerAdvice -public class ErrorHandler { - @ExceptionHandler - @ResponseStatus(HttpStatus.BAD_REQUEST) - public ErrorResponse handleValidationException(final ValidationException e) { - log.error("{} - {}", HttpStatus.BAD_REQUEST, e.getMessage()); - return new ErrorResponse(e.getMessage()); - } - - @ExceptionHandler - @ResponseStatus(HttpStatus.CONFLICT) - public ErrorResponse handleDataConflictException(final DataConflictException e) { - log.error("{} - {}", HttpStatus.CONFLICT, e.getMessage()); - return new ErrorResponse(e.getMessage()); - } - - @ExceptionHandler - @ResponseStatus(HttpStatus.FORBIDDEN) - public ErrorResponse handleRestrictedAccessException(final RestrictedAccessException e) { - log.error("{} - {}", HttpStatus.FORBIDDEN, e.getMessage()); - return new ErrorResponse(e.getMessage()); - } - - @ExceptionHandler - @ResponseStatus(HttpStatus.BAD_REQUEST) - public ErrorResponse handleMethodArgumentNotValidException(final MethodArgumentNotValidException e) { - BindingResult bindingResult = e.getBindingResult(); - List allErrors = bindingResult.getAllErrors(); - String defaultMessage = allErrors.stream() - .map(error -> Objects.requireNonNull(error.getDefaultMessage())) - .collect(Collectors.joining(", ")); - log.error("{} - {}", HttpStatus.BAD_REQUEST, defaultMessage); - return new ErrorResponse(defaultMessage); - } - - @ExceptionHandler - @ResponseStatus(HttpStatus.NOT_FOUND) - public ErrorResponse handleNotFoundException(final NotFoundException e) { - log.error("{} - {}", HttpStatus.NOT_FOUND, e.getMessage()); - return new ErrorResponse(e.getMessage()); - } - - @ExceptionHandler - @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) - public ErrorResponse handleRunTimeException(final RuntimeException e) { - log.error("{} - {}", HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage()); - return new ErrorResponse(e.getMessage()); - } - - @ExceptionHandler(MissingRequestHeaderException.class) - @ResponseStatus(HttpStatus.BAD_REQUEST) - public ErrorResponse handleMissingRequestHeaderException(MissingRequestHeaderException e) { - log.error("{} - {}", HttpStatus.BAD_REQUEST, e.getMessage()); - return new ErrorResponse(e.getMessage()); - } - - @Getter - public static class ErrorResponse { - private final String error; - - public ErrorResponse(String error) { - this.error = error; - } - - } -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/exception/NotFoundException.java b/src/main/java/ru/practicum/shareit/exception/NotFoundException.java deleted file mode 100644 index 98610d2..0000000 --- a/src/main/java/ru/practicum/shareit/exception/NotFoundException.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.shareit.exception; - -public class NotFoundException extends RuntimeException { - public NotFoundException(String message) { - super(message); - } -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java b/src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java deleted file mode 100644 index 09df8b9..0000000 --- a/src/main/java/ru/practicum/shareit/exception/RestrictedAccessException.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.shareit.exception; - -public class RestrictedAccessException extends RuntimeException { - public RestrictedAccessException(String message) { - super(message); - } -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/exception/ValidationException.java b/src/main/java/ru/practicum/shareit/exception/ValidationException.java deleted file mode 100644 index 59043da..0000000 --- a/src/main/java/ru/practicum/shareit/exception/ValidationException.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.shareit.exception; - -public class ValidationException extends RuntimeException { - public ValidationException(String message) { - super(message); - } -} diff --git a/src/main/java/ru/practicum/shareit/item/Comment.java b/src/main/java/ru/practicum/shareit/item/Comment.java deleted file mode 100644 index 107457e..0000000 --- a/src/main/java/ru/practicum/shareit/item/Comment.java +++ /dev/null @@ -1,36 +0,0 @@ -package ru.practicum.shareit.item; - -import jakarta.persistence.*; -import lombok.*; -import ru.practicum.shareit.user.User; - -import java.time.LocalDateTime; - -@Getter -@Setter -@RequiredArgsConstructor -@AllArgsConstructor -@ToString -@EqualsAndHashCode(of = {"id"}) -@Entity -@Table(name = "comments") -public class Comment { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(length = 250) - private String text; - - @ManyToOne - @JoinColumn(name = "item_id", nullable = false) - private Item item; - - @ManyToOne - @JoinColumn(name = "user_id") - private User user; - - @Column(name = "created", nullable = false) - private LocalDateTime created; - -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/item/CommentDto.java b/src/main/java/ru/practicum/shareit/item/CommentDto.java deleted file mode 100644 index bd2daa4..0000000 --- a/src/main/java/ru/practicum/shareit/item/CommentDto.java +++ /dev/null @@ -1,30 +0,0 @@ -package ru.practicum.shareit.item; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.shareit.user.User; -import ru.practicum.shareit.validation.CreateObject; - -import java.time.LocalDateTime; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class CommentDto { - - private Long id; - - @NotNull(groups = {CreateObject.class}, message = "комментарий должен быть указан.") - @NotBlank(groups = {CreateObject.class}, message = "Комментарий не может быть пустым.") - private String text; - - private Item item; - - private User user; - - private LocalDateTime created; - -} diff --git a/src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java b/src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java deleted file mode 100644 index 2e76ce5..0000000 --- a/src/main/java/ru/practicum/shareit/item/CommentDtoOutput.java +++ /dev/null @@ -1,24 +0,0 @@ -package ru.practicum.shareit.item; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class CommentDtoOutput { - - private Long id; - - private String text; - - private ItemDtoShort item; - - private String authorName; - - private LocalDateTime created; - -} diff --git a/src/main/java/ru/practicum/shareit/item/CommentDtoShort.java b/src/main/java/ru/practicum/shareit/item/CommentDtoShort.java deleted file mode 100644 index 2e4fa12..0000000 --- a/src/main/java/ru/practicum/shareit/item/CommentDtoShort.java +++ /dev/null @@ -1,24 +0,0 @@ -package ru.practicum.shareit.item; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -import java.time.LocalDateTime; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class CommentDtoShort { - - private Long id; - - private String text; - - private ItemDtoShort item; - - private String authorName; - - private LocalDateTime created; - -} diff --git a/src/main/java/ru/practicum/shareit/item/CommentMapper.java b/src/main/java/ru/practicum/shareit/item/CommentMapper.java deleted file mode 100644 index 60d09b9..0000000 --- a/src/main/java/ru/practicum/shareit/item/CommentMapper.java +++ /dev/null @@ -1,84 +0,0 @@ -package ru.practicum.shareit.item; - -import java.time.LocalDateTime; - -public class CommentMapper { - - public static CommentDto toCommentDto(Comment comment) { - if (comment == null) { - return null; - } - CommentDto commentDto = new CommentDto(); - - commentDto.setId(comment.getId()); - commentDto.setText(comment.getText()); - commentDto.setItem(comment.getItem()); - commentDto.setUser(comment.getUser()); - commentDto.setCreated(comment.getCreated()); - - return commentDto; - } - - public static CommentDtoOutput toCommentDtoOutput(Comment comment) { - if (comment == null) { - return null; - } - CommentDtoOutput commentDto = new CommentDtoOutput(); - - commentDto.setId(comment.getId()); - commentDto.setText(comment.getText()); - commentDto.setItem(ItemMapper.toItemDtoShort(comment.getItem())); - commentDto.setAuthorName(comment.getUser().getName()); - commentDto.setCreated(comment.getCreated()); - - return commentDto; - } - - public static CommentDtoShort toCommentDtoShort(Comment comment) { - if (comment == null) { - return null; - } - CommentDtoShort commentDto = new CommentDtoShort(); - - commentDto.setId(comment.getId()); - commentDto.setText(comment.getText()); - commentDto.setItem(ItemMapper.toItemDtoShort(comment.getItem())); - commentDto.setAuthorName(comment.getUser().getName()); - commentDto.setCreated(comment.getCreated()); - - return commentDto; - } - - public static Comment toComment(CommentDto commentDto) { - Comment comment = new Comment(); - - if (commentDto.getId() != null) { - if (commentDto.getId() > 0) { - comment.setId(commentDto.getId()); - } - } - if (commentDto.getText() != null) { - if (!commentDto.getText().trim().isEmpty()) { - comment.setText(commentDto.getText().trim()); - } - } - if (commentDto.getItem().getId() != null) { - if (commentDto.getItem().getId() > 0) { - comment.setItem(commentDto.getItem()); - } - } - if (commentDto.getUser().getId() != null) { - if (commentDto.getUser().getId() > 0) { - comment.setUser(commentDto.getUser()); - } - } - if (commentDto.getCreated() != null) { - if (!commentDto.getCreated().isBefore(LocalDateTime.now())) { - comment.setCreated(commentDto.getCreated()); - } - } - - return comment; - } - -} diff --git a/src/main/java/ru/practicum/shareit/item/CommentRepository.java b/src/main/java/ru/practicum/shareit/item/CommentRepository.java deleted file mode 100644 index 55f2ae5..0000000 --- a/src/main/java/ru/practicum/shareit/item/CommentRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package ru.practicum.shareit.item; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface CommentRepository extends JpaRepository { - -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/item/Item.java b/src/main/java/ru/practicum/shareit/item/Item.java deleted file mode 100644 index 99e8100..0000000 --- a/src/main/java/ru/practicum/shareit/item/Item.java +++ /dev/null @@ -1,52 +0,0 @@ -package ru.practicum.shareit.item; - -import com.fasterxml.jackson.annotation.JsonIdentityInfo; -import com.fasterxml.jackson.annotation.ObjectIdGenerators; -import jakarta.persistence.*; -import lombok.*; -import ru.practicum.shareit.booking.Booking; -import ru.practicum.shareit.request.Request; -import ru.practicum.shareit.user.User; - -import java.util.List; - -@Getter -@Setter -@RequiredArgsConstructor -@AllArgsConstructor -@ToString -@EqualsAndHashCode(of = {"id"}) -@JsonIdentityInfo( - generator = ObjectIdGenerators.PropertyGenerator.class, - property = "id") -@Entity -@Table(name = "items") -public class Item { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(length = 100) - private String name; - - @Column(length = 250) - private String description; - - @Column(name = "is_available", nullable = false) - private Boolean available; - - @ManyToOne - @JoinColumn(name = "owner_id", nullable = false) - private User owner; - - @OneToOne - @JoinColumn(name = "request_id") - private Request request; - - @OneToMany(mappedBy = "item") - private List comments; - - @OneToMany(mappedBy = "item") - private List bookings; - -} \ 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 affabdd..0000000 --- a/src/main/java/ru/practicum/shareit/item/ItemController.java +++ /dev/null @@ -1,62 +0,0 @@ -package ru.practicum.shareit.item; - -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; -import ru.practicum.shareit.validation.CreateObject; -import ru.practicum.shareit.validation.UpdateObject; - -import java.util.List; - -@RestController -@RequestMapping("/items") -@RequiredArgsConstructor -public class ItemController { - private final ItemService itemService; - - @PostMapping - @ResponseStatus(HttpStatus.CREATED) - public ItemDtoOutput addItem(@Validated(CreateObject.class) @RequestBody ItemDto itemDto, - @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { - return ItemMapper.toItemDtoOutput(itemService.addItem(idUser, itemDto), idUser); - } - - @PatchMapping("/{idItem}") - public ItemDtoOutput updateItem(@PathVariable Long idItem, - @Validated(UpdateObject.class) @RequestBody ItemDto itemDto, - @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { - return ItemMapper.toItemDtoOutput(itemService.updateItem(idUser, idItem, itemDto), idUser); - } - - @GetMapping("/{idItem}") - public ItemDtoOutput getItemById(@PathVariable Long idItem, - @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { - return ItemMapper.toItemDtoOutput(itemService.getItemById(idItem), idUser); - } - - @GetMapping - public List getAllItemsByUser(@RequestHeader(value = "X-Sharer-User-Id") Long idUser) { - return itemService.getAllItems(idUser).stream() - .map(item -> ItemMapper.toItemDtoOutput(item, idUser)).toList(); - } - - @DeleteMapping("/{idItem}") - public void removeItem(@PathVariable Long idItem, - @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { - itemService.removeItem(idUser, idItem); - } - - @GetMapping("/search") - public List searchItemsByText(@RequestParam String text, - @RequestHeader(value = "X-Sharer-User-Id") Long idUser) { - return itemService.searchItems(text).stream() - .map(item -> ItemMapper.toItemDtoOutput(item, idUser)).toList(); - } - - @PostMapping("/{itemId}/comment") - public CommentDtoOutput addCommentToItem(@RequestHeader("X-Sharer-User-Id") Long userId, - @PathVariable Long itemId, @RequestBody CommentDto commentDto) { - return CommentMapper.toCommentDtoOutput((itemService.saveComment(userId, itemId, commentDto))); - } -} diff --git a/src/main/java/ru/practicum/shareit/item/ItemDto.java b/src/main/java/ru/practicum/shareit/item/ItemDto.java deleted file mode 100644 index 2fb9d3e..0000000 --- a/src/main/java/ru/practicum/shareit/item/ItemDto.java +++ /dev/null @@ -1,45 +0,0 @@ -package ru.practicum.shareit.item; - -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.shareit.booking.Booking; -import ru.practicum.shareit.request.Request; -import ru.practicum.shareit.user.User; -import ru.practicum.shareit.validation.CreateObject; - -import java.util.List; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class ItemDto { - - private Long id; - - @NotNull(groups = {CreateObject.class}, message = "Название вещи должно быть указано.") - @NotBlank(groups = {CreateObject.class}, message = "Название вещи не может быть пустым.") - private String name; - - @NotNull(groups = {CreateObject.class}, message = "Описание вещи должно быть указано.") - @NotBlank(groups = {CreateObject.class}, message = "Описание вещи не может быть пустым.") - private String description; - - @NotNull(groups = {CreateObject.class}, message = "Доступность вещи должна быть указана.") - private Boolean available; - - private User owner; - - private Request request; - - private List comments; - - private List bookings; - - private Booking lastBooking; - - private Booking nextBooking; - -} diff --git a/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java b/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java deleted file mode 100644 index 36ff82c..0000000 --- a/src/main/java/ru/practicum/shareit/item/ItemDtoOutput.java +++ /dev/null @@ -1,37 +0,0 @@ -package ru.practicum.shareit.item; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.shareit.booking.BookingDtoShort; -import ru.practicum.shareit.request.Request; -import ru.practicum.shareit.user.UserDtoShort; - -import java.util.List; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class ItemDtoOutput { - - private Long id; - - private String name; - - private String description; - - private Boolean available; - - private UserDtoShort owner; - - private Request request; - - private List comments; - - private List bookings; - - private BookingDtoShort lastBooking; - - private BookingDtoShort nextBooking; - -} diff --git a/src/main/java/ru/practicum/shareit/item/ItemDtoShort.java b/src/main/java/ru/practicum/shareit/item/ItemDtoShort.java deleted file mode 100644 index 8dc3627..0000000 --- a/src/main/java/ru/practicum/shareit/item/ItemDtoShort.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.practicum.shareit.item; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class ItemDtoShort { - - private Long id; - - private String name; - - private String description; - - private Boolean available; - -} diff --git a/src/main/java/ru/practicum/shareit/item/ItemMapper.java b/src/main/java/ru/practicum/shareit/item/ItemMapper.java deleted file mode 100644 index d076fa1..0000000 --- a/src/main/java/ru/practicum/shareit/item/ItemMapper.java +++ /dev/null @@ -1,159 +0,0 @@ -package ru.practicum.shareit.item; - -import ru.practicum.shareit.booking.Booking; -import ru.practicum.shareit.booking.BookingMapper; -import ru.practicum.shareit.booking.BookingStatus; -import ru.practicum.shareit.user.UserMapper; - -import java.time.LocalDateTime; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Optional; - -public class ItemMapper { - - public static ItemDto toItemDto(Item item) { - if (item == null) { - return null; - } - ItemDto itemDto = new ItemDto(); - - itemDto.setId(item.getId()); - itemDto.setName(item.getName()); - itemDto.setDescription(item.getDescription()); - itemDto.setAvailable(item.getAvailable()); - itemDto.setOwner(item.getOwner()); - itemDto.setRequest(item.getRequest()); - itemDto.setComments(item.getComments()); - itemDto.setBookings(item.getBookings()); - - return itemDto; - } - - public static ItemDtoOutput toItemDtoOutput(Item item, Long userId) { - if (item == null) { - return null; - } - ItemDtoOutput itemDto = new ItemDtoOutput(); - - itemDto.setId(item.getId()); - itemDto.setName(item.getName()); - itemDto.setDescription(item.getDescription()); - itemDto.setAvailable(item.getAvailable()); - itemDto.setOwner(UserMapper.toUserDtoShort(item.getOwner())); - itemDto.setRequest(item.getRequest()); - List comments = Optional.ofNullable(item.getComments()).orElse(Collections.emptyList()); - if (!comments.isEmpty()) { - itemDto.setComments(comments.stream().map(CommentMapper::toCommentDtoShort).toList()); - } else { - itemDto.setComments(Collections.emptyList()); - } - List bookings = Optional.ofNullable(item.getBookings()).orElse(Collections.emptyList()); - if (!bookings.isEmpty()) { - if (itemDto.getOwner().getId().equals(userId)) { - itemDto.setBookings(bookings.stream().map(BookingMapper::toBookingDtoShort).toList()); - itemDto.setLastBooking(BookingMapper.toBookingDtoShort(findLastBooking(bookings))); - itemDto.setNextBooking(BookingMapper.toBookingDtoShort(findNextBooking(bookings))); - } else { - itemDto.setBookings(Collections.emptyList()); - itemDto.setLastBooking(null); - itemDto.setNextBooking(null); - } - } else { - itemDto.setBookings(Collections.emptyList()); - itemDto.setLastBooking(null); - itemDto.setNextBooking(null); - } - - return itemDto; - } - - public static ItemDtoShort toItemDtoShort(Item item) { - if (item == null) { - return null; - } - ItemDtoShort itemDto = new ItemDtoShort(); - - itemDto.setId(item.getId()); - itemDto.setName(item.getName()); - itemDto.setDescription(item.getDescription()); - itemDto.setAvailable(item.getAvailable()); - - return itemDto; - } - - public static Item toItem(ItemDto itemDto) { - Item item = new Item(); - - if (itemDto.getId() != null) { - if (itemDto.getId() > 0) { - item.setId(itemDto.getId()); - } - } - if (itemDto.getName() != null) { - item.setName(itemDto.getName().trim()); - } - if (itemDto.getDescription() != null) { - item.setDescription(itemDto.getDescription().trim()); - } - if (itemDto.getAvailable() != null) { - item.setAvailable(itemDto.getAvailable()); - } - if (itemDto.getOwner().getId() != null) { - if (itemDto.getOwner().getId() > 0) { - item.setOwner(itemDto.getOwner()); - } - } - if (itemDto.getRequest() != null) { - if (itemDto.getRequest().getId() > 0) { - item.setRequest(itemDto.getRequest()); - } - } - if (itemDto.getComments() != null) { - if (!itemDto.getComments().isEmpty()) { - item.setComments(itemDto.getComments()); - } - } - if (itemDto.getBookings() != null) { - if (!itemDto.getBookings().isEmpty()) { - item.setBookings(itemDto.getBookings()); - } - } - - return item; - } - - /** - * Метод поиска первой аренды после текущего момента времени. - * - * @param bookings список бронирований. - * @return следующее бронирование после текущего момента времени. - */ - private static Booking findNextBooking(List bookings) { - return Optional.ofNullable(bookings) - .filter(list -> !list.isEmpty()) - .flatMap(list -> list.stream() - .filter(b -> b.getStart().isAfter(LocalDateTime.now())) - .filter(b -> b.getStatus().equals(BookingStatus.APPROVED) || - b.getStatus().equals(BookingStatus.WAITING)) - .min(Comparator.comparing(Booking::getStart))) - .orElse(null); - } - - /** - * Метод поиска последней аренды до текущего момента времени. - * - * @param bookings список бронирований. - * @return последнее бронирование до текущего момента времени. - */ - private static Booking findLastBooking(List bookings) { - return Optional.ofNullable(bookings) - .filter(list -> !list.isEmpty()) - .flatMap(list -> list.stream() - .filter(b -> b.getEnd().isBefore(LocalDateTime.now())) - .filter(b -> b.getStatus().equals(BookingStatus.APPROVED)) - .max(Comparator.comparing(Booking::getEnd))) - .orElse(null); - } -} 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 7a9bd2d..0000000 --- a/src/main/java/ru/practicum/shareit/item/ItemRepository.java +++ /dev/null @@ -1,23 +0,0 @@ -package ru.practicum.shareit.item; - -import org.springframework.data.domain.Sort; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; -import ru.practicum.shareit.user.User; - -import java.util.List; - -@Repository -public interface ItemRepository extends JpaRepository { - List findAllByOwner(User owner, Sort name); - - List findByNameContainingIgnoreCaseAndAvailableTrueOrDescriptionContainingIgnoreCaseAndAvailableTrue( - String name, String description, Sort by); - - default List findAllByText(String text) { - return - findByNameContainingIgnoreCaseAndAvailableTrueOrDescriptionContainingIgnoreCaseAndAvailableTrue( - text, text, Sort.by("name")); - } - -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/item/ItemService.java b/src/main/java/ru/practicum/shareit/item/ItemService.java deleted file mode 100644 index ba5bf8a..0000000 --- a/src/main/java/ru/practicum/shareit/item/ItemService.java +++ /dev/null @@ -1,21 +0,0 @@ -package ru.practicum.shareit.item; - -import java.util.List; - -public interface ItemService { - - Item addItem(Long idUser, ItemDto itemDto); - - Item updateItem(Long idUser, Long idItem, ItemDto itemDto); - - Item getItemById(Long idItem); - - void removeItem(Long idUser, Long idItem); - - List getAllItems(Long idUser); - - List searchItems(String text); - - Comment saveComment(Long userId, Long itemId, CommentDto inputCommentDto); - -} diff --git a/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java b/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java deleted file mode 100644 index edaf54d..0000000 --- a/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java +++ /dev/null @@ -1,218 +0,0 @@ -package ru.practicum.shareit.item; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.Sort; -import org.springframework.stereotype.Service; -import ru.practicum.shareit.booking.Booking; -import ru.practicum.shareit.exception.NotFoundException; -import ru.practicum.shareit.exception.RestrictedAccessException; -import ru.practicum.shareit.exception.ValidationException; -import ru.practicum.shareit.user.User; -import ru.practicum.shareit.user.UserMapper; -import ru.practicum.shareit.user.UserRepository; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; - -@Slf4j -@Service -public class ItemServiceImpl implements ItemService { - - private final ItemRepository itemRepository; - private final UserRepository userRepository; - private final CommentRepository commentRepository; - - public ItemServiceImpl(ItemRepository itemRepository, UserRepository userRepository, CommentRepository commentRepository) { - this.itemRepository = itemRepository; - this.userRepository = userRepository; - this.commentRepository = commentRepository; - } - - /** - * Метод для добавления новой вещи. - * - * @param idUser идентификатор пользователя - * @param itemDto объект ItemDto, содержащий данные новой вещи - * @return объект Item, содержащий данные добавленной вещи - * @throws ValidationException если идентификатор пользователя не указан - * @throws NotFoundException если пользователь с указанным id не найден в БД - */ - @Override - public Item addItem(Long idUser, ItemDto itemDto) { - if (userRepository.findById(idUser).isEmpty()) { - String error = "Пользователь с id [ " + idUser + " ] не найден в БД при добавлении вещи."; - log.info(error); - throw new NotFoundException(error); - } - itemDto.setOwner(userRepository.findById(idUser).get()); - Item result = ItemMapper.toItem(itemDto); - itemRepository.save(result); - log.info("Добавлена вещь [ {} ] пользователем [ {} ]", result.getId(), idUser); - return result; - } - - /** - * Метод для обновления данных вещи. - * - * @param idUser идентификатор пользователя - * @param idItem идентификатор вещи - * @param itemDto объект ItemDto, содержащий новые данные вещи - * @return объект Item, содержащий обновленные данные вещи - * @throws ValidationException если идентификатор пользователя не указан - * @throws NotFoundException если пользователь или вещь с указанным id не найдены в БД - * @throws RestrictedAccessException если пользователь не является владельцем вещи - */ - @Override - public Item updateItem(Long idUser, Long idItem, ItemDto itemDto) { - if (userRepository.findById(idUser).isEmpty()) { - String error = "Пользователь с id [ " + idUser + " ] не найден в БД при изменение данных вещи."; - log.info(error); - throw new NotFoundException(error); - } - Optional oldItem = itemRepository.findById(idItem); - if (oldItem.isEmpty()) { - String error = "Вещь с id [ " + idItem + " ] не найдена в БД при изменение данных вещи."; - log.info(error); - throw new NotFoundException(error); - } - if (!idUser.equals(oldItem.get().getOwner().getId())) { - String error = "Пользователь с id [ " + idUser + " ] не является владельцем вещи с id [ " + idItem + " ]."; - log.info(error); - throw new RestrictedAccessException(error); - } - itemDto.setId(idItem); - itemDto.setOwner(oldItem.get().getOwner()); - itemDto.setRequest(oldItem.get().getRequest()); - if (itemDto.getName() == null) { - itemDto.setName(oldItem.get().getName()); - } - if (itemDto.getDescription() == null) { - itemDto.setDescription(oldItem.get().getDescription()); - } - if (itemDto.getAvailable() == null) { - itemDto.setAvailable(oldItem.get().getAvailable()); - } - Item newItem = ItemMapper.toItem(itemDto); - itemRepository.save(newItem); - log.info("Обновлены данные вещи [ {} ]", ItemMapper.toItemDtoShort(newItem)); - return newItem; - } - - /** - * Метод для получения данных вещи по идентификатору. - * - * @param idItem идентификатор вещи - * @return объект Item, содержащий данные вещи - * @throws NotFoundException если вещь с указанным id не найдена в БД - */ - @Override - public Item getItemById(Long idItem) { - Optional oldItem = itemRepository.findById(idItem); - if (oldItem.isEmpty()) { - String error = "Вещь с id [ " + idItem + " ] не найдена в БД при запросе данных вещи."; - log.info(error); - throw new NotFoundException(error); - } - log.info("По вещи с id [ {} ] успешно получены данные.", idItem); - return oldItem.get(); - } - - /** - * Метод для удаления вещи по идентификатору. - * - * @param idUser идентификатор пользователя - * @param idItem идентификатор вещи - * @throws ValidationException если идентификатор пользователя не указан - * @throws NotFoundException если пользователь или вещь с указанным id не найдены в БД - */ - @Override - public void removeItem(Long idUser, Long idItem) { - if (userRepository.findById(idUser).isEmpty()) { - String error = "Пользователь с id [ " + idUser + " ] не найден в БД при удалении данных о вещи."; - log.info(error); - throw new NotFoundException(error); - } - if (itemRepository.findById(idItem).isEmpty()) { - String error = "Вещь с id [ " + idItem + " ] не найдена в БД при удалении данных о вещи."; - log.info(error); - throw new NotFoundException(error); - } - itemRepository.delete(itemRepository.findById(idItem).get()); - log.info("Вещь с id [ {} ] успешно удалена.", idItem); - } - - /** - * Метод для получения списка всех вещей пользователя. - * - * @param idUser идентификатор пользователя - * @return список всех вещей пользователя, отсортированных по имени - * @throws ValidationException если идентификатор пользователя не указан - * @throws NotFoundException если пользователь с указанным id не найден в БД - */ - @Override - public List getAllItems(Long idUser) { - Optional user = userRepository.findById(idUser); - if (user.isEmpty()) { - String error = "Пользователь с id [ " + idUser + " ] не найден в БД при изменение данных вещи."; - log.info(error); - throw new NotFoundException(error); - } - List result = itemRepository.findAllByOwner(user.get(), Sort.by("name")); - log.info("Получен список всех вещей пользователя [ {} ] : [ {} ]", UserMapper.toUserDtoShort(user.get()), result.size()); - return result; - } - - /** - * Метод для поиска вещей по поисковому запросу. - * - * @param text поисковый запрос - * @return список вещей, в названии или описании которых присутствует поисковый запрос - */ - @Override - public List searchItems(String text) { - if (text.isBlank()) { - return List.of(); - } - List result = itemRepository.findAllByText(text); - log.info("Получен список вещей по поисковому запросу [ {} ] : [ {} ]", text, result.size()); - return result; - } - - /** - * Добавить комментарий к вещи пользователем, действительно бравшим вещь в аренду. - * - * @param bookerId ID пользователя, добавляющего комментарий. - * @param itemId ID вещи, которой оставляется комментарий. - */ - @Override - public Comment saveComment(Long bookerId, Long itemId, CommentDto commentDto) { - - User userFromBd = userRepository.findById(bookerId).orElseThrow(() -> - new NotFoundException("Ошибка при сохранении комментария к вещи с ID = " + itemId - + " пользователя с ID = " + bookerId + " в БД. В БД отсутствует запись о пользователе.")); - Item itemFromBd = itemRepository.findById(itemId).orElseThrow(() -> - new NotFoundException("Ошибка при сохранении комментария к вещи с ID = " + itemId - + " пользователя с ID = " + bookerId + " в БД. В БД отсутствует запись о вещи.")); - List bookings = itemFromBd.getBookings(); - boolean isBooker = false; - for (Booking b : bookings) { - if (b.getBooker().getId().equals(bookerId) && b.getEnd().isBefore(LocalDateTime.now())) { - isBooker = true; - break; - } - } - if (!isBooker) { - throw new ValidationException("Ошибка при сохранении комментария к вещи с ID = " + itemId - + " пользователя с ID = " + bookerId + " в БД. Пользователь не арендовал эту вещь."); - } - commentDto.setItem(itemFromBd); - commentDto.setUser(userFromBd); - commentDto.setCreated(LocalDateTime.now()); - Comment result = CommentMapper.toComment(commentDto); - result = commentRepository.save(result); - log.info("Комментарий к вещи успешно добавлен. Данные комментария: {}", CommentMapper.toCommentDtoShort(result)); - return result; - } - -} 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 064e2e9..0000000 --- a/src/main/java/ru/practicum/shareit/request/ItemRequestController.java +++ /dev/null @@ -1,12 +0,0 @@ -package ru.practicum.shareit.request; - -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -/** - * TODO Sprint add-item-requests. - */ -@RestController -@RequestMapping(path = "/requests") -public class ItemRequestController { -} diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequestDto.java b/src/main/java/ru/practicum/shareit/request/ItemRequestDto.java deleted file mode 100644 index 16d55ee..0000000 --- a/src/main/java/ru/practicum/shareit/request/ItemRequestDto.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.practicum.shareit.request; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.shareit.user.User; - -import java.time.LocalDateTime; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class ItemRequestDto { - - private String description; - - private User requestor; - - private LocalDateTime created; -} diff --git a/src/main/java/ru/practicum/shareit/request/Request.java b/src/main/java/ru/practicum/shareit/request/Request.java deleted file mode 100644 index 2e67520..0000000 --- a/src/main/java/ru/practicum/shareit/request/Request.java +++ /dev/null @@ -1,35 +0,0 @@ -package ru.practicum.shareit.request; - -import jakarta.persistence.*; -import lombok.*; -import ru.practicum.shareit.item.Item; -import ru.practicum.shareit.user.User; - -import java.time.LocalDateTime; - -@Getter -@Setter -@RequiredArgsConstructor -@AllArgsConstructor -@ToString -@EqualsAndHashCode(of = {"id"}) -@Entity -@Table(name = "requests") -public class Request { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(length = 250) - private String description; - - @ManyToOne - @JoinColumn(name = "requestor_id", nullable = false) - private User requestor; - - @OneToOne(mappedBy = "request") - private Item requestItem; - - @Column(nullable = false) - private LocalDateTime created; -} diff --git a/src/main/java/ru/practicum/shareit/user/User.java b/src/main/java/ru/practicum/shareit/user/User.java deleted file mode 100644 index 07d790c..0000000 --- a/src/main/java/ru/practicum/shareit/user/User.java +++ /dev/null @@ -1,39 +0,0 @@ -package ru.practicum.shareit.user; - -import jakarta.persistence.*; -import lombok.*; -import ru.practicum.shareit.booking.Booking; -import ru.practicum.shareit.item.Comment; -import ru.practicum.shareit.item.Item; - -import java.util.List; - -@Getter -@Setter -@ToString -@RequiredArgsConstructor -@AllArgsConstructor -@EqualsAndHashCode(of = {"id"}) -@Entity -@Table(name = "users") -public class User { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(length = 100) - private String name; - - @Column(length = 50, unique = true) - private String email; - - @OneToMany(mappedBy = "owner") - private List items; - - @OneToMany(mappedBy = "booker") - private List bookings; - - @OneToMany(mappedBy = "user") - private List comments; - -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/user/UserController.java b/src/main/java/ru/practicum/shareit/user/UserController.java deleted file mode 100644 index 38dff38..0000000 --- a/src/main/java/ru/practicum/shareit/user/UserController.java +++ /dev/null @@ -1,43 +0,0 @@ -package ru.practicum.shareit.user; - -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; -import ru.practicum.shareit.validation.CreateObject; -import ru.practicum.shareit.validation.UpdateObject; - -import java.util.List; - -@RestController -@RequestMapping("/users") -@RequiredArgsConstructor -public class UserController { - private final UserService userService; - - @PostMapping - @ResponseStatus(HttpStatus.CREATED) - public UserDtoOutput addUser(@Validated(CreateObject.class) @RequestBody UserDto userDto) { - return UserMapper.toUserDtoOutput(userService.addUser(userDto)); - } - - @PatchMapping("/{idUser}") - public UserDtoOutput updateUser(@PathVariable Long idUser, @Validated(UpdateObject.class) @RequestBody UserDto userDto) { - return UserMapper.toUserDtoOutput(userService.updateUser(idUser, userDto)); - } - - @GetMapping("/{idUser}") - public UserDtoOutput getUserById(@PathVariable Long idUser) { - return UserMapper.toUserDtoOutput(userService.getUserById(idUser)); - } - - @GetMapping - public List getAllUsers() { - return userService.getAllUsers().stream().map(UserMapper::toUserDtoOutput).toList(); - } - - @DeleteMapping("/{idUser}") - public void removeUser(@PathVariable Long idUser) { - userService.removeUser(idUser); - } -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/user/UserDto.java b/src/main/java/ru/practicum/shareit/user/UserDto.java deleted file mode 100644 index 122a434..0000000 --- a/src/main/java/ru/practicum/shareit/user/UserDto.java +++ /dev/null @@ -1,38 +0,0 @@ -package ru.practicum.shareit.user; - -import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.NotNull; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.shareit.booking.Booking; -import ru.practicum.shareit.item.Comment; -import ru.practicum.shareit.item.Item; -import ru.practicum.shareit.validation.CreateObject; -import ru.practicum.shareit.validation.UpdateObject; - -import java.util.List; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class UserDto { - - private Long id; - - @NotNull(groups = {CreateObject.class}, message = "Имя должно быть указано.") - @NotBlank(groups = {CreateObject.class}, message = "Имя не может быть пустым.") - private String name; - - @NotNull(groups = {CreateObject.class}, message = "Email должен быть указан.") - @Email(groups = {CreateObject.class, UpdateObject.class}, message = "Email должен быть указан корректно.") - private String email; - - private List items; - - private List bookings; - - private List comments; - -} diff --git a/src/main/java/ru/practicum/shareit/user/UserDtoOutput.java b/src/main/java/ru/practicum/shareit/user/UserDtoOutput.java deleted file mode 100644 index fd7a87b..0000000 --- a/src/main/java/ru/practicum/shareit/user/UserDtoOutput.java +++ /dev/null @@ -1,29 +0,0 @@ -package ru.practicum.shareit.user; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; -import ru.practicum.shareit.booking.BookingDtoShort; -import ru.practicum.shareit.item.CommentDtoShort; -import ru.practicum.shareit.item.ItemDtoShort; - -import java.util.List; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class UserDtoOutput { - - private Long id; - - private String name; - - private String email; - - private List items; - - private List bookings; - - private List comments; - -} diff --git a/src/main/java/ru/practicum/shareit/user/UserDtoShort.java b/src/main/java/ru/practicum/shareit/user/UserDtoShort.java deleted file mode 100644 index 7bb1143..0000000 --- a/src/main/java/ru/practicum/shareit/user/UserDtoShort.java +++ /dev/null @@ -1,18 +0,0 @@ -package ru.practicum.shareit.user; - -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NoArgsConstructor; - -@Data -@AllArgsConstructor -@NoArgsConstructor -public class UserDtoShort { - - private Long id; - - private String name; - - private String email; - -} diff --git a/src/main/java/ru/practicum/shareit/user/UserMapper.java b/src/main/java/ru/practicum/shareit/user/UserMapper.java deleted file mode 100644 index 400b2ec..0000000 --- a/src/main/java/ru/practicum/shareit/user/UserMapper.java +++ /dev/null @@ -1,96 +0,0 @@ -package ru.practicum.shareit.user; - -import ru.practicum.shareit.booking.BookingMapper; -import ru.practicum.shareit.item.ItemMapper; -import ru.practicum.shareit.item.CommentMapper; - -import java.util.Collections; -import java.util.Optional; - -public class UserMapper { - - public static UserDto toUserDto(User user) { - if (user == null) { - return null; - } - UserDto userDto = new UserDto(); - - userDto.setId(user.getId()); - userDto.setName(user.getName()); - userDto.setEmail(user.getEmail()); - userDto.setItems(user.getItems()); - userDto.setBookings(user.getBookings()); - userDto.setComments(user.getComments()); - - return userDto; - } - - public static UserDtoOutput toUserDtoOutput(User user) { - if (user == null) { - return null; - } - UserDtoOutput userDto = new UserDtoOutput(); - - userDto.setId(user.getId()); - userDto.setName(user.getName()); - userDto.setEmail(user.getEmail()); - userDto.setItems(Optional.ofNullable(user.getItems()) - .map(items -> items.stream().map(ItemMapper::toItemDtoShort).toList()) - .orElse(Collections.emptyList())); - userDto.setBookings(Optional.ofNullable(user.getBookings()) - .map(bookings -> bookings.stream().map(BookingMapper::toBookingDtoShort).toList()) - .orElse(Collections.emptyList())); - userDto.setComments(Optional.ofNullable(user.getComments()) - .map(comments -> comments.stream().map(CommentMapper::toCommentDtoShort).toList()) - .orElse(Collections.emptyList())); - - return userDto; - } - - public static UserDtoShort toUserDtoShort(User user) { - if (user == null) { - return null; - } - UserDtoShort userDto = new UserDtoShort(); - - userDto.setId(user.getId()); - userDto.setName(user.getName()); - userDto.setEmail(user.getEmail()); - - return userDto; - } - - public static User toUser(UserDto userDto) { - User user = new User(); - - if (userDto.getId() != null) { - if (userDto.getId() > 0) { - user.setId(userDto.getId()); - } - } - if (userDto.getName() != null) { - user.setName(userDto.getName().trim()); - } - if (userDto.getEmail() != null) { - user.setEmail(userDto.getEmail().trim()); - } - if (userDto.getItems() != null) { - if (!userDto.getItems().isEmpty()) { - user.setItems(userDto.getItems()); - } - } - if (userDto.getBookings() != null) { - if (!userDto.getBookings().isEmpty()) { - user.setBookings(userDto.getBookings()); - } - } - if (userDto.getComments() != null) { - if (!userDto.getComments().isEmpty()) { - user.setComments(userDto.getComments()); - } - } - - return user; - } - -} diff --git a/src/main/java/ru/practicum/shareit/user/UserRepository.java b/src/main/java/ru/practicum/shareit/user/UserRepository.java deleted file mode 100644 index 2b0e794..0000000 --- a/src/main/java/ru/practicum/shareit/user/UserRepository.java +++ /dev/null @@ -1,11 +0,0 @@ -package ru.practicum.shareit.user; - -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public interface UserRepository extends JpaRepository { - Optional getUserByEmail(String email); -} diff --git a/src/main/java/ru/practicum/shareit/user/UserService.java b/src/main/java/ru/practicum/shareit/user/UserService.java deleted file mode 100644 index 678b79d..0000000 --- a/src/main/java/ru/practicum/shareit/user/UserService.java +++ /dev/null @@ -1,17 +0,0 @@ -package ru.practicum.shareit.user; - -import java.util.List; - -public interface UserService { - - User addUser(UserDto userDto); - - User updateUser(Long idUser, UserDto userDto); - - User getUserById(Long idUser); - - void removeUser(Long idUser); - - List getAllUsers(); - -} diff --git a/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java b/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java deleted file mode 100644 index 0ca64a0..0000000 --- a/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java +++ /dev/null @@ -1,132 +0,0 @@ -package ru.practicum.shareit.user; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.data.domain.Sort; -import org.springframework.stereotype.Service; -import ru.practicum.shareit.exception.DataConflictException; -import ru.practicum.shareit.exception.NotFoundException; - -import java.util.List; -import java.util.Optional; - -@Slf4j -@Service -public class UserServiceImpl implements UserService { - - private final UserRepository userRepository; - - public UserServiceImpl(UserRepository userRepository) { - this.userRepository = userRepository; - } - - /** - * Метод для добавления нового пользователя. - * - * @param userDto объект UserDto, содержащий данные нового пользователя - * @return объект User, содержащий данные добавленного пользователя - * @throws DataConflictException если пользователь с указанным email уже существует в БД - */ - @Override - public User addUser(UserDto userDto) { - Optional oldUser = userRepository.getUserByEmail(userDto.getEmail()); - if (oldUser.isPresent()) { - String error = "Пользователь с email [ " + userDto.getEmail() + " ] уже существует в БД. " + - "Добавление пользователя невозможно."; - log.error(error); - throw new DataConflictException(error); - } - User result = userRepository.save(UserMapper.toUser(userDto)); - log.info("Добавлен пользователь [ {} ]", result); - return result; - } - - /** - * Метод для обновления данных пользователя. - * - * @param idUser идентификатор пользователя - * @param userDto объект UserDto, содержащий новые данные пользователя - * @return объект User, содержащий обновленные данные пользователя - * @throws DataConflictException если пользователь с указанным email уже существует в БД - * @throws NotFoundException если пользователь с указанным id не найден в БД - */ - @Override - public User updateUser(Long idUser, UserDto userDto) { - Optional oldUser = userRepository.getUserByEmail(userDto.getEmail()); - if (oldUser.isPresent()) { - String error = "Пользователь с email [ " + userDto.getEmail() + " ] уже существует в БД. " + - "Обновление данных пользователя невозможно."; - log.error(error); - throw new DataConflictException(error); - } - userDto.setId(idUser); - oldUser = userRepository.findById(idUser); - if (oldUser.isPresent()) { - if (userDto.getName() == null) { - userDto.setName(oldUser.get().getName()); - } - if (userDto.getEmail() == null) { - userDto.setEmail(oldUser.get().getEmail()); - } - } else { - String error = "Пользователь с id [ " + idUser + " ] не найден в БД при обновлении данных пользователя."; - log.info(error); - throw new NotFoundException(error); - } - User newUser = UserMapper.toUser(userDto); - userRepository.save(newUser); - log.info("Обновлен пользователь [ {} ]", newUser); - return newUser; - } - - - /** - * Метод для получения данных пользователя по идентификатору. - * - * @param idUser идентификатор пользователя - * @return объект User, содержащий данные пользователя - * @throws NotFoundException если пользователь с указанным id не найден в БД - */ - @Override - public User getUserById(Long idUser) { - Optional oldUser = userRepository.findById(idUser); - if (oldUser.isEmpty()) { - String error = "Пользователь с id [ " + idUser + " ] не найден в БД при запросе данных пользователя."; - log.info(error); - throw new NotFoundException(error); - } - log.info("По id [ {} ] успешно получены данные пользователя [ {} ].", idUser, oldUser.get()); - return oldUser.get(); - } - - /** - * Метод для получения списка всех пользователей. - * - * @return список всех пользователей, отсортированных по имени - */ - @Override - public List getAllUsers() { - List result = userRepository.findAll(Sort.by("name")); - log.info("Получен список всех пользователей : [ {} ]", result); - return result; - } - - /** - * Метод для удаления пользователя по идентификатору. - * - * @param idUser идентификатор пользователя - * @throws NotFoundException если пользователь с указанным id не найден в БД - */ - @Override - public void removeUser(Long idUser) { - Optional oldUser = userRepository.findById(idUser); - if (oldUser.isEmpty()) { - String error = "Пользователь с id [ " + idUser + " ] не найден в БД при удалении пользователя."; - log.info(error); - throw new NotFoundException(error); - } - userRepository.delete(oldUser.get()); - log.info("По id [ {} ] успешно удален пользователь.", idUser); - // log.info("По id [ {} ] успешно удален пользователь [ {} ].", idUser, oldUser.get()); - } - -} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/validation/CreateObject.java b/src/main/java/ru/practicum/shareit/validation/CreateObject.java deleted file mode 100644 index db15c62..0000000 --- a/src/main/java/ru/practicum/shareit/validation/CreateObject.java +++ /dev/null @@ -1,4 +0,0 @@ -package ru.practicum.shareit.validation; - -public interface CreateObject { -} diff --git a/src/main/java/ru/practicum/shareit/validation/UpdateObject.java b/src/main/java/ru/practicum/shareit/validation/UpdateObject.java deleted file mode 100644 index 9a3949c..0000000 --- a/src/main/java/ru/practicum/shareit/validation/UpdateObject.java +++ /dev/null @@ -1,4 +0,0 @@ -package ru.practicum.shareit.validation; - -public interface UpdateObject { -} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml deleted file mode 100644 index faf6257..0000000 --- a/src/main/resources/application.yaml +++ /dev/null @@ -1,20 +0,0 @@ -spring: - application.name: shareit - main.banner-mode: off - datasource: - url: jdbc:postgresql://localhost:5432/shareit - driver-class-name: org.postgresql.Driver - username: shareituser - password: 12345678 - jpa: - show-sql: true - hibernate: - ddl-auto: update - properties: - hibernate.jdbc.time_zone: UTC - dialect: org.hibernate.dialect.PostgreSQL95Dialect - format_sql: true - sql: - init: - mode: always - diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql deleted file mode 100644 index 0f68bd1..0000000 --- a/src/main/resources/schema.sql +++ /dev/null @@ -1,44 +0,0 @@ -DROP TABLE IF EXISTS requests CASCADE; -DROP TABLE IF EXISTS comments CASCADE; -DROP TABLE IF EXISTS bookings CASCADE; -DROP TABLE IF EXISTS users CASCADE; -DROP TABLE IF EXISTS items CASCADE; - -CREATE TABLE IF NOT EXISTS users ( - id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - name VARCHAR(100), - email VARCHAR(50) UNIQUE -); - -CREATE TABLE IF NOT EXISTS requests ( - id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - description VARCHAR(250) NOT NULL, - requestor_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE, - created TIMESTAMP WITHOUT TIME ZONE DEFAULT (now()) -); - -CREATE TABLE IF NOT EXISTS items ( - id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - name VARCHAR(100), - description VARCHAR(250), - is_available BOOL DEFAULT FALSE, - owner_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE, - request_id BIGINT DEFAULT NULL REFERENCES requests (id) ON DELETE SET NULL ON UPDATE CASCADE -); - -CREATE TABLE IF NOT EXISTS bookings ( - id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - start_date TIMESTAMP WITHOUT TIME ZONE, - end_date TIMESTAMP WITHOUT TIME ZONE, - item_id BIGINT NOT NULL REFERENCES items (id) ON DELETE CASCADE ON UPDATE CASCADE, - booker_id BIGINT NOT NULL REFERENCES users (id) ON DELETE CASCADE ON UPDATE CASCADE, - status VARCHAR(10) DEFAULT 'WAITING' -); - -CREATE TABLE IF NOT EXISTS comments ( - id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - text VARCHAR(250), - item_id BIGINT NOT NULL REFERENCES items (id) ON DELETE CASCADE ON UPDATE CASCADE, - user_id BIGINT DEFAULT NULL REFERENCES users (id) ON DELETE SET NULL ON UPDATE CASCADE, - created TIMESTAMP WITHOUT TIME ZONE DEFAULT (now()) -); From c2f14e81dbd0e9169a98806484a7d6c326304332 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sun, 27 Apr 2025 00:06:24 +0300 Subject: [PATCH 21/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/ru/practicum/shareit/item/ItemServiceImpl.java | 3 +++ 1 file changed, 3 insertions(+) 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 e939b0e..894a88d 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.item; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Sort; @@ -14,6 +15,7 @@ import ru.practicum.shareit.user.UserMapper; import ru.practicum.shareit.user.UserRepository; +import java.beans.Transient; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; @@ -202,6 +204,7 @@ public List searchItems(String text) { * @param itemId ID вещи, которой оставляется комментарий. */ @Override + @Transactional public Comment saveComment(Long bookerId, Long itemId, CommentDto commentDto) { User userFromBd = userRepository.findById(bookerId).orElseThrow(() -> From c7b025b7baea1cd06bdb1b6b29490f9cc8a9fab5 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sun, 27 Apr 2025 00:08:21 +0300 Subject: [PATCH 22/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0=20=D1=81=D1=82=D0=B8=D0=BB?= =?UTF-8?q?=D0=B5=D0=B9.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java | 1 - 1 file changed, 1 deletion(-) 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 894a88d..f1f18bd 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java @@ -15,7 +15,6 @@ import ru.practicum.shareit.user.UserMapper; import ru.practicum.shareit.user.UserRepository; -import java.beans.Transient; import java.time.LocalDateTime; import java.util.List; import java.util.Optional; From 07654c04536c59ea1fb635679b1ea46971654c69 Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sun, 27 Apr 2025 21:21:45 +0300 Subject: [PATCH 23/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shareit/booking/BookingMapper.java | 2 ++ .../shareit/booking/BookingServiceImpl.java | 3 ++ .../practicum/shareit/item/CommentMapper.java | 3 ++ .../ru/practicum/shareit/item/ItemMapper.java | 28 +++++++++++++++++++ .../shareit/item/ItemRepository.java | 6 ---- .../shareit/item/ItemServiceImpl.java | 27 +++++------------- .../shareit/request/RequestController.java | 3 -- .../shareit/request/RequestMapper.java | 2 ++ .../shareit/request/RequestServiceImpl.java | 2 ++ .../ru/practicum/shareit/user/UserMapper.java | 2 ++ .../shareit/user/UserServiceImpl.java | 5 +++- 11 files changed, 53 insertions(+), 30 deletions(-) diff --git a/server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java b/server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java index adf3f3d..dba6b88 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java @@ -1,8 +1,10 @@ package ru.practicum.shareit.booking; +import org.springframework.stereotype.Component; import ru.practicum.shareit.item.ItemMapper; import ru.practicum.shareit.user.UserMapper; +@Component public class BookingMapper { public static BookingDtoOutput toBookingDtoOutput(Booking booking) { 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 d0f22f3..6d6ab54 100644 --- a/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.booking; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -31,6 +32,7 @@ public class BookingServiceImpl implements BookingService { * @return бронь из БД. */ @Override + @Transactional public Booking addBooking(Long bookerId, BookingDtoInput inputBookingDto) { Item itemFromDB = itemRepository.findById(inputBookingDto.getItemId()) .orElseThrow(() -> new NotFoundException("При создании бронирования не найдена " + @@ -64,6 +66,7 @@ public Booking addBooking(Long bookerId, BookingDtoInput inputBookingDto) { * @return обновлённая бронь. */ @Override + @Transactional public Booking updateBooking(Long ownerId, Long bookingId, Boolean approved) { Booking bookingFromBd = bookingRepository.findById(bookingId).orElseThrow(() -> new NotFoundException( "При обновлении бронирования не найдено бронирование с ID = '" + bookingId + "' в БД.")); diff --git a/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java b/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java index cdced80..4dfc25c 100644 --- a/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java +++ b/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java @@ -1,5 +1,8 @@ package ru.practicum.shareit.item; +import org.springframework.stereotype.Component; + +@Component public class CommentMapper { public static CommentDto toCommentDto(Comment comment) { diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemMapper.java b/server/src/main/java/ru/practicum/shareit/item/ItemMapper.java index 9f81b4e..8416ade 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemMapper.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemMapper.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.item; +import org.springframework.stereotype.Component; import ru.practicum.shareit.booking.Booking; import ru.practicum.shareit.booking.BookingMapper; import ru.practicum.shareit.booking.BookingStatus; @@ -12,6 +13,7 @@ import java.util.List; import java.util.Optional; +@Component public class ItemMapper { public static ItemDto toItemDto(Item item) { @@ -139,6 +141,32 @@ public static Item toItem(ItemDto itemDto) { return item; } + public static ItemDto updateItemDto(ItemDtoInput itemDtoInput, Item oldItem) { + ItemDto itemDto = new ItemDto(); + + itemDto.setId(oldItem.getId()); + itemDto.setOwner(oldItem.getOwner()); + itemDto.setRequest(RequestMapper.toRequestDto(oldItem.getRequest())); + if (itemDtoInput.getName() == null) { + itemDto.setName(oldItem.getName()); + } else { + itemDto.setName(itemDtoInput.getName()); + } + if (itemDtoInput.getDescription() == null) { + itemDto.setDescription(oldItem.getDescription()); + } else { + itemDto.setDescription(itemDtoInput.getDescription()); + } + if (itemDtoInput.getAvailable() == null) { + itemDto.setAvailable(oldItem.getAvailable()); + } else { + itemDto.setAvailable(itemDtoInput.getAvailable()); + } + + return itemDto; + } + + /** * Метод поиска первой аренды после текущего момента времени. * diff --git a/server/src/main/java/ru/practicum/shareit/item/ItemRepository.java b/server/src/main/java/ru/practicum/shareit/item/ItemRepository.java index 7a9bd2d..66cb1ac 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemRepository.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemRepository.java @@ -14,10 +14,4 @@ public interface ItemRepository extends JpaRepository { List findByNameContainingIgnoreCaseAndAvailableTrueOrDescriptionContainingIgnoreCaseAndAvailableTrue( String name, String description, Sort by); - default List findAllByText(String text) { - return - findByNameContainingIgnoreCaseAndAvailableTrueOrDescriptionContainingIgnoreCaseAndAvailableTrue( - text, text, Sort.by("name")); - } - } \ No newline at end of file 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 f1f18bd..aedd320 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java @@ -39,6 +39,7 @@ public class ItemServiceImpl implements ItemService { * @throws NotFoundException если пользователь с указанным id не найден в БД */ @Override + @Transactional public Item addItem(Long idUser, ItemDtoInput inputItemDto) { ItemDto itemDto = new ItemDto(); User user = userRepository.findById(idUser).orElse(null); @@ -74,6 +75,7 @@ public Item addItem(Long idUser, ItemDtoInput inputItemDto) { * @throws RestrictedAccessException если пользователь не является владельцем вещи */ @Override + @Transactional public Item updateItem(Long idUser, Long idItem, ItemDtoInput itemDtoInput) { ItemDto itemDto = new ItemDto(); if (userRepository.findById(idUser).isEmpty()) { @@ -92,25 +94,7 @@ public Item updateItem(Long idUser, Long idItem, ItemDtoInput itemDtoInput) { log.info(error); throw new RestrictedAccessException(error); } - itemDto.setId(idItem); - itemDto.setOwner(oldItem.get().getOwner()); - itemDto.setRequest(RequestMapper.toRequestDto(oldItem.get().getRequest())); - if (itemDtoInput.getName() == null) { - itemDto.setName(oldItem.get().getName()); - } else { - itemDto.setName(itemDtoInput.getName()); - } - if (itemDtoInput.getDescription() == null) { - itemDto.setDescription(oldItem.get().getDescription()); - } else { - itemDto.setDescription(itemDtoInput.getDescription()); - } - if (itemDtoInput.getAvailable() == null) { - itemDto.setAvailable(oldItem.get().getAvailable()); - } else { - itemDto.setAvailable(itemDtoInput.getAvailable()); - } - Item newItem = ItemMapper.toItem(itemDto); + Item newItem = ItemMapper.toItem(ItemMapper.updateItemDto(itemDtoInput, oldItem.get())); itemRepository.save(newItem); log.info("Обновлены данные вещи [ {} ]", ItemMapper.toItemDtoShort(newItem)); return newItem; @@ -144,6 +128,7 @@ public Item getItemById(Long idItem) { * @throws NotFoundException если пользователь или вещь с указанным id не найдены в БД */ @Override + @Transactional public void removeItem(Long idUser, Long idItem) { if (userRepository.findById(idUser).isEmpty()) { String error = "Пользователь с id [ " + idUser + " ] не найден в БД при удалении данных о вещи."; @@ -191,7 +176,9 @@ public List searchItems(String text) { if (text.isBlank()) { return List.of(); } - List result = itemRepository.findAllByText(text); + List result = itemRepository + .findByNameContainingIgnoreCaseAndAvailableTrueOrDescriptionContainingIgnoreCaseAndAvailableTrue( + text, text, Sort.by("name")); log.info("Получен список вещей по поисковому запросу [ {} ] : [ {} ]", text, result.size()); return result; } diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestController.java b/server/src/main/java/ru/practicum/shareit/request/RequestController.java index d3c49ec..ff5ef33 100644 --- a/server/src/main/java/ru/practicum/shareit/request/RequestController.java +++ b/server/src/main/java/ru/practicum/shareit/request/RequestController.java @@ -5,9 +5,6 @@ import java.util.List; -/** - * TODO Sprint add-item-requests. - */ @RestController @RequestMapping(path = "/requests") @RequiredArgsConstructor diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestMapper.java b/server/src/main/java/ru/practicum/shareit/request/RequestMapper.java index 9608ce2..ac4d840 100644 --- a/server/src/main/java/ru/practicum/shareit/request/RequestMapper.java +++ b/server/src/main/java/ru/practicum/shareit/request/RequestMapper.java @@ -1,7 +1,9 @@ package ru.practicum.shareit.request; +import org.springframework.stereotype.Component; import ru.practicum.shareit.item.ItemMapper; +@Component public class RequestMapper { public static RequestDto toRequestDto(Request request) { diff --git a/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java b/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java index 6677b00..b4afae8 100644 --- a/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.request; +import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -27,6 +28,7 @@ public class RequestServiceImpl implements RequestService { * @throws NotFoundException если пользователь с указанным идентификатором не найден в базе данных */ @Override + @Transactional public Request addRequest(Long requestorId, RequestDtoInput requestDto) { User requestor = new User(); Request request = new Request(); diff --git a/server/src/main/java/ru/practicum/shareit/user/UserMapper.java b/server/src/main/java/ru/practicum/shareit/user/UserMapper.java index 400b2ec..154c1f3 100644 --- a/server/src/main/java/ru/practicum/shareit/user/UserMapper.java +++ b/server/src/main/java/ru/practicum/shareit/user/UserMapper.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.user; +import org.springframework.stereotype.Component; import ru.practicum.shareit.booking.BookingMapper; import ru.practicum.shareit.item.ItemMapper; import ru.practicum.shareit.item.CommentMapper; @@ -7,6 +8,7 @@ import java.util.Collections; import java.util.Optional; +@Component public class UserMapper { public static UserDto toUserDto(User user) { diff --git a/server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java b/server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java index 0ca64a0..21b7cf2 100644 --- a/server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.user; +import jakarta.transaction.Transactional; import lombok.extern.slf4j.Slf4j; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; @@ -27,6 +28,7 @@ public UserServiceImpl(UserRepository userRepository) { * @throws DataConflictException если пользователь с указанным email уже существует в БД */ @Override + @Transactional public User addUser(UserDto userDto) { Optional oldUser = userRepository.getUserByEmail(userDto.getEmail()); if (oldUser.isPresent()) { @@ -50,6 +52,7 @@ public User addUser(UserDto userDto) { * @throws NotFoundException если пользователь с указанным id не найден в БД */ @Override + @Transactional public User updateUser(Long idUser, UserDto userDto) { Optional oldUser = userRepository.getUserByEmail(userDto.getEmail()); if (oldUser.isPresent()) { @@ -117,6 +120,7 @@ public List getAllUsers() { * @throws NotFoundException если пользователь с указанным id не найден в БД */ @Override + @Transactional public void removeUser(Long idUser) { Optional oldUser = userRepository.findById(idUser); if (oldUser.isEmpty()) { @@ -126,7 +130,6 @@ public void removeUser(Long idUser) { } userRepository.delete(oldUser.get()); log.info("По id [ {} ] успешно удален пользователь.", idUser); - // log.info("По id [ {} ] успешно удален пользователь [ {} ].", idUser, oldUser.get()); } } \ No newline at end of file From c0524c46a39e4a2717fff659c6444fcf9e78737a Mon Sep 17 00:00:00 2001 From: Sergey Filippovskikh <116564864+SergikF@users.noreply.github.com> Date: Sun, 27 Apr 2025 21:29:21 +0300 Subject: [PATCH 24/24] =?UTF-8?q?=D0=92=D1=8B=D0=BF=D0=BE=D0=BB=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=2016=20=D1=81=D0=BF=D1=80=D0=B8=D0=BD?= =?UTF-8?q?=D1=82=D0=B0.=20=D0=9A=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B0.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java | 1 - 1 file changed, 1 deletion(-) 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 aedd320..22f5ccd 100644 --- a/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java @@ -77,7 +77,6 @@ public Item addItem(Long idUser, ItemDtoInput inputItemDto) { @Override @Transactional public Item updateItem(Long idUser, Long idItem, ItemDtoInput itemDtoInput) { - ItemDto itemDto = new ItemDto(); if (userRepository.findById(idUser).isEmpty()) { String error = "Пользователь с id [ " + idUser + " ] не найден в БД при изменение данных вещи."; log.info(error);