diff --git a/.run/ShareitGateway.run.xml b/.run/ShareitGateway.run.xml new file mode 100644 index 0000000..2b5e51e --- /dev/null +++ b/.run/ShareitGateway.run.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/.run/ShareitServer.run.xml b/.run/ShareitServer.run.xml new file mode 100644 index 0000000..80abfa8 --- /dev/null +++ b/.run/ShareitServer.run.xml @@ -0,0 +1,15 @@ + + + + \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..6b5e23c --- /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/postgres + - SPRING_DATASOURCE_USERNAME=user + - SPRING_DATASOURCE_PASSWORD=12345 + + db: + image: postgres:16.1 + container_name: postgres + ports: + - "6541:5432" + environment: + - POSTGRES_PASSWORD=12345 + - POSTGRES_USER=user + - 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..833b0ed --- /dev/null +++ b/gateway/pom.xml @@ -0,0 +1,92 @@ + + + 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 + + + ru.practicum + shareit-server + 0.0.1-SNAPSHOT + compile + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + org.apache.maven.plugins + maven-compiler-plugin + + + + org.projectlombok + lombok + 1.18.38 + + + + + + + + + + \ 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..e659d11 --- /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..70e68db --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java @@ -0,0 +1,57 @@ +package ru.practicum.shareit.booking; + +import java.util.Map; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.util.DefaultUriBuilderFactory; + +import ru.practicum.shareit.booking.dto.BookingRequestDto; +import ru.practicum.shareit.booking.dto.BookingState; +import ru.practicum.shareit.client.BaseClient; + +@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, BookingRequestDto requestDto) { + return post("", userId, requestDto); + } + + public ResponseEntity getBooking(Long bookingId, Long userId) { + return get("/" + bookingId, userId); + } + + public ResponseEntity bookingApprove(long bookingId, long userId, boolean approved) { + + return patch("/" + bookingId + "?approved=" + approved, userId); + } + + public ResponseEntity getBookingsByUser(Long userId, String state) { + Map parameters = Map.of( + "state", state + ); + return get("", userId, parameters); + } + + public ResponseEntity getBookingsByOwner(Long userId, BookingState state) { + Map parameters = Map.of( + "state", state + ); + return get("/owner", userId, parameters); + } +} 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..0a16bfb --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -0,0 +1,47 @@ +package ru.practicum.shareit.booking; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.booking.dto.BookingRequestDto; +import ru.practicum.shareit.booking.dto.BookingState; + +@RestController +@RequestMapping(path = "/bookings") +@RequiredArgsConstructor +public class BookingController { + private final BookingClient bookingService; + public static final String USER_ID = "X-Sharer-User-Id"; + + @PostMapping + public ResponseEntity addBooking(@RequestBody @Valid BookingRequestDto booking, + @RequestHeader(USER_ID) Long bookerId) { + return bookingService.addBooking(bookerId, booking); + } + + @PatchMapping("/{bookingId}") + public ResponseEntity bookingApprove(@PathVariable Long bookingId, + @RequestHeader (USER_ID) Long ownerId, + @RequestParam Boolean approved) { + return bookingService.bookingApprove(bookingId, ownerId, approved); + } + + @GetMapping("/{bookingId}") + public ResponseEntity getBooking(@PathVariable Long bookingId, + @RequestHeader(USER_ID) Long userId) { + return bookingService.getBooking(bookingId, userId); + } + + @GetMapping + public ResponseEntity getBookingsByUser(@RequestHeader (USER_ID) Long userId, + @RequestParam(name = "state", defaultValue = "all") String state) { + return bookingService.getBookingsByUser(userId, state); + } + + @GetMapping("/owner") + public ResponseEntity getBookingsByOwner(@RequestHeader (USER_ID) Long userId, + @RequestParam(name = "state", defaultValue = "all") BookingState state) { + return bookingService.getBookingsByOwner(userId, state); + } +} diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingRequestDto.java b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingRequestDto.java similarity index 87% rename from src/main/java/ru/practicum/shareit/booking/dto/BookingRequestDto.java rename to gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingRequestDto.java index 0e94f98..213047a 100644 --- a/src/main/java/ru/practicum/shareit/booking/dto/BookingRequestDto.java +++ b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingRequestDto.java @@ -6,7 +6,6 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import ru.practicum.shareit.booking.model.BookingStatus; import java.time.LocalDateTime; @@ -19,6 +18,7 @@ public class BookingRequestDto { @NotNull @Future + private LocalDateTime start; @NotNull @@ -30,5 +30,5 @@ public class BookingRequestDto { private Long bookerId; - private BookingStatus status; + private BookingState status; } diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingResponseDto.java b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingResponseDto.java new file mode 100644 index 0000000..096c6eb --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingResponseDto.java @@ -0,0 +1,30 @@ +package ru.practicum.shareit.booking.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.item.dto.ItemBookerDto; +import ru.practicum.shareit.user.dto.UserBookingDto; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BookingResponseDto { + + @JsonProperty("id") + private Long bookingId; + + private LocalDateTime start; + + private LocalDateTime end; + + private ItemBookerDto item; + + private UserBookingDto booker; + + private BookingState status; + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingState.java b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingState.java new file mode 100644 index 0000000..9726a10 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingState.java @@ -0,0 +1,36 @@ +package ru.practicum.shareit.booking.dto; + +import lombok.Getter; + +import java.util.Optional; + +@Getter +public enum BookingState { + // Все + ALL("ALL"), + // Текущие + CURRENT("CURRENT"), + // Будущие + FUTURE("FUTURE"), + // Завершенные + PAST("PAST"), + // Отклоненные + REJECTED("REJECTED"), + // Ожидающие подтверждения + WAITING("WAITING"),; + + private final String value; + + BookingState(String value) { + this.value = value; + } + + public static Optional from(String stringState) { + for (BookingState state : values()) { + if (state.name().equalsIgnoreCase(stringState)) { + return Optional.of(state); + } + } + return Optional.empty(); + } +} \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java new file mode 100644 index 0000000..1413765 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java @@ -0,0 +1,125 @@ +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, @Nullable Map parameters) { + return get(path,null, parameters); + } + + 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/src/main/java/ru/practicum/shareit/exception/AnotherUserException.java b/gateway/src/main/java/ru/practicum/shareit/exception/AnotherUserException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/AnotherUserException.java rename to gateway/src/main/java/ru/practicum/shareit/exception/AnotherUserException.java diff --git a/src/main/java/ru/practicum/shareit/exception/DuplicatedDataException.java b/gateway/src/main/java/ru/practicum/shareit/exception/DuplicatedDataException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/DuplicatedDataException.java rename to gateway/src/main/java/ru/practicum/shareit/exception/DuplicatedDataException.java diff --git a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java similarity index 67% rename from src/main/java/ru/practicum/shareit/exception/ErrorHandler.java rename to gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java index d118158..61d8fd9 100644 --- a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java +++ b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -24,6 +24,20 @@ public ErrorResponse handleNotFoundException(final NotFoundException e) { return new ErrorResponse(e.getMessage(), HttpStatus.NOT_FOUND); } + @ExceptionHandler(ValidationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleValidationException(final ValidationException e) { + log.error("Not found: {}", e.getMessage()); + return new ErrorResponse(e.getMessage(), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(AnotherUserException.class) + @ResponseStatus(HttpStatus.FORBIDDEN) + public ErrorResponse handleAnotherUserException(final AnotherUserException e) { + log.error("Not found: {}", e.getMessage()); + return new ErrorResponse(e.getMessage(), HttpStatus.FORBIDDEN); + } + @ExceptionHandler @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ErrorResponse handleException(final RuntimeException e) { diff --git a/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java b/gateway/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/ErrorResponse.java rename to gateway/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java diff --git a/src/main/java/ru/practicum/shareit/exception/NotFoundException.java b/gateway/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 gateway/src/main/java/ru/practicum/shareit/exception/NotFoundException.java 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/ItemClient.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java new file mode 100644 index 0000000..5c0730f --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java @@ -0,0 +1,56 @@ +package ru.practicum.shareit.item; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.util.DefaultUriBuilderFactory; +import ru.practicum.shareit.client.BaseClient; +import ru.practicum.shareit.item.dto.CommentRequestDto; +import ru.practicum.shareit.item.dto.ItemRequestDto; + +import java.util.Map; + +@Service +public class ItemClient extends BaseClient { + private static final String API_PREFIX = "/items"; + + @Autowired + public ItemClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { + super( + builder + .uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()) + .build() + ); + } + + public ResponseEntity create(Long userId, ItemRequestDto item) { + return post("", userId, item); + } + + public ResponseEntity update(Long itemId, ItemRequestDto item, Long ownerId) { + return patch("/" + itemId, ownerId, item); + } + + public ResponseEntity getItemById(Long itemId, Long ownerId) { + return get("/" + itemId, ownerId); + } + + public ResponseEntity getAllItemsForOwner(Long ownerId) { + return get("", ownerId); + } + + public ResponseEntity searchByText(String text) { + Map parameters = Map.of( + "text", text + ); + return get("/search", parameters); + } + + public ResponseEntity addComment(Long commentatorId, CommentRequestDto comment, Long itemId) { + return post("/" + itemId + "/comment", commentatorId, comment); + } +} 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..05240a8 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -0,0 +1,61 @@ +package ru.practicum.shareit.item; + + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.item.dto.*; + + + +@RestController +@RequestMapping("/items") +@RequiredArgsConstructor +@Slf4j +@Validated +public class ItemController { + + public static final String OWNER_ID = "X-Sharer-User-Id"; + private final ItemClient itemService; + + @PostMapping + public ResponseEntity create(@RequestHeader(OWNER_ID) Long ownerId, + @RequestBody @Valid ItemRequestDto item) { + return itemService.create(ownerId, item); + } + + @PatchMapping("/{itemId}") + public ResponseEntity update(@RequestBody ItemRequestDto item, + @PathVariable Long itemId, + @RequestHeader(OWNER_ID) Long ownerId) { + return itemService.update(itemId, item, ownerId); + } + + @GetMapping("/{itemId}") + public ResponseEntity getItemById(@PathVariable Long itemId, + @RequestHeader(OWNER_ID) Long ownerId) { + return itemService.getItemById(itemId, ownerId); + } + + @GetMapping + public ResponseEntity getAllItemsForOwner( + @RequestHeader(OWNER_ID) Long ownerId) { + return itemService.getAllItemsForOwner(ownerId); + } + + @GetMapping("/search") + public ResponseEntity searchItems(@RequestParam String text) { + return itemService.searchByText(text); + } + + @PostMapping("/{itemId}/comment") + public ResponseEntity addComment(@RequestBody CommentRequestDto comment, + @PathVariable Long itemId, + @RequestHeader(OWNER_ID) Long commentatorId) { + return itemService.addComment(commentatorId, comment, itemId); + } + +} diff --git a/src/main/java/ru/practicum/shareit/item/dto/CommentRequestDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentRequestDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/CommentRequestDto.java rename to gateway/src/main/java/ru/practicum/shareit/item/dto/CommentRequestDto.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/CommentResponseCreatedDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentResponseCreatedDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/CommentResponseCreatedDto.java rename to gateway/src/main/java/ru/practicum/shareit/item/dto/CommentResponseCreatedDto.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/CommentResponseDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentResponseDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/CommentResponseDto.java rename to gateway/src/main/java/ru/practicum/shareit/item/dto/CommentResponseDto.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemBookerDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemBookerDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/ItemBookerDto.java rename to gateway/src/main/java/ru/practicum/shareit/item/dto/ItemBookerDto.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemRequestDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemRequestDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/ItemRequestDto.java rename to gateway/src/main/java/ru/practicum/shareit/item/dto/ItemRequestDto.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/ItemResponseDto.java rename to gateway/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDto.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDtoWithComments.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDtoWithComments.java similarity index 93% rename from src/main/java/ru/practicum/shareit/item/dto/ItemResponseDtoWithComments.java rename to gateway/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDtoWithComments.java index 63d3bd6..5d8009a 100644 --- a/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDtoWithComments.java +++ b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDtoWithComments.java @@ -1,7 +1,6 @@ package ru.practicum.shareit.item.dto; import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -14,7 +13,6 @@ @NoArgsConstructor public class ItemResponseDtoWithComments { - @NotNull @JsonProperty("id") private Long itemId; 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..204efce --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestClient.java @@ -0,0 +1,43 @@ +package ru.practicum.shareit.request; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.util.DefaultUriBuilderFactory; +import ru.practicum.shareit.client.BaseClient; +import ru.practicum.shareit.request.dto.RequestInputDto; + + +@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 addItemRequest(RequestInputDto requestInputDto, Long userId) { + return post("", userId, requestInputDto); + } + + public ResponseEntity getRequestByUser(Long userId) { + return get("", userId); + } + + public ResponseEntity getAllRequests() { + return get("/all"); + } + + 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..3cd1da4 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/RequestController.java @@ -0,0 +1,38 @@ +package ru.practicum.shareit.request; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.request.dto.RequestInputDto; + + +@RestController +@RequestMapping(path = "/requests") +@RequiredArgsConstructor +public class RequestController { + private final RequestClient requestService; + public static final String USER_ID = "X-Sharer-User-Id"; + + @PostMapping + public ResponseEntity addRequest(@RequestBody @Valid RequestInputDto requestInputDto, + @RequestHeader(USER_ID) Long userId) { + return requestService.addItemRequest(requestInputDto, userId); + } + + @GetMapping + public ResponseEntity getRequestByUser(@RequestHeader(USER_ID) Long userId) { + return requestService.getRequestByUser(userId); + } + + @GetMapping("/all") + public ResponseEntity getAllRequests() { + return requestService.getAllRequests(); + } + + @GetMapping("/{requestId}") + public ResponseEntity getRequestById(@PathVariable Long requestId) { + return requestService.getRequestById(requestId); + } +} + diff --git a/gateway/src/main/java/ru/practicum/shareit/request/dto/RequestInputDto.java b/gateway/src/main/java/ru/practicum/shareit/request/dto/RequestInputDto.java new file mode 100644 index 0000000..c88b416 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/dto/RequestInputDto.java @@ -0,0 +1,23 @@ +package ru.practicum.shareit.request.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RequestInputDto { + @JsonProperty("id") + private Long itemRequestId; + + @NotNull + @JsonProperty("description") + private String requestDescription; + + private LocalDateTime requestDate = LocalDateTime.now(); +} diff --git a/gateway/src/main/java/ru/practicum/shareit/request/dto/RequestOutputDto.java b/gateway/src/main/java/ru/practicum/shareit/request/dto/RequestOutputDto.java new file mode 100644 index 0000000..711abeb --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/dto/RequestOutputDto.java @@ -0,0 +1,25 @@ +package ru.practicum.shareit.request.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.user.dto.UserBookingDto; + +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RequestOutputDto { + @JsonProperty("id") + private Long itemRequestId; + + @JsonProperty("description") + private String requestDescription; + + private UserBookingDto requester; + + @JsonProperty("created") + private LocalDateTime requestDate; +} diff --git a/gateway/src/main/java/ru/practicum/shareit/request/dto/RequestOutputWithItemsDto.java b/gateway/src/main/java/ru/practicum/shareit/request/dto/RequestOutputWithItemsDto.java new file mode 100644 index 0000000..98d8d0b --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/dto/RequestOutputWithItemsDto.java @@ -0,0 +1,30 @@ +package ru.practicum.shareit.request.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.item.dto.ItemResponseDto; +import ru.practicum.shareit.user.dto.UserBookingDto; + +import java.time.LocalDateTime; +import java.util.Collection; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RequestOutputWithItemsDto { + @JsonProperty("id") + private Long itemRequestId; + + @JsonProperty("description") + private String requestDescription; + + private UserBookingDto requester; + + @JsonProperty("created") + private LocalDateTime requestDate; + + @JsonProperty("items") + Collection items; +} 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..215102c --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java @@ -0,0 +1,46 @@ +package ru.practicum.shareit.user; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.util.DefaultUriBuilderFactory; +import ru.practicum.shareit.client.BaseClient; +import ru.practicum.shareit.user.dto.UserRequestDto; + +@Service +public class UserClient extends BaseClient { + private static final String API_PREFIX = "/users"; + + @Autowired + public UserClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { + super( + builder + .uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()) + .build() + ); + } + + public ResponseEntity createUser(UserRequestDto user) { + return post("", user); + } + + public ResponseEntity getUserById(Long id) { + return get("/" + id); + } + + public ResponseEntity updateUser(Long id, UserRequestDto user) { + return patch("/" + id, user); + } + + public void deleteUser(Long id) { + delete("/" + id); + } + + public void deleteAllUsers() { + delete(""); + } +} 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..09731bd --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserController.java @@ -0,0 +1,42 @@ +package ru.practicum.shareit.user; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.user.dto.UserRequestDto; + +@RestController +@Validated +@RequestMapping(path = "/users") +@RequiredArgsConstructor +public class UserController { + private final UserClient userService; + + @PostMapping + public ResponseEntity createUser(@Valid @RequestBody UserRequestDto user) { + return userService.createUser(user); + } + + @GetMapping("/{id}") + public ResponseEntity getUser(@PathVariable long id) { + return userService.getUserById(id); + } + + @PatchMapping("/{id}") + public ResponseEntity updateUser(@PathVariable long id, @RequestBody UserRequestDto user) { + return userService.updateUser(id, user); + } + + @DeleteMapping("/{id}") + public void deleteUser(@PathVariable long id) { + userService.deleteUser(id); + } + + @DeleteMapping + public void deleteUser() { + userService.deleteAllUsers(); + } + +} diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserAuthorDto.java b/gateway/src/main/java/ru/practicum/shareit/user/dto/UserAuthorDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/dto/UserAuthorDto.java rename to gateway/src/main/java/ru/practicum/shareit/user/dto/UserAuthorDto.java diff --git a/gateway/src/main/java/ru/practicum/shareit/user/dto/UserBookingDto.java b/gateway/src/main/java/ru/practicum/shareit/user/dto/UserBookingDto.java new file mode 100644 index 0000000..f1be4e5 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/dto/UserBookingDto.java @@ -0,0 +1,4 @@ +package ru.practicum.shareit.user.dto; + +public class UserBookingDto { +} diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserRequestDto.java b/gateway/src/main/java/ru/practicum/shareit/user/dto/UserRequestDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/dto/UserRequestDto.java rename to gateway/src/main/java/ru/practicum/shareit/user/dto/UserRequestDto.java diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserResponseDto.java b/gateway/src/main/java/ru/practicum/shareit/user/dto/UserResponseDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/dto/UserResponseDto.java rename to gateway/src/main/java/ru/practicum/shareit/user/dto/UserResponseDto.java diff --git a/gateway/src/main/resources/application.yml b/gateway/src/main/resources/application.yml new file mode 100644 index 0000000..e301265 --- /dev/null +++ b/gateway/src/main/resources/application.yml @@ -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 091dc51..16ed22f 100644 --- a/pom.xml +++ b/pom.xml @@ -12,98 +12,39 @@ ru.practicum shareit 0.0.1-SNAPSHOT + pom ShareIt + + gateway + server + + 21 1.6.3 0.2.0 - - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - org.postgresql - postgresql - runtime - - - - org.projectlombok - lombok - true - - - - com.h2database - h2 - test - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-starter-validation - - - - org.mapstruct - mapstruct - ${org.mapstruct.version} - - - - org.projectlombok - lombok-mapstruct-binding - ${lombok-mapstruct-binding.version} - - - - - - - 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 @@ -139,44 +80,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - 3.8.1 - - 1.8 - 1.8 - - - org.projectlombok - lombok - ${lombok.version} - - - org.mapstruct - mapstruct-processor - ${org.mapstruct.version} - - - org.projectlombok - lombok-mapstruct-binding - ${lombok-mapstruct-binding.version} - - - true - - - -Amapstruct.suppressGeneratorTimestamp=true - - - -Amapstruct.suppressGeneratorVersionInfoComment=true - - - -Amapstruct.verbose=true - - - - com.github.spotbugs spotbugs-maven-plugin @@ -288,17 +191,5 @@ - - coverage - - - - org.jacoco - jacoco-maven-plugin - - - - - - + \ No newline at end of file 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..5bd587c --- /dev/null +++ b/server/pom.xml @@ -0,0 +1,147 @@ + + + 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.mapstruct + mapstruct + 1.6.3 + + + org.projectlombok + lombok-mapstruct-binding + ${lombok-mapstruct-binding.version} + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 1.8 + 1.8 + + + org.projectlombok + lombok + ${lombok.version} + + + org.mapstruct + mapstruct-processor + ${org.mapstruct.version} + + + org.projectlombok + lombok-mapstruct-binding + ${lombok-mapstruct-binding.version} + + + true + + + -Amapstruct.suppressGeneratorTimestamp=true + + + -Amapstruct.suppressGeneratorVersionInfoComment=true + + + -Amapstruct.verbose=true + + + + + + + + + + coverage + + + + org.jacoco + jacoco-maven-plugin + + + + + + + \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/ShareItApp.java b/server/src/main/java/ru/practicum/shareit/ShareItServer.java similarity index 56% rename from src/main/java/ru/practicum/shareit/ShareItApp.java rename to server/src/main/java/ru/practicum/shareit/ShareItServer.java index a00ad56..e793b9c 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); + } -} +} \ No newline at end of file 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 81% rename from src/main/java/ru/practicum/shareit/booking/BookingController.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingController.java index 5d1a56c..1b0f719 100644 --- a/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -1,11 +1,9 @@ package ru.practicum.shareit.booking; -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; import ru.practicum.shareit.booking.dto.BookingRequestDto; import ru.practicum.shareit.booking.dto.BookingResponseDto; -import ru.practicum.shareit.booking.intrfaces.BookingServiceInterface; import java.util.Collection; @@ -13,11 +11,11 @@ @RequestMapping(path = "/bookings") @RequiredArgsConstructor public class BookingController { - private final BookingServiceInterface bookingService; + private final BookingServiceImpl bookingService; public static final String USER_ID = "X-Sharer-User-Id"; @PostMapping - public BookingResponseDto addBooking(@RequestBody @Valid BookingRequestDto booking, + public BookingResponseDto addBooking(@RequestBody BookingRequestDto booking, @RequestHeader(USER_ID) Long bookerId) { return bookingService.addBooking(booking, bookerId); } @@ -30,8 +28,8 @@ public BookingResponseDto bookingApprove(@PathVariable Long bookingId, } @GetMapping("/{bookingId}") - public BookingResponseDto getBooking(@PathVariable Long bookingId, - @RequestHeader(USER_ID) Long userId) { + public BookingResponseDto getBookingById(@PathVariable Long bookingId, + @RequestHeader(USER_ID) Long userId) { return bookingService.getBookingByBookingId(bookingId, userId); } 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 97% rename from src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java index 4bc3009..7baac4c 100644 --- a/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java @@ -4,8 +4,8 @@ import org.springframework.stereotype.Service; import ru.practicum.shareit.booking.dto.BookingRequestDto; import ru.practicum.shareit.booking.dto.BookingResponseDto; -import ru.practicum.shareit.booking.intrfaces.BookingRepository; -import ru.practicum.shareit.booking.intrfaces.BookingServiceInterface; +import ru.practicum.shareit.booking.interfaces.BookingRepository; +import ru.practicum.shareit.booking.interfaces.BookingServiceInterface; import ru.practicum.shareit.booking.mapper.BookingMapper; import ru.practicum.shareit.booking.model.Booking; import ru.practicum.shareit.booking.model.BookingStatus; @@ -52,7 +52,7 @@ public BookingResponseDto addBooking(BookingRequestDto booking, Long bookerId) { if (item.getIsAvailable().equals(false)) { saved.setStatus(BookingStatus.REJECTED); storage.save(saved); - throw new RuntimeException("Booking is rejected, item is not available"); + throw new ValidationException("Booking is rejected, item is not available"); } else { saved.setStatus(BookingStatus.WAITING); storage.save(saved); diff --git a/server/src/main/java/ru/practicum/shareit/booking/dto/BookingRequestDto.java b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingRequestDto.java new file mode 100644 index 0000000..cad3ac9 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingRequestDto.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.booking.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.model.BookingStatus; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class BookingRequestDto { + + private Long bookingId; + private LocalDateTime start; + private LocalDateTime end; + private Long itemId; + private Long bookerId; + private BookingStatus status; +} diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingResponseDto.java b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingResponseDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/dto/BookingResponseDto.java rename to server/src/main/java/ru/practicum/shareit/booking/dto/BookingResponseDto.java diff --git a/src/main/java/ru/practicum/shareit/booking/intrfaces/BookingRepository.java b/server/src/main/java/ru/practicum/shareit/booking/interfaces/BookingRepository.java similarity index 96% rename from src/main/java/ru/practicum/shareit/booking/intrfaces/BookingRepository.java rename to server/src/main/java/ru/practicum/shareit/booking/interfaces/BookingRepository.java index f5c6a0d..fe1b88b 100644 --- a/src/main/java/ru/practicum/shareit/booking/intrfaces/BookingRepository.java +++ b/server/src/main/java/ru/practicum/shareit/booking/interfaces/BookingRepository.java @@ -1,4 +1,4 @@ -package ru.practicum.shareit.booking.intrfaces; +package ru.practicum.shareit.booking.interfaces; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; diff --git a/src/main/java/ru/practicum/shareit/booking/intrfaces/BookingServiceInterface.java b/server/src/main/java/ru/practicum/shareit/booking/interfaces/BookingServiceInterface.java similarity index 92% rename from src/main/java/ru/practicum/shareit/booking/intrfaces/BookingServiceInterface.java rename to server/src/main/java/ru/practicum/shareit/booking/interfaces/BookingServiceInterface.java index 539d1cf..d67f3ec 100644 --- a/src/main/java/ru/practicum/shareit/booking/intrfaces/BookingServiceInterface.java +++ b/server/src/main/java/ru/practicum/shareit/booking/interfaces/BookingServiceInterface.java @@ -1,4 +1,4 @@ -package ru.practicum.shareit.booking.intrfaces; +package ru.practicum.shareit.booking.interfaces; import ru.practicum.shareit.booking.dto.BookingRequestDto; import ru.practicum.shareit.booking.dto.BookingResponseDto; diff --git a/src/main/java/ru/practicum/shareit/booking/mapper/BookingMapper.java b/server/src/main/java/ru/practicum/shareit/booking/mapper/BookingMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/mapper/BookingMapper.java rename to server/src/main/java/ru/practicum/shareit/booking/mapper/BookingMapper.java diff --git a/src/main/java/ru/practicum/shareit/booking/model/Booking.java b/server/src/main/java/ru/practicum/shareit/booking/model/Booking.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/model/Booking.java rename to server/src/main/java/ru/practicum/shareit/booking/model/Booking.java diff --git a/src/main/java/ru/practicum/shareit/booking/model/BookingStatus.java b/server/src/main/java/ru/practicum/shareit/booking/model/BookingStatus.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/model/BookingStatus.java rename to server/src/main/java/ru/practicum/shareit/booking/model/BookingStatus.java diff --git a/server/src/main/java/ru/practicum/shareit/exception/AnotherUserException.java b/server/src/main/java/ru/practicum/shareit/exception/AnotherUserException.java new file mode 100644 index 0000000..6fc8434 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/AnotherUserException.java @@ -0,0 +1,8 @@ +package ru.practicum.shareit.exception; + +public class AnotherUserException extends RuntimeException { + + public AnotherUserException(String message) { + super(message); + } +} diff --git a/server/src/main/java/ru/practicum/shareit/exception/DuplicatedDataException.java b/server/src/main/java/ru/practicum/shareit/exception/DuplicatedDataException.java new file mode 100644 index 0000000..3aef12f --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/DuplicatedDataException.java @@ -0,0 +1,8 @@ +package ru.practicum.shareit.exception; + +public class DuplicatedDataException extends RuntimeException { + + public DuplicatedDataException(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..61d8fd9 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -0,0 +1,47 @@ +package ru.practicum.shareit.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +@Slf4j +public class ErrorHandler { + + @ExceptionHandler(DuplicatedDataException.class) + @ResponseStatus(HttpStatus.CONFLICT) + public ErrorResponse handleDuplicatedDataException(final DuplicatedDataException e) { + log.error("Duplicated: {}", e.getMessage()); + return new ErrorResponse(e.getMessage(), HttpStatus.CONFLICT); + } + + @ExceptionHandler(NotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public ErrorResponse handleNotFoundException(final NotFoundException e) { + log.error("Not found: {}", e.getMessage()); + return new ErrorResponse(e.getMessage(), HttpStatus.NOT_FOUND); + } + + @ExceptionHandler(ValidationException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleValidationException(final ValidationException e) { + log.error("Not found: {}", e.getMessage()); + return new ErrorResponse(e.getMessage(), HttpStatus.BAD_REQUEST); + } + + @ExceptionHandler(AnotherUserException.class) + @ResponseStatus(HttpStatus.FORBIDDEN) + public ErrorResponse handleAnotherUserException(final AnotherUserException e) { + log.error("Not found: {}", e.getMessage()); + return new ErrorResponse(e.getMessage(), HttpStatus.FORBIDDEN); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public ErrorResponse handleException(final RuntimeException e) { + log.error("ServerError: {}", e.getMessage()); + return new ErrorResponse(e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); + } +} diff --git a/server/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java b/server/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java new file mode 100644 index 0000000..40e1fb8 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java @@ -0,0 +1,16 @@ +package ru.practicum.shareit.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class ErrorResponse { + String error; + HttpStatus status; + + public ErrorResponse(String error, HttpStatus status) { + this.error = error; + this.status = status; + } + +} 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..89a2ad0 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/NotFoundException.java @@ -0,0 +1,10 @@ +package ru.practicum.shareit.exception; + +public class NotFoundException extends RuntimeException { + + public NotFoundException(String message) { + super(message); + } + +} + 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..56cb5a8 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/exception/ValidationException.java @@ -0,0 +1,8 @@ +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/ItemController.java b/server/src/main/java/ru/practicum/shareit/item/ItemController.java similarity index 94% rename from src/main/java/ru/practicum/shareit/item/ItemController.java rename to server/src/main/java/ru/practicum/shareit/item/ItemController.java index 418f8b6..a8b5154 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemController.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -1,7 +1,5 @@ package ru.practicum.shareit.item; - -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; @@ -22,7 +20,7 @@ public class ItemController { @PostMapping public ItemResponseDto create(@RequestHeader(OWNER_ID) Long ownerId, - @Valid @RequestBody ItemRequestDto item) { + @RequestBody ItemRequestDto item) { return itemService.createItem(item, ownerId); } 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 94% rename from src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java rename to server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java index 3b77efe..9e4dd0a 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java @@ -3,7 +3,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import ru.practicum.shareit.booking.dto.BookingResponseDto; -import ru.practicum.shareit.booking.intrfaces.BookingRepository; +import ru.practicum.shareit.booking.interfaces.BookingRepository; import ru.practicum.shareit.booking.mapper.BookingMapper; import ru.practicum.shareit.booking.model.Booking; import ru.practicum.shareit.booking.model.BookingStatus; @@ -18,6 +18,7 @@ import ru.practicum.shareit.item.mapper.ItemMapper; import ru.practicum.shareit.item.model.Comment; import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.request.interfaces.RequestRepository; import ru.practicum.shareit.user.interfaces.UserRepository; import ru.practicum.shareit.user.mapper.UserMapper; import ru.practicum.shareit.user.model.User; @@ -38,6 +39,7 @@ public class ItemServiceImpl implements ItemServiceInterface { private final UserRepository userRepository; private final CommentRepository commentRepository; private final BookingRepository bookingRepository; + private final RequestRepository requestRepository; private final ItemMapper itemMapper; private final UserMapper userMapper; private final CommentMapper commentMapper; @@ -53,6 +55,10 @@ public ItemResponseDto createItem(ItemRequestDto item, Long ownerId) { Item itemSaved = itemMapper.toItem(item); itemSaved.setOwner(owner); + if (item.getRequestId() != null) { + itemSaved.setItemRequest(requestRepository.getRequestByItemRequestId(item.getRequestId()) + .orElseThrow(() -> new NotFoundException("Request with id " + item.getRequestId() + " not found"))); + } itemRepository.save(itemSaved); diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/CommentRequestDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/CommentRequestDto.java new file mode 100644 index 0000000..3462221 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/dto/CommentRequestDto.java @@ -0,0 +1,19 @@ +package ru.practicum.shareit.item.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class CommentRequestDto { + + @JsonProperty("id") + private Long authorId; + + @JsonProperty("text") + private String text; + + private LocalDateTime date = LocalDateTime.now(); + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/CommentResponseCreatedDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/CommentResponseCreatedDto.java new file mode 100644 index 0000000..5e37d63 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/dto/CommentResponseCreatedDto.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.item.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CommentResponseCreatedDto { + @JsonProperty("id") + private Long commentId; + + @JsonProperty("text") + private String text; + + private String authorName; + + private Boolean created = true; +} diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/CommentResponseDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/CommentResponseDto.java new file mode 100644 index 0000000..af164d7 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/dto/CommentResponseDto.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.item.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.user.dto.UserAuthorDto; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CommentResponseDto { + + @JsonProperty("id") + private Long commentId; + + @JsonProperty("text") + private String text; + + private UserAuthorDto author; +} diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/ItemBookerDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemBookerDto.java new file mode 100644 index 0000000..28d23ca --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/dto/ItemBookerDto.java @@ -0,0 +1,18 @@ +package ru.practicum.shareit.item.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemBookerDto { + + @JsonProperty("id") + private Long itemId; + + @JsonProperty("name") + private String itemName; +} diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/ItemRequestDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemRequestDto.java new file mode 100644 index 0000000..d2a9880 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/dto/ItemRequestDto.java @@ -0,0 +1,26 @@ +package ru.practicum.shareit.item.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemRequestDto { + @JsonProperty("id") + private Long itemId; + + @JsonProperty("name") + private String itemName; + + @JsonProperty("description") + private String itemDescription; + + @JsonProperty("available") + private Boolean isAvailable; + + private Long requestId; + +} diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDto.java new file mode 100644 index 0000000..dfec0b2 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDto.java @@ -0,0 +1,29 @@ +package ru.practicum.shareit.item.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemResponseDto { + + @JsonProperty("id") + private Long itemId; + + @JsonProperty("name") + private String itemName; + + @JsonProperty("description") + private String itemDescription; + + @JsonProperty("available") + private Boolean isAvailable; + + @JsonProperty("userId") + private Long ownerId; + + private Long requestId; +} diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDtoWithComments.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDtoWithComments.java new file mode 100644 index 0000000..5d8009a --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/dto/ItemResponseDtoWithComments.java @@ -0,0 +1,38 @@ +package ru.practicum.shareit.item.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.dto.BookingResponseDto; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemResponseDtoWithComments { + + @JsonProperty("id") + private Long itemId; + + @JsonProperty("name") + private String itemName; + + @JsonProperty("description") + private String itemDescription; + + @JsonProperty("available") + private Boolean isAvailable; + + @JsonProperty("userId") + private Long ownerId; + + private Long requestId; + + private BookingResponseDto lastBooking; + + private BookingResponseDto nextBooking; + + private List comments; +} diff --git a/src/main/java/ru/practicum/shareit/item/interfaces/CommentRepository.java b/server/src/main/java/ru/practicum/shareit/item/interfaces/CommentRepository.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/interfaces/CommentRepository.java rename to server/src/main/java/ru/practicum/shareit/item/interfaces/CommentRepository.java diff --git a/src/main/java/ru/practicum/shareit/item/interfaces/ItemRepository.java b/server/src/main/java/ru/practicum/shareit/item/interfaces/ItemRepository.java similarity index 90% rename from src/main/java/ru/practicum/shareit/item/interfaces/ItemRepository.java rename to server/src/main/java/ru/practicum/shareit/item/interfaces/ItemRepository.java index d27984b..2942a2e 100644 --- a/src/main/java/ru/practicum/shareit/item/interfaces/ItemRepository.java +++ b/server/src/main/java/ru/practicum/shareit/item/interfaces/ItemRepository.java @@ -17,4 +17,6 @@ public interface ItemRepository extends JpaRepository { @Query(value = "SELECT i FROM Item i WHERE upper(i.itemName) like upper(concat('%', ?1, '%')) " + " OR upper(i.itemDescription) LIKE upper(concat('%', ?1, '%'))") Collection searchByTextContainingIgnoreCase(String text); + + Collection getItemsByItemRequest_ItemRequestId(Long itemRequestId); } diff --git a/src/main/java/ru/practicum/shareit/item/interfaces/ItemServiceInterface.java b/server/src/main/java/ru/practicum/shareit/item/interfaces/ItemServiceInterface.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/interfaces/ItemServiceInterface.java rename to server/src/main/java/ru/practicum/shareit/item/interfaces/ItemServiceInterface.java diff --git a/src/main/java/ru/practicum/shareit/item/interfaces/ItemStorageInterface.java b/server/src/main/java/ru/practicum/shareit/item/interfaces/ItemStorageInterface.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/interfaces/ItemStorageInterface.java rename to server/src/main/java/ru/practicum/shareit/item/interfaces/ItemStorageInterface.java diff --git a/src/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java b/server/src/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java rename to server/src/main/java/ru/practicum/shareit/item/mapper/CommentMapper.java diff --git a/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java b/server/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java similarity index 99% rename from src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java rename to server/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java index 38eb932..910271f 100644 --- a/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java +++ b/server/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java @@ -29,7 +29,7 @@ default ItemResponseDto toItemResponseDto(Item item) { itemResponseDto.setOwnerId(item.getOwner().getUserId()); if (item.getItemRequest() != null) { - itemResponseDto.setRequestId(item.getItemRequest().getRequesterId()); + itemResponseDto.setRequestId(item.getItemRequest().getItemRequestId()); } return itemResponseDto; diff --git a/src/main/java/ru/practicum/shareit/item/model/Comment.java b/server/src/main/java/ru/practicum/shareit/item/model/Comment.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/model/Comment.java rename to server/src/main/java/ru/practicum/shareit/item/model/Comment.java diff --git a/src/main/java/ru/practicum/shareit/item/model/Item.java b/server/src/main/java/ru/practicum/shareit/item/model/Item.java similarity index 93% rename from src/main/java/ru/practicum/shareit/item/model/Item.java rename to server/src/main/java/ru/practicum/shareit/item/model/Item.java index c1d6d11..31726c1 100644 --- a/src/main/java/ru/practicum/shareit/item/model/Item.java +++ b/server/src/main/java/ru/practicum/shareit/item/model/Item.java @@ -5,7 +5,7 @@ import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; -import ru.practicum.shareit.request.ItemRequest; +import ru.practicum.shareit.request.model.ItemRequest; import ru.practicum.shareit.user.model.User; @Data @@ -15,7 +15,6 @@ @ToString @Table(name = "items") public class Item { - @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "item_id") 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..eb712aa --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestController.java @@ -0,0 +1,42 @@ +package ru.practicum.shareit.request; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.request.dto.RequestInputDto; +import ru.practicum.shareit.request.dto.RequestOutputDto; +import ru.practicum.shareit.request.dto.RequestOutputWithItemsDto; +import ru.practicum.shareit.request.interfaces.RequestServiceInterface; + +import java.util.Collection; +import java.util.List; + + +@RestController +@RequestMapping(path = "/requests") +@RequiredArgsConstructor +public class RequestController { + private final RequestServiceInterface requestService; + public static final String USER_ID = "X-Sharer-User-Id"; + + @PostMapping + public RequestOutputDto addRequest(@RequestBody RequestInputDto requestInputDto, + @RequestHeader(USER_ID) Long userId) { + return requestService.addItemRequest(requestInputDto, userId); + } + + @GetMapping + public Collection getRequestByUser(@RequestHeader(USER_ID) Long userId) { + return requestService.getRequestByUser(userId); + } + + @GetMapping("/all") + public List getAllRequests() { + return requestService.getAllRequests(); + } + + @GetMapping("/{requestId}") + public RequestOutputWithItemsDto getRequestById(@PathVariable Long requestId) { + return requestService.getRequestById(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..9ad3095 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/RequestServiceImpl.java @@ -0,0 +1,75 @@ +package ru.practicum.shareit.request; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.item.dto.ItemResponseDto; +import ru.practicum.shareit.item.interfaces.ItemRepository; +import ru.practicum.shareit.item.mapper.ItemMapper; +import ru.practicum.shareit.request.dto.RequestInputDto; +import ru.practicum.shareit.request.dto.RequestOutputDto; +import ru.practicum.shareit.request.dto.RequestOutputWithItemsDto; +import ru.practicum.shareit.request.interfaces.RequestServiceInterface; +import ru.practicum.shareit.request.interfaces.RequestRepository; +import ru.practicum.shareit.request.mapper.RequestMapper; +import ru.practicum.shareit.request.model.ItemRequest; +import ru.practicum.shareit.user.interfaces.UserRepository; +import ru.practicum.shareit.user.model.User; + +import java.util.Collection; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class RequestServiceImpl implements RequestServiceInterface { + private final UserRepository userRepository; + private final RequestRepository requestRepository; + private final ItemRepository itemRepository; + private final RequestMapper requestMapper; + private final ItemMapper itemMapper; + + @Override + public RequestOutputDto addItemRequest(RequestInputDto requestDto, Long userId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new NotFoundException("User not found")); + + ItemRequest itemRequest = requestMapper.toRequest(requestDto); + itemRequest.setRequester(user); + return requestMapper.toRequestOutputDto(requestRepository.save(itemRequest)); + } + + @Override + public List getRequestByUser(Long userId) { + User user = userRepository.findById(userId) + .orElseThrow(() -> new NotFoundException("User not found")); + + return requestRepository.findItemRequestByRequester_UserIdOrderByCreatedAtDesc(userId).stream() + .map(requestMapper::toRequestOutputDto) + .toList(); + } + + @Override + public List getAllRequests() { + return requestRepository.findAllByOrderByCreatedAtDesc().stream() + .map(requestMapper::toRequestOutputDto) + .toList(); + } + + @Override + public RequestOutputWithItemsDto getRequestById(Long requestId) { + ItemRequest itemRequest = requestRepository.getRequestByItemRequestId(requestId) + .orElseThrow(() -> new NotFoundException("Request not found")); + + Collection items = + itemRepository.getItemsByItemRequest_ItemRequestId(requestId).stream() + .map(itemMapper::toItemResponseDto) + .toList(); + + RequestOutputWithItemsDto responseDto = requestMapper.toRequestOutputWithItemsDto(itemRequest); + responseDto.setItems(items); + + return responseDto; + } + + +} diff --git a/server/src/main/java/ru/practicum/shareit/request/dto/RequestInputDto.java b/server/src/main/java/ru/practicum/shareit/request/dto/RequestInputDto.java new file mode 100644 index 0000000..ace1634 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/dto/RequestInputDto.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.request.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RequestInputDto { + @JsonProperty("id") + private Long itemRequestId; + + @JsonProperty("description") + private String requestDescription; + + private LocalDateTime createdAt = LocalDateTime.now(); +} diff --git a/server/src/main/java/ru/practicum/shareit/request/dto/RequestOutputDto.java b/server/src/main/java/ru/practicum/shareit/request/dto/RequestOutputDto.java new file mode 100644 index 0000000..0fafba4 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/dto/RequestOutputDto.java @@ -0,0 +1,25 @@ +package ru.practicum.shareit.request.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.user.dto.UserBookingDto; + +import java.time.LocalDateTime; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RequestOutputDto { + @JsonProperty("id") + private Long itemRequestId; + + @JsonProperty("description") + private String requestDescription; + + private UserBookingDto requester; + + @JsonProperty("created") + private LocalDateTime createdAt; +} diff --git a/server/src/main/java/ru/practicum/shareit/request/dto/RequestOutputWithItemsDto.java b/server/src/main/java/ru/practicum/shareit/request/dto/RequestOutputWithItemsDto.java new file mode 100644 index 0000000..75eeac8 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/dto/RequestOutputWithItemsDto.java @@ -0,0 +1,30 @@ +package ru.practicum.shareit.request.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.item.dto.ItemResponseDto; +import ru.practicum.shareit.user.dto.UserBookingDto; + +import java.time.LocalDateTime; +import java.util.Collection; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class RequestOutputWithItemsDto { + @JsonProperty("id") + private Long itemRequestId; + + @JsonProperty("description") + private String requestDescription; + + private UserBookingDto requester; + + @JsonProperty("created") + private LocalDateTime createdAt; + + @JsonProperty("items") + Collection items; +} diff --git a/server/src/main/java/ru/practicum/shareit/request/interfaces/RequestRepository.java b/server/src/main/java/ru/practicum/shareit/request/interfaces/RequestRepository.java new file mode 100644 index 0000000..43b6b2b --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/interfaces/RequestRepository.java @@ -0,0 +1,18 @@ +package ru.practicum.shareit.request.interfaces; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.practicum.shareit.request.model.ItemRequest; + +import java.util.List; +import java.util.Optional; + +public interface RequestRepository extends JpaRepository { + ItemRequest save(ItemRequest item); + + List findItemRequestByRequester_UserIdOrderByCreatedAtDesc(Long userId); + + List findAllByOrderByCreatedAtDesc(); + + Optional getRequestByItemRequestId(Long requestId); + +} diff --git a/server/src/main/java/ru/practicum/shareit/request/interfaces/RequestServiceInterface.java b/server/src/main/java/ru/practicum/shareit/request/interfaces/RequestServiceInterface.java new file mode 100644 index 0000000..2238138 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/interfaces/RequestServiceInterface.java @@ -0,0 +1,17 @@ +package ru.practicum.shareit.request.interfaces; + +import ru.practicum.shareit.request.dto.RequestInputDto; +import ru.practicum.shareit.request.dto.RequestOutputDto; +import ru.practicum.shareit.request.dto.RequestOutputWithItemsDto; + +import java.util.List; + +public interface RequestServiceInterface { + RequestOutputDto addItemRequest(RequestInputDto requestDto, Long userId); + + List getRequestByUser(Long userId); + + List getAllRequests(); + + RequestOutputWithItemsDto getRequestById(Long requestId); +} diff --git a/server/src/main/java/ru/practicum/shareit/request/mapper/RequestMapper.java b/server/src/main/java/ru/practicum/shareit/request/mapper/RequestMapper.java new file mode 100644 index 0000000..d623e12 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/mapper/RequestMapper.java @@ -0,0 +1,17 @@ +package ru.practicum.shareit.request.mapper; + +import org.mapstruct.Mapper; +import org.mapstruct.MappingConstants; +import ru.practicum.shareit.request.dto.RequestInputDto; +import ru.practicum.shareit.request.dto.RequestOutputDto; +import ru.practicum.shareit.request.dto.RequestOutputWithItemsDto; +import ru.practicum.shareit.request.model.ItemRequest; + +@Mapper(componentModel = MappingConstants.ComponentModel.SPRING) +public interface RequestMapper { + ItemRequest toRequest(RequestInputDto itemRequestInputDto); + + RequestOutputDto toRequestOutputDto(ItemRequest itemRequest); + + RequestOutputWithItemsDto toRequestOutputWithItemsDto(ItemRequest itemRequest); +} diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequest.java b/server/src/main/java/ru/practicum/shareit/request/model/ItemRequest.java similarity index 62% rename from src/main/java/ru/practicum/shareit/request/ItemRequest.java rename to server/src/main/java/ru/practicum/shareit/request/model/ItemRequest.java index 0da7fda..a8e64d2 100644 --- a/src/main/java/ru/practicum/shareit/request/ItemRequest.java +++ b/server/src/main/java/ru/practicum/shareit/request/model/ItemRequest.java @@ -1,16 +1,14 @@ -package ru.practicum.shareit.request; +package ru.practicum.shareit.request.model; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.ToString; +import ru.practicum.shareit.user.model.User; -import java.time.LocalDate; +import java.time.LocalDateTime; -/** - * TODO Sprint add-item-requests. - */ @Data @Entity @Table(name = "requests") @@ -27,9 +25,10 @@ public class ItemRequest { @Column(name = "description") private String requestDescription; - @Column(name = "requestor_id") - private Long requesterId; + @ManyToOne + @JoinColumn(name = "requester_id") + private User requester; - @Transient - private LocalDate requestDate; + @Column(name = "request_date") + private LocalDateTime createdAt; } 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 83% rename from src/main/java/ru/practicum/shareit/user/UserController.java rename to server/src/main/java/ru/practicum/shareit/user/UserController.java index 0289b90..4b95fe5 100644 --- a/src/main/java/ru/practicum/shareit/user/UserController.java +++ b/server/src/main/java/ru/practicum/shareit/user/UserController.java @@ -1,6 +1,5 @@ package ru.practicum.shareit.user; -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; @@ -15,7 +14,7 @@ public class UserController { private final UserServiceImpl userService; @PostMapping - public UserResponseDto createUser(@Valid @RequestBody UserRequestDto user) { + public UserResponseDto createUser(@RequestBody UserRequestDto user) { return userService.createUser(user); } @@ -30,12 +29,12 @@ public UserResponseDto updateUser(@PathVariable long id, @RequestBody UserReques } @DeleteMapping("/{id}") - public void deleteUser(@PathVariable long id) { + public void deleteAllUsers(@PathVariable long id) { userService.deleteUser(id); } @DeleteMapping - public void deleteUser() { + public void deleteAllUsers() { userService.deleteAllUsers(); } 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/java/ru/practicum/shareit/user/dto/UserAuthorDto.java b/server/src/main/java/ru/practicum/shareit/user/dto/UserAuthorDto.java new file mode 100644 index 0000000..3ad9b9a --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/dto/UserAuthorDto.java @@ -0,0 +1,18 @@ +package ru.practicum.shareit.user.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class UserAuthorDto { + + @JsonProperty("id") + private Long userId; + + @JsonProperty("authorName") + private String userName; +} diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserBookingDto.java b/server/src/main/java/ru/practicum/shareit/user/dto/UserBookingDto.java similarity index 85% rename from src/main/java/ru/practicum/shareit/user/dto/UserBookingDto.java rename to server/src/main/java/ru/practicum/shareit/user/dto/UserBookingDto.java index ef634fa..bffdc8f 100644 --- a/src/main/java/ru/practicum/shareit/user/dto/UserBookingDto.java +++ b/server/src/main/java/ru/practicum/shareit/user/dto/UserBookingDto.java @@ -1,7 +1,6 @@ package ru.practicum.shareit.user.dto; import com.fasterxml.jackson.annotation.JsonProperty; -import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; @@ -10,7 +9,7 @@ @AllArgsConstructor @NoArgsConstructor public class UserBookingDto { - @NotNull + @JsonProperty("id") private Long userId; diff --git a/server/src/main/java/ru/practicum/shareit/user/dto/UserRequestDto.java b/server/src/main/java/ru/practicum/shareit/user/dto/UserRequestDto.java new file mode 100644 index 0000000..1cdead7 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/dto/UserRequestDto.java @@ -0,0 +1,22 @@ +package ru.practicum.shareit.user.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UserRequestDto { + + @JsonProperty("id") + private Long userId; + + + @JsonProperty("name") + private String userName; + + @JsonProperty("email") + private String userEmail; +} diff --git a/server/src/main/java/ru/practicum/shareit/user/dto/UserResponseDto.java b/server/src/main/java/ru/practicum/shareit/user/dto/UserResponseDto.java new file mode 100644 index 0000000..c03b139 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/dto/UserResponseDto.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.user.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +public class UserResponseDto { + + @JsonProperty("id") + private Long userId; + + @JsonProperty("name") + private String userName; + + @JsonProperty("email") + private String userEmail; +} diff --git a/src/main/java/ru/practicum/shareit/user/interfaces/UserRepository.java b/server/src/main/java/ru/practicum/shareit/user/interfaces/UserRepository.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/interfaces/UserRepository.java rename to server/src/main/java/ru/practicum/shareit/user/interfaces/UserRepository.java diff --git a/src/main/java/ru/practicum/shareit/user/interfaces/UserServiceInterface.java b/server/src/main/java/ru/practicum/shareit/user/interfaces/UserServiceInterface.java similarity index 99% rename from src/main/java/ru/practicum/shareit/user/interfaces/UserServiceInterface.java rename to server/src/main/java/ru/practicum/shareit/user/interfaces/UserServiceInterface.java index 1ca30dc..4dca5a4 100644 --- a/src/main/java/ru/practicum/shareit/user/interfaces/UserServiceInterface.java +++ b/server/src/main/java/ru/practicum/shareit/user/interfaces/UserServiceInterface.java @@ -3,6 +3,7 @@ import ru.practicum.shareit.user.dto.UserRequestDto; import ru.practicum.shareit.user.dto.UserResponseDto; + public interface UserServiceInterface { UserResponseDto createUser(UserRequestDto user); @@ -13,4 +14,5 @@ public interface UserServiceInterface { void deleteUser(Long userId); void deleteAllUsers(); + } diff --git a/src/main/java/ru/practicum/shareit/user/mapper/UserMapper.java b/server/src/main/java/ru/practicum/shareit/user/mapper/UserMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/mapper/UserMapper.java rename to server/src/main/java/ru/practicum/shareit/user/mapper/UserMapper.java diff --git a/src/main/java/ru/practicum/shareit/user/model/User.java b/server/src/main/java/ru/practicum/shareit/user/model/User.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/model/User.java rename to server/src/main/java/ru/practicum/shareit/user/model/User.java diff --git a/server/src/main/resources/application-test.yml b/server/src/main/resources/application-test.yml new file mode 100644 index 0000000..c02e1e5 --- /dev/null +++ b/server/src/main/resources/application-test.yml @@ -0,0 +1,15 @@ +spring: + config: + activate: + on-profile: test + jpa: + generate-ddl: false + hibernate.ddl-auto: none + datasource: + url: jdbc:h2:mem:testdb;MODE=PostgreSQL + driverClassName: org.h2.Driver + username: user + password: 12345 + sql: + init: + mode: always diff --git a/server/src/main/resources/application.yml b/server/src/main/resources/application.yml new file mode 100644 index 0000000..70ed292 --- /dev/null +++ b/server/src/main/resources/application.yml @@ -0,0 +1,25 @@ +server.port: 9090 + +spring: + jpa: + hibernate.ddl-auto: none + properties.hibernate.format_sql: true + + sql: + init: + mode: always + datasource: + driverClassName: org.postgresql.Driver + url: jdbc:postgresql://localhost:5432/postgres + username: user + password: 12345 +logging: + level: + org: + springframework: + orm.jpa: INFO + transaction: INFO + interceptor: TRACE + JpaTransactionManager: DEBUG + + diff --git a/src/main/resources/schema.sql b/server/src/main/resources/schema.sql similarity index 70% rename from src/main/resources/schema.sql rename to server/src/main/resources/schema.sql index c07802f..a7a4013 100644 --- a/src/main/resources/schema.sql +++ b/server/src/main/resources/schema.sql @@ -1,11 +1,11 @@ CREATE TABLE IF NOT EXISTS users ( - user_id BIGSERIAL PRIMARY KEY, + user_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, user_name CHARACTER VARYING(255) NOT NULL, user_email CHARACTER VARYING(500) NOT NULL UNIQUE ); CREATE TABLE IF NOT EXISTS items ( - item_id BIGSERIAL PRIMARY KEY, + item_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, item_name CHARACTER VARYING(255) NOT NULL, item_description CHARACTER VARYING(150) NOT NULL, is_available BOOLEAN NOT NULL, @@ -15,7 +15,7 @@ CREATE TABLE IF NOT EXISTS items ( ); CREATE TABLE IF NOT EXISTS comments ( - comment_id BIGSERIAL PRIMARY KEY, + comment_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, text CHARACTER VARYING(2000) NOT NULL, item_id BIGINT NOT NULL, author_id BIGINT NOT NULL, @@ -24,7 +24,7 @@ CREATE TABLE IF NOT EXISTS comments ( ); CREATE TABLE IF NOT EXISTS bookings ( - booking_id BIGSERIAL PRIMARY KEY, + booking_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, start_date TIMESTAMP WITHOUT TIME ZONE NOT NULL, end_date TIMESTAMP WITHOUT TIME ZONE NOT NULL, item_id BIGINT NOT NULL, @@ -35,8 +35,9 @@ CREATE TABLE IF NOT EXISTS bookings ( ); CREATE TABLE IF NOT EXISTS requests ( - request_id BIGSERIAL PRIMARY KEY, - requestor_id BIGINT NOT NULL, - description CHARACTER VARYING (200), - FOREIGN KEY (requestor_id) REFERENCES users (user_id) ON DELETE CASCADE + request_id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + requester_id BIGINT NOT NULL, + description CHARACTER VARYING (200) NOT NULL, + request_date TIMESTAMP WITHOUT TIME ZONE, + FOREIGN KEY (requester_id) REFERENCES users (user_id) ON DELETE CASCADE ); \ No newline at end of file diff --git a/src/test/java/ru/practicum/shareit/ShareItTests.java b/server/src/test/java/ru/practicum/shareit/ShareItTests.java similarity index 100% rename from src/test/java/ru/practicum/shareit/ShareItTests.java rename to server/src/test/java/ru/practicum/shareit/ShareItTests.java diff --git a/server/src/test/java/ru/practicum/shareit/TestData.java b/server/src/test/java/ru/practicum/shareit/TestData.java new file mode 100644 index 0000000..e2c1e83 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/TestData.java @@ -0,0 +1,179 @@ +package ru.practicum.shareit; + +import ru.practicum.shareit.booking.dto.BookingRequestDto; +import ru.practicum.shareit.booking.dto.BookingResponseDto; +import ru.practicum.shareit.booking.model.BookingStatus; +import ru.practicum.shareit.item.dto.*; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.request.dto.RequestInputDto; +import ru.practicum.shareit.request.dto.RequestOutputDto; +import ru.practicum.shareit.request.dto.RequestOutputWithItemsDto; +import ru.practicum.shareit.request.model.ItemRequest; +import ru.practicum.shareit.user.dto.UserAuthorDto; +import ru.practicum.shareit.user.dto.UserBookingDto; +import ru.practicum.shareit.user.dto.UserRequestDto; +import ru.practicum.shareit.user.dto.UserResponseDto; +import ru.practicum.shareit.user.model.User; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + + +public class TestData { + public static User user1 = new User(1L, "TestUser1", "user1@mail.ru"); + public static UserBookingDto userBookingDto1 = new UserBookingDto(1L, "TestUser1"); + public static UserAuthorDto userAuthorDto1 = new UserAuthorDto(1L, "TestUser1"); + public static UserRequestDto userRequestDto1 = + new UserRequestDto(1L, "TestUser1", "user1@mail.ru"); + + public static UserRequestDto userForCreate() { + UserRequestDto user = new UserRequestDto(); + user.setUserName("TestUser1"); + user.setUserEmail("user1@mail.ru"); + return user; + } + + public static UserRequestDto user2ForCreate() { + UserRequestDto user = new UserRequestDto(); + user.setUserName("TestUser2"); + user.setUserEmail("user2@mail.ru"); + return user; + } + + public static UserResponseDto userResponseDto1 = + new UserResponseDto(1L, "TestUser1", "user1@mail.ru"); + + + public static User user2 = new User(2L, "TestUser2", "user2@mail.ru"); + public static UserBookingDto userBookingDto2 = + new UserBookingDto(2L, "TestUser2"); + public static UserRequestDto userRequestDto2 = + new UserRequestDto(2L, "TestUser2", "user2@mail.ru"); + public static UserResponseDto userResponseDto2 = + new UserResponseDto(2L, "TestUser2", "user2@mail.ru"); + + + public static ItemRequest request1 = new ItemRequest(1L, "Test request description1", user1, + LocalDateTime.of(2025, 7, 7, 10, 15, 25)); + public static RequestInputDto requestInputDto1 = new RequestInputDto(1L, "Test request description1", + LocalDateTime.of(2025, 7, 7, 10, 15, 25)); + public static RequestOutputDto requestOutputDto1 = new RequestOutputDto( + 1L, "Test request description1", userBookingDto1, + LocalDateTime.of(2025, 7, 7, 10, 15, 25)); + public static RequestOutputWithItemsDto requestWithItemsDto1 = new RequestOutputWithItemsDto(1L, + "Test request description1", userBookingDto1, + LocalDateTime.of(2025, 7, 7, 10, 15, 25), new ArrayList<>()); + + + public static ItemRequestDto itemForCreate() { + ItemRequestDto request = new ItemRequestDto(); + request.setItemName("testItem1"); + request.setItemDescription("test description1"); + request.setIsAvailable(true); + return request; + } + + public static ItemRequestDto item2ForCreate() { + ItemRequestDto request = new ItemRequestDto(); + request.setItemName("testItem2"); + request.setItemDescription("test description2"); + request.setIsAvailable(false); + return request; + } + + public static ItemRequestDto itemForUpdate() { + ItemRequestDto request = new ItemRequestDto(); + request.setItemName("testItem1"); + request.setItemDescription("test description1"); + request.setIsAvailable(false); + return request; + } + + public static ItemRequest request2 = new ItemRequest(2L, "Test request description2", user2, + LocalDateTime.of(2025, 8, 8, 15, 10, 20)); + public static RequestOutputDto requestOutputDto2 = new RequestOutputDto( + 2L, "Test request description2", + userBookingDto2, LocalDateTime.of(2025, 8, 8, 15, 10, 20)); + + + public static List requests = List.of(requestOutputDto1); + public static List allRequests = List.of(requestOutputDto1, requestOutputDto2); + + + public static Item item1 = new Item(1L, "testItem1", + "test description1", false, user1, request1); + public static ItemRequestDto itemRequestDto1 = new ItemRequestDto(1L, "testItem1", + "test description1", false, request1.getItemRequestId()); + public static ItemBookerDto itemBookerDto1 = new ItemBookerDto(1L, "testItem1"); + public static ItemResponseDto itemResponseDto1 = new ItemResponseDto(1L, "testItem1", + "test description1", false, user1.getUserId(), request1.getItemRequestId()); + public static ItemResponseDtoWithComments itemWithComments1 = new ItemResponseDtoWithComments( + 1L, "testItem1", "test description1", false, + user1.getUserId(), request1.getItemRequestId(), new BookingResponseDto(), new BookingResponseDto(), new ArrayList<>()); + + + public static ItemBookerDto itemBookerDto2 = new ItemBookerDto(2L, "testItem2"); + public static ItemResponseDto itemResponseDto2 = new ItemResponseDto(2L, "testItem2", + "test description2", true, user2.getUserId(), request2.getItemRequestId()); + + public Collection itemsForOwner = List.of(itemResponseDto1); + public Collection itemsFound = List.of(itemResponseDto2); + + + public static BookingResponseDto responseBooking1 = new BookingResponseDto(1L, + LocalDateTime.of(2025, 7, 7, 14, 40, 35), + LocalDateTime.of(2025, 7, 15, 18, 12, 43), + itemBookerDto1, userBookingDto1, BookingStatus.WAITING); + + public static BookingRequestDto bookingRequestDto1 = new BookingRequestDto(1L, + LocalDateTime.of(2025, 7, 7, 14, 40, 35), + LocalDateTime.of(2025, 7, 15, 18, 12, 43), + itemBookerDto1.getItemId(), userBookingDto1.getUserId(), BookingStatus.WAITING); + + + public static BookingRequestDto booking1ForCreate() { + BookingRequestDto bookingRequestDto = new BookingRequestDto(); + bookingRequestDto.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + bookingRequestDto.setEnd(LocalDateTime.of(2025, 7, 25, 18, 12, 43)); + bookingRequestDto.setItemId(1L); + bookingRequestDto.setBookerId(1L); + bookingRequestDto.setStatus(BookingStatus.WAITING); + return bookingRequestDto; + } + + public static BookingResponseDto responseBooking2 = new BookingResponseDto( + 2L, LocalDateTime.of(2025, 8, 8, 15, 45, 30), + LocalDateTime.of(2025, 8, 18, 12, 10, 40), + itemBookerDto2, userBookingDto2, BookingStatus.APPROVED); + + public static BookingResponseDto responseBooking3 = new BookingResponseDto( + 3L, LocalDateTime.of(2025, 3, 7, 14, 40, 35), + LocalDateTime.of(2025, 5, 25, 18, 12, 43), + itemBookerDto1, userBookingDto1, BookingStatus.WAITING); + + public static BookingResponseDto responseBooking4 = new BookingResponseDto( + 4L, LocalDateTime.of(2025, 9, 7, 14, 40, 35), + LocalDateTime.of(2025, 10, 25, 18, 12, 43), + itemBookerDto1, userBookingDto1, BookingStatus.WAITING); + + public static BookingResponseDto responseBooking5 = new BookingResponseDto( + 5L, LocalDateTime.of(2025, 5, 7, 14, 40, 35), + LocalDateTime.of(2025, 10, 25, 18, 12, 43), + itemBookerDto1, userBookingDto1, BookingStatus.REJECTED); + + public Collection bookings = + Arrays.asList(responseBooking1, responseBooking2, responseBooking3, responseBooking4, responseBooking5); + + public Collection bookingsForOwner = + Arrays.asList(responseBooking1, responseBooking3, responseBooking4, responseBooking5); + + public static CommentResponseCreatedDto createdDto = + new CommentResponseCreatedDto(1L, "Test comment text1", userAuthorDto1.getUserName(), true); + +} + + + diff --git a/server/src/test/java/ru/practicum/shareit/controllersTest/BookingControllerTest.java b/server/src/test/java/ru/practicum/shareit/controllersTest/BookingControllerTest.java new file mode 100644 index 0000000..c0c0bf2 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/controllersTest/BookingControllerTest.java @@ -0,0 +1,162 @@ +package ru.practicum.shareit.controllersTest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.junit.jupiter.api.BeforeEach; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; + +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import ru.practicum.shareit.TestData; +import ru.practicum.shareit.booking.BookingController; + +import ru.practicum.shareit.booking.BookingServiceImpl; +import ru.practicum.shareit.booking.dto.BookingResponseDto; +import ru.practicum.shareit.booking.model.BookingStatus; + +import java.nio.charset.StandardCharsets; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +@ExtendWith(MockitoExtension.class) +public class BookingControllerTest extends TestData { + @Mock + private BookingServiceImpl bookingService; + + @InjectMocks + private BookingController bookingController; + + private MockMvc mvc; + private final ObjectMapper mapper = JsonMapper.builder() + .addModule(new JavaTimeModule()) + .build(); + + @BeforeEach + void setUp() { + mvc = MockMvcBuilders + .standaloneSetup(bookingController) + .build(); + } + + @Test + void createUserTest() throws Exception { + when(bookingService.addBooking(any(), anyLong())) + .thenReturn(responseBooking1); + + MvcResult result = mvc.perform(post("/bookings") + .header("X-Sharer-User-Id", "1") + .content(mapper.writeValueAsString(bookingRequestDto1)) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn(); + + String body = result.getResponse().getContentAsString(); + BookingResponseDto bookingResponseDto = mapper.readValue(body, BookingResponseDto.class); + + assertAll(() -> { + assertNotNull(bookingResponseDto); + assertEquals(bookingRequestDto1.getStart(), bookingResponseDto.getStart()); + assertEquals(bookingRequestDto1.getEnd(), bookingResponseDto.getEnd()); + assertEquals(bookingRequestDto1.getBookerId(), bookingResponseDto.getBooker().getUserId()); + assertEquals(bookingRequestDto1.getItemId(), bookingResponseDto.getItem().getItemId()); + assertEquals(bookingRequestDto1.getStatus(), bookingResponseDto.getStatus()); + }); + + } + + @Test + void bookingApprovedTest() throws Exception { + when(bookingService.bookingApprove(anyLong(), anyLong(), anyBoolean())) + .thenReturn(responseBooking2); + + MvcResult result = mvc.perform(patch("/bookings/2?approved=true") + .header("X-Sharer-User-Id", "2") + .content(mapper.writeValueAsString(bookingRequestDto1)) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn(); + + String body = result.getResponse().getContentAsString(); + BookingResponseDto bookingResponseDto = mapper.readValue(body, BookingResponseDto.class); + + assertAll(() -> { + assertNotNull(bookingResponseDto); + assertEquals(BookingStatus.APPROVED, bookingResponseDto.getStatus()); + }); + } + + @Test + void getBookingByIdTest() throws Exception { + when(bookingService.getBookingByBookingId(anyLong(), anyLong())) + .thenReturn(responseBooking1); + + MvcResult result = mvc.perform(get("/bookings/1") + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn(); + + String body = result.getResponse().getContentAsString(); + BookingResponseDto bookingResponseDto = mapper.readValue(body, BookingResponseDto.class); + + assertAll(() -> { + assertNotNull(bookingResponseDto); + assertEquals(bookingRequestDto1.getBookingId(), bookingResponseDto.getBookingId()); + assertEquals(bookingRequestDto1.getStart(), bookingResponseDto.getStart()); + assertEquals(bookingRequestDto1.getEnd(), bookingResponseDto.getEnd()); + assertEquals(bookingRequestDto1.getBookerId(), bookingResponseDto.getBooker().getUserId()); + assertEquals(bookingRequestDto1.getItemId(), bookingResponseDto.getItem().getItemId()); + assertEquals(bookingRequestDto1.getStatus(), bookingResponseDto.getStatus()); + }); + } + + @Test + void getBookingByUserTest() throws Exception { + + when(bookingService.getBookingsByUser(anyLong(), anyString())) + .thenReturn(bookings); + + mvc.perform(get("/bookings?state=all") + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(5)); + } + + @Test + void getBookingByOwnerTest() throws Exception { + when(bookingService.getBookingsByOwner(anyLong(), anyString())) + .thenReturn(bookingsForOwner); + + mvc.perform(get("/bookings/owner?state=all") + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(4)); + } +} diff --git a/server/src/test/java/ru/practicum/shareit/controllersTest/ItemControllerTest.java b/server/src/test/java/ru/practicum/shareit/controllersTest/ItemControllerTest.java new file mode 100644 index 0000000..73e6ab7 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/controllersTest/ItemControllerTest.java @@ -0,0 +1,187 @@ +package ru.practicum.shareit.controllersTest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import ru.practicum.shareit.TestData; +import ru.practicum.shareit.item.ItemController; +import ru.practicum.shareit.item.ItemServiceImpl; +import ru.practicum.shareit.item.dto.CommentRequestDto; +import ru.practicum.shareit.item.dto.CommentResponseCreatedDto; +import ru.practicum.shareit.item.dto.ItemResponseDto; +import ru.practicum.shareit.item.dto.ItemResponseDtoWithComments; + +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(MockitoExtension.class) +public class ItemControllerTest extends TestData { + @Mock + private ItemServiceImpl itemService; + + @InjectMocks + private ItemController itemController; + + private MockMvc mvc; + private final ObjectMapper mapper = JsonMapper.builder() + .addModule(new JavaTimeModule()) + .build(); + + @BeforeEach + void setUp() { + mvc = MockMvcBuilders + .standaloneSetup(itemController) + .build(); + } + + @Test + void createItem() throws Exception { + when(itemService.createItem(any(), anyLong())) + .thenReturn(itemResponseDto1); + + MvcResult result = mvc.perform(post("/items") + .header("X-Sharer-User-Id", "1") + .content(mapper.writeValueAsString(itemRequestDto1)) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn(); + + String body = result.getResponse().getContentAsString(); + ItemResponseDto responseDto = mapper.readValue(body, ItemResponseDto.class); + + assertAll(() -> { + assertNotNull(responseDto); + assertEquals(itemRequestDto1.getItemDescription(), responseDto.getItemDescription()); + assertEquals(itemRequestDto1.getItemName(), responseDto.getItemName()); + assertEquals(itemRequestDto1.getIsAvailable(), responseDto.getIsAvailable()); + assertEquals(itemRequestDto1.getItemId(), responseDto.getItemId()); + assertEquals(itemRequestDto1.getRequestId(), responseDto.getRequestId()); + }); + } + + @Test + void updateItemTest() throws Exception { + ItemResponseDto forUpdate = new ItemResponseDto(); + forUpdate.setItemId(1L); + forUpdate.setRequestId(1L); + forUpdate.setItemName("Test"); + forUpdate.setItemDescription("Test item description"); + forUpdate.setIsAvailable(true); + + when(itemService.updateItem(anyLong(), any(), anyLong())) + .thenReturn(forUpdate); + + MvcResult result = mvc.perform(patch("/items/1") + .header("X-Sharer-User-Id", "1") + .content(mapper.writeValueAsString(itemRequestDto1)) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn(); + + String body = result.getResponse().getContentAsString(); + ItemResponseDto responseDto = mapper.readValue(body, ItemResponseDto.class); + + assertAll(() -> { + assertNotNull(responseDto); + assertEquals(forUpdate.getItemId(), responseDto.getItemId()); + assertEquals(forUpdate.getRequestId(), responseDto.getRequestId()); + assertEquals(forUpdate.getItemName(), responseDto.getItemName()); + assertEquals(forUpdate.getItemDescription(), responseDto.getItemDescription()); + assertEquals(forUpdate.getIsAvailable(), responseDto.getIsAvailable()); + assertEquals(forUpdate.getRequestId(), responseDto.getRequestId()); + }); + } + + @Test + void getItemTest() throws Exception { + when(itemService.getItemById(anyLong(), anyLong())) + .thenReturn(itemWithComments1); + + MvcResult result = mvc.perform(get("/items/1") + .header("X-Sharer-User-Id", "1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn(); + + String body = result.getResponse().getContentAsString(); + ItemResponseDtoWithComments withComments = mapper.readValue(body, ItemResponseDtoWithComments.class); + + assertAll(() -> { + assertNotNull(withComments); + assertThat(withComments).usingRecursiveComparison().isEqualTo(itemWithComments1); + }); + } + + @Test + void getAllItemsForOwnerTest() throws Exception { + when(itemService.getAllItemsForOwner(anyLong())) + .thenReturn(itemsForOwner); + + mvc.perform(get("/items") + .header("X-Sharer-User-Id", "1")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(1)); + } + + @Test + void searchItemsTest() throws Exception { + when(itemService.searchByText(anyString())) + .thenReturn(itemsFound); + + mvc.perform(get("/items/search?text=") + .param("text", "testItem2")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(1)); + } + + @Test + void addCommentTest() throws Exception { + CommentRequestDto commentRequestDto = new CommentRequestDto(); + commentRequestDto.setText("Test comment text1"); + + when(itemService.addComment(anyLong(), any(), anyLong())) + .thenReturn(createdDto); + + MvcResult result = mvc.perform(post("/items/1/comment") + .header("X-Sharer-User-Id", "1") + .content(mapper.writeValueAsString(commentRequestDto)) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn(); + + String body = result.getResponse().getContentAsString(); + CommentResponseCreatedDto responseDto = mapper.readValue(body, CommentResponseCreatedDto.class); + + assertAll(() -> { + assertNotNull(responseDto); + assertThat(responseDto).usingRecursiveComparison().isEqualTo(createdDto); + }); + + } +} diff --git a/server/src/test/java/ru/practicum/shareit/controllersTest/RequestControllerTest.java b/server/src/test/java/ru/practicum/shareit/controllersTest/RequestControllerTest.java new file mode 100644 index 0000000..5611a33 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/controllersTest/RequestControllerTest.java @@ -0,0 +1,117 @@ +package ru.practicum.shareit.controllersTest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.json.JsonMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import ru.practicum.shareit.TestData; +import ru.practicum.shareit.request.RequestController; +import ru.practicum.shareit.request.RequestServiceImpl; +import ru.practicum.shareit.request.dto.RequestOutputDto; +import ru.practicum.shareit.request.dto.RequestOutputWithItemsDto; + +import java.nio.charset.StandardCharsets; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@ExtendWith(MockitoExtension.class) +public class RequestControllerTest extends TestData { + @Mock + private RequestServiceImpl requestService; + + @InjectMocks + private RequestController controller; + + private MockMvc mvc; + private final ObjectMapper mapper = JsonMapper.builder() + .addModule(new JavaTimeModule()) + .build(); + + @BeforeEach + void setUp() { + mvc = MockMvcBuilders + .standaloneSetup(controller) + .build(); + } + + @Test + void addRequestTest() throws Exception { + when(controller.addRequest(any(), anyLong())) + .thenReturn(requestOutputDto1); + + MvcResult result = mvc.perform(post("/requests") + .header("X-Sharer-User-Id", "1") + .content(mapper.writeValueAsString(requestInputDto1)) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn(); + + String body = result.getResponse().getContentAsString(); + RequestOutputDto responseDto = mapper.readValue(body, RequestOutputDto.class); + + assertAll(() -> { + assertNotNull(responseDto); + assertThat(responseDto).usingRecursiveComparison().isEqualTo(requestOutputDto1); + }); + } + + @Test + void getRequestByUserTest() throws Exception { + when(controller.getRequestByUser(anyLong())) + .thenReturn(requests); + + mvc.perform(get("/requests") + .header("X-Sharer-User-Id", "1")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(1)); + } + + @Test + void getAllRequestsTest() throws Exception { + when(controller.getAllRequests()) + .thenReturn(allRequests); + + mvc.perform(get("/requests/all")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.length()").value(2)); + } + + @Test + void getRequestByIdTest() throws Exception { + when(controller.getRequestById(anyLong())) + .thenReturn(requestWithItemsDto1); + + MvcResult result = mvc.perform(get("/requests/1") + .header("X-Sharer-User-Id", "1")) + .andExpect(status().isOk()) + .andReturn(); + + String body = result.getResponse().getContentAsString(); + RequestOutputWithItemsDto responseDto = mapper.readValue(body, RequestOutputWithItemsDto.class); + + assertAll(() -> { + assertNotNull(responseDto); + assertThat(responseDto).usingRecursiveComparison().isEqualTo(requestWithItemsDto1); + }); + } +} diff --git a/server/src/test/java/ru/practicum/shareit/controllersTest/UserControllerTest.java b/server/src/test/java/ru/practicum/shareit/controllersTest/UserControllerTest.java new file mode 100644 index 0000000..fc50c60 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/controllersTest/UserControllerTest.java @@ -0,0 +1,131 @@ +package ru.practicum.shareit.controllersTest; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import ru.practicum.shareit.TestData; +import ru.practicum.shareit.user.UserController; +import ru.practicum.shareit.user.UserServiceImpl; + +import org.springframework.http.MediaType; +import ru.practicum.shareit.user.dto.UserResponseDto; + +import java.nio.charset.StandardCharsets; + +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.when; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + + +@ExtendWith(MockitoExtension.class) +class UserControllerTest extends TestData { + @Mock + private UserServiceImpl userService; + + @InjectMocks + private UserController controller; + + private final ObjectMapper mapper = new ObjectMapper(); + + private MockMvc mvc; + + + @BeforeEach + void setUp() { + mvc = MockMvcBuilders + .standaloneSetup(controller) + .build(); + } + + @Test + void createUserTest() throws Exception { + when(userService.createUser(any())) + .thenReturn(userResponseDto2); + + mvc.perform(post("/users") + .content(mapper.writeValueAsString(userRequestDto2)) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", is(userResponseDto2.getUserId()), Long.class)) + .andExpect(jsonPath("$.name", is(userResponseDto2.getUserName()), String.class)) + .andExpect(jsonPath("$.email", is(userResponseDto2.getUserEmail()), String.class)); + } + + @Test + void getUserTest() throws Exception { + when(userService.getUserById(any())) + .thenReturn(userResponseDto1); + + mvc.perform(get("/users/1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id", is(userResponseDto1.getUserId()), Long.class)) + .andExpect(jsonPath("$.name", is(userResponseDto1.getUserName()), String.class)) + .andExpect(jsonPath("$.email", is(userResponseDto1.getUserEmail()), String.class)); + + } + + @Test + void updateUserTest() throws Exception { + UserResponseDto forUpdate = new UserResponseDto(); + forUpdate.setUserId(1L); + forUpdate.setUserName("UpdatedName"); + forUpdate.setUserEmail("UpdatedEmail@mail.ru"); + + when(userService.updateUser(anyLong(), any())) + .thenReturn(forUpdate); + + MvcResult result = mvc.perform(patch("/users/1") + .content(mapper.writeValueAsString(forUpdate)) + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andReturn(); + + String responseBody = result.getResponse().getContentAsString(); + UserResponseDto responseDto = mapper.readValue(responseBody, UserResponseDto.class); + assertAll(() -> { + assertNotNull(responseDto); + assertEquals("UpdatedName", responseDto.getUserName()); + assertEquals("UpdatedEmail@mail.ru", responseDto.getUserEmail()); + }); + } + + @Test + void deleteUserTest() throws Exception { + mvc.perform(delete("/users/1") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + } + + @Test + void getAllUsersTest() throws Exception { + mvc.perform(delete("/users") + .characterEncoding(StandardCharsets.UTF_8) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()); + } +} + + + diff --git a/src/test/java/ru/practicum/shareit/mapperTest/BookingMapperTest.java b/server/src/test/java/ru/practicum/shareit/mapperTest/BookingMapperTest.java similarity index 96% rename from src/test/java/ru/practicum/shareit/mapperTest/BookingMapperTest.java rename to server/src/test/java/ru/practicum/shareit/mapperTest/BookingMapperTest.java index 5744f96..83ee4ed 100644 --- a/src/test/java/ru/practicum/shareit/mapperTest/BookingMapperTest.java +++ b/server/src/test/java/ru/practicum/shareit/mapperTest/BookingMapperTest.java @@ -11,12 +11,12 @@ import ru.practicum.shareit.item.dto.ItemBookerDto; import ru.practicum.shareit.item.mapper.ItemMapper; import ru.practicum.shareit.item.model.Item; -import ru.practicum.shareit.request.ItemRequest; +import ru.practicum.shareit.request.model.ItemRequest; import ru.practicum.shareit.user.dto.UserBookingDto; import ru.practicum.shareit.user.mapper.UserMapper; import ru.practicum.shareit.user.model.User; -import java.time.LocalDate; + import java.time.LocalDateTime; import static org.junit.jupiter.api.Assertions.assertAll; @@ -36,7 +36,7 @@ public class BookingMapperTest { User user = new User(1L, "testName1", "test1@test.com"); ItemRequest itemRequest = new ItemRequest(1L, "testDescription1", - 1L, LocalDate.of(2025, 6, 25)); + user, LocalDateTime.of(2025, 6, 25, 15, 25)); Item item = new Item(1L, "TestName1", "Test description1", true, user, itemRequest); LocalDateTime startTime = LocalDateTime.of(2025, 6, 25, 12, 30); diff --git a/src/test/java/ru/practicum/shareit/mapperTest/CommentMapperTest.java b/server/src/test/java/ru/practicum/shareit/mapperTest/CommentMapperTest.java similarity index 94% rename from src/test/java/ru/practicum/shareit/mapperTest/CommentMapperTest.java rename to server/src/test/java/ru/practicum/shareit/mapperTest/CommentMapperTest.java index 6b7179f..2ad1ac0 100644 --- a/src/test/java/ru/practicum/shareit/mapperTest/CommentMapperTest.java +++ b/server/src/test/java/ru/practicum/shareit/mapperTest/CommentMapperTest.java @@ -9,12 +9,12 @@ import ru.practicum.shareit.item.mapper.CommentMapper; import ru.practicum.shareit.item.model.Comment; import ru.practicum.shareit.item.model.Item; -import ru.practicum.shareit.request.ItemRequest; +import ru.practicum.shareit.request.model.ItemRequest; import ru.practicum.shareit.user.dto.UserAuthorDto; import ru.practicum.shareit.user.mapper.UserMapper; import ru.practicum.shareit.user.model.User; -import java.time.LocalDate; +import java.time.LocalDateTime; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -30,7 +30,7 @@ public class CommentMapperTest { User user = new User(1L, "testName1", "test1@test.com"); ItemRequest itemRequest = new ItemRequest(1L, "testDescription1", - 1L, LocalDate.of(2025, 6, 25)); + user, LocalDateTime.of(2025, 6, 25, 15, 25)); Item item = new Item(1L, "TestName1", "Test description1", true, user, itemRequest); diff --git a/src/test/java/ru/practicum/shareit/mapperTest/ItemMapperTest.java b/server/src/test/java/ru/practicum/shareit/mapperTest/ItemMapperTest.java similarity index 96% rename from src/test/java/ru/practicum/shareit/mapperTest/ItemMapperTest.java rename to server/src/test/java/ru/practicum/shareit/mapperTest/ItemMapperTest.java index 5143e32..1c2aec8 100644 --- a/src/test/java/ru/practicum/shareit/mapperTest/ItemMapperTest.java +++ b/server/src/test/java/ru/practicum/shareit/mapperTest/ItemMapperTest.java @@ -6,10 +6,11 @@ import ru.practicum.shareit.item.dto.*; import ru.practicum.shareit.item.mapper.ItemMapper; import ru.practicum.shareit.item.model.Item; -import ru.practicum.shareit.request.ItemRequest; +import ru.practicum.shareit.request.model.ItemRequest; import ru.practicum.shareit.user.model.User; -import java.time.LocalDate; + +import java.time.LocalDateTime; import static org.junit.jupiter.api.Assertions.*; @@ -22,7 +23,7 @@ public class ItemMapperTest { User user = new User(1L, "testName1", "test1@test.com"); ItemRequest itemRequest = new ItemRequest(1L, "testDescription1", - 1L, LocalDate.of(2025, 6, 25)); + user, LocalDateTime.of(2025, 6, 25, 15, 25)); @Test public void itemRequestDtoToItemTest() { diff --git a/src/test/java/ru/practicum/shareit/mapperTest/UserMapperTest.java b/server/src/test/java/ru/practicum/shareit/mapperTest/UserMapperTest.java similarity index 100% rename from src/test/java/ru/practicum/shareit/mapperTest/UserMapperTest.java rename to server/src/test/java/ru/practicum/shareit/mapperTest/UserMapperTest.java diff --git a/server/src/test/java/ru/practicum/shareit/serviceTest/BookingServiceTest.java b/server/src/test/java/ru/practicum/shareit/serviceTest/BookingServiceTest.java new file mode 100644 index 0000000..f3115e2 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/serviceTest/BookingServiceTest.java @@ -0,0 +1,550 @@ +package ru.practicum.shareit.serviceTest; + +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import ru.practicum.shareit.TestData; +import ru.practicum.shareit.booking.BookingServiceImpl; +import ru.practicum.shareit.booking.dto.BookingRequestDto; +import ru.practicum.shareit.booking.dto.BookingResponseDto; +import ru.practicum.shareit.booking.model.BookingStatus; +import ru.practicum.shareit.exception.AnotherUserException; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.exception.ValidationException; +import ru.practicum.shareit.item.ItemServiceImpl; +import ru.practicum.shareit.item.dto.ItemRequestDto; +import ru.practicum.shareit.item.dto.ItemResponseDto; +import ru.practicum.shareit.user.UserServiceImpl; +import ru.practicum.shareit.user.dto.UserResponseDto; + + +import java.time.LocalDateTime; +import java.util.Collection; + +import static org.junit.jupiter.api.Assertions.*; + +@Transactional +@SpringBootTest(properties = "jdbc.url=jdbc:h2:mem:testdb;MODE=PostgreSQL", + webEnvironment = SpringBootTest.WebEnvironment.NONE) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class BookingServiceTest extends TestData { + private final BookingServiceImpl bookingService; + private final UserServiceImpl userService; + private final ItemServiceImpl itemService; + + @Test + public void addBookingTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + BookingRequestDto bookingRequestDto = new BookingRequestDto(); + bookingRequestDto.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + bookingRequestDto.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + bookingRequestDto.setItemId(item.getItemId()); + BookingResponseDto bookingSaved = bookingService.addBooking(bookingRequestDto, user.getUserId()); + + assertAll(() -> { + assertNotNull(bookingSaved); + assertEquals(bookingRequestDto.getStart(), bookingSaved.getStart()); + assertEquals(bookingRequestDto.getEnd(), bookingSaved.getEnd()); + assertEquals(BookingStatus.WAITING, bookingSaved.getStatus()); + assertEquals(user.getUserId(), bookingSaved.getBooker().getUserId()); + assertEquals(item.getItemId(), bookingSaved.getItem().getItemId()); + }); + } + + @Test + public void bookingApproveTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + BookingRequestDto bookingRequestDto = new BookingRequestDto(); + bookingRequestDto.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + bookingRequestDto.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + bookingRequestDto.setItemId(item.getItemId()); + BookingResponseDto bookingSaved = bookingService.addBooking(bookingRequestDto, user.getUserId()); + + BookingResponseDto bookingResponseDto = bookingService.bookingApprove(bookingSaved.getBookingId(), user.getUserId(), false); + + assertAll(() -> { + assertNotNull(bookingResponseDto); + assertEquals(BookingStatus.APPROVED, bookingResponseDto.getStatus()); + }); + } + + @Test + public void bookingApproveWithWrongOwnerTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + BookingRequestDto bookingRequestDto = new BookingRequestDto(); + bookingRequestDto.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + bookingRequestDto.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + bookingRequestDto.setItemId(item.getItemId()); + BookingResponseDto bookingSaved = bookingService.addBooking(bookingRequestDto, user.getUserId()); + + assertThrows(AnotherUserException.class, () -> { + BookingResponseDto bookingResponseDto = + bookingService.bookingApprove(bookingSaved.getBookingId(), 25L, false); + }); + } + + @Test + public void bookingApproveWithUnavailableItemTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + BookingRequestDto bookingRequestDto = new BookingRequestDto(); + bookingRequestDto.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + bookingRequestDto.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + bookingRequestDto.setItemId(item.getItemId()); + BookingResponseDto bookingSaved = bookingService.addBooking(bookingRequestDto, user.getUserId()); + + ItemRequestDto itemForUpdate = new ItemRequestDto(); + itemForUpdate.setIsAvailable(false); + itemService.updateItem(item.getItemId(), itemForUpdate, user.getUserId()); + + assertThrows(ValidationException.class, () -> + bookingService.bookingApprove(bookingSaved.getBookingId(), user.getUserId(), false)); + } + + @Test + public void getBookingByBookingIdTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + BookingRequestDto bookingRequestDto = new BookingRequestDto(); + bookingRequestDto.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + bookingRequestDto.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + bookingRequestDto.setItemId(item.getItemId()); + BookingResponseDto bookingSaved = bookingService.addBooking(bookingRequestDto, user.getUserId()); + + assertAll(() -> { + assertNotNull(bookingSaved); + assertEquals(LocalDateTime.of(2025, 8, 8, 15, 45, 30), bookingSaved.getStart()); + assertEquals(LocalDateTime.of(2025, 8, 18, 12, 10, 40), bookingSaved.getEnd()); + assertEquals(BookingStatus.WAITING, bookingSaved.getStatus()); + assertEquals(user.getUserId(), bookingSaved.getBooker().getUserId()); + assertEquals(item.getItemId(), bookingSaved.getItem().getItemId()); + }); + } + + @Test + public void getBookingByUserIdWithWrongUserTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + UserResponseDto user1 = userService.createUser(user2ForCreate()); + + BookingRequestDto bookingRequestDto = new BookingRequestDto(); + bookingRequestDto.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + bookingRequestDto.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + bookingRequestDto.setItemId(item.getItemId()); + BookingResponseDto bookingResponseDto = bookingService.addBooking(bookingRequestDto, user.getUserId()); + + assertThrows(AnotherUserException.class, () -> + bookingService.getBookingByBookingId(bookingResponseDto.getBookingId(), user1.getUserId())); + } + + @Test + public void getBookingsByUserWithWrongUserTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + BookingRequestDto bookingRequestDto = new BookingRequestDto(); + bookingRequestDto.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + bookingRequestDto.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + bookingRequestDto.setItemId(item.getItemId()); + bookingService.addBooking(bookingRequestDto, user.getUserId()); + + assertThrows(NotFoundException.class, () -> + bookingService.getBookingsByUser(125L, "ALL")); + } + + @Test + public void getBookingsByUserALLTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + BookingRequestDto booking1 = booking1ForCreate(); + booking1.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + booking1.setEnd(LocalDateTime.of(2025, 7, 25, 18, 12, 43)); + booking1.setItemId(item.getItemId()); + booking1.setBookerId(user.getUserId()); + bookingService.addBooking(booking1, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + booking2.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + booking2.setItemId(item.getItemId()); + booking2.setBookerId(user.getUserId()); + bookingService.addBooking(booking2, user.getUserId()); + + BookingRequestDto booking3 = booking1ForCreate(); + booking3.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + booking3.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + booking3.setItemId(item.getItemId()); + booking3.setBookerId(user.getUserId()); + bookingService.addBooking(booking3, user.getUserId()); + + Collection bookings = bookingService.getBookingsByUser(user.getUserId(), "ALL"); + + assertAll(() -> { + assertNotNull(bookings); + assertEquals(3, bookings.size()); + }); + } + + @Test + public void getBookingsByUserCURRENTTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + BookingRequestDto booking1 = booking1ForCreate(); + booking1.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + booking1.setEnd(LocalDateTime.of(2025, 7, 25, 18, 12, 43)); + booking1.setItemId(item.getItemId()); + booking1.setBookerId(user.getUserId()); + bookingService.addBooking(booking1, user.getUserId()); + + Collection bookings = bookingService.getBookingsByUser(user.getUserId(), "CURRENT"); + + assertAll(() -> { + assertNotNull(bookings); + assertEquals(1, bookings.size()); + }); + } + + @Test + public void getBookingsByUserPastTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + BookingRequestDto booking1 = booking1ForCreate(); + booking1.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + booking1.setEnd(LocalDateTime.of(2025, 7, 25, 18, 12, 43)); + booking1.setItemId(item.getItemId()); + booking1.setBookerId(user.getUserId()); + bookingService.addBooking(booking1, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 3, 7, 14, 40, 35)); + booking2.setEnd(LocalDateTime.of(2025, 5, 25, 18, 12, 43)); + booking2.setItemId(item.getItemId()); + booking2.setBookerId(user.getUserId()); + bookingService.addBooking(booking2, user.getUserId()); + + Collection bookings = bookingService.getBookingsByUser(user.getUserId(), "PAST"); + + assertAll(() -> { + assertNotNull(bookings); + assertEquals(1, bookings.size()); + }); + } + + @Test + public void getBookingsByUserFutureTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + BookingRequestDto booking1 = booking1ForCreate(); + booking1.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + booking1.setEnd(LocalDateTime.of(2025, 7, 25, 18, 12, 43)); + booking1.setItemId(item.getItemId()); + booking1.setBookerId(user.getUserId()); + bookingService.addBooking(booking1, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + booking2.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + booking2.setItemId(item.getItemId()); + booking2.setBookerId(user.getUserId()); + bookingService.addBooking(booking2, user.getUserId()); + + + Collection bookings = bookingService.getBookingsByUser(user.getUserId(), "FUTURE"); + + assertAll(() -> { + assertNotNull(bookings); + assertEquals(1, bookings.size()); + }); + } + + @Test + public void getBookingsByUserWaitingTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + BookingRequestDto booking1 = booking1ForCreate(); + booking1.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + booking1.setEnd(LocalDateTime.of(2025, 7, 25, 18, 12, 43)); + booking1.setItemId(item.getItemId()); + booking1.setBookerId(user.getUserId()); + bookingService.addBooking(booking1, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + booking2.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + booking2.setItemId(item.getItemId()); + booking2.setBookerId(user.getUserId()); + bookingService.addBooking(booking2, user.getUserId()); + + BookingRequestDto booking3 = booking1ForCreate(); + booking3.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + booking3.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + booking3.setItemId(item.getItemId()); + booking3.setBookerId(user.getUserId()); + bookingService.addBooking(booking3, user.getUserId()); + + Collection bookings = bookingService.getBookingsByUser(user.getUserId(), "WAITING"); + + assertAll(() -> { + assertNotNull(bookings); + assertEquals(3, bookings.size()); + }); + } + + @Test + public void getBookingsByUserRejectedTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + ItemResponseDto item2 = itemService.createItem(item2ForCreate(), user.getUserId()); + + BookingRequestDto booking1 = booking1ForCreate(); + booking1.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + booking1.setEnd(LocalDateTime.of(2025, 7, 25, 18, 12, 43)); + booking1.setItemId(item.getItemId()); + booking1.setBookerId(user.getUserId()); + bookingService.addBooking(booking1, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + booking2.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + booking2.setItemId(item.getItemId()); + booking2.setBookerId(user.getUserId()); + bookingService.addBooking(booking2, user.getUserId()); + + BookingRequestDto booking3 = booking1ForCreate(); + booking3.setStart(LocalDateTime.of(2025, 5, 7, 14, 40, 35)); + booking3.setEnd(LocalDateTime.of(2025, 10, 25, 18, 12, 43)); + booking3.setItemId(item2.getItemId()); + booking3.setBookerId(user.getUserId()); + assertThrows(ValidationException.class, + () -> bookingService.addBooking(booking3, user.getUserId())); + + Collection bookings = bookingService.getBookingsByUser(user.getUserId(), "REJECTED"); + + assertAll(() -> { + assertNotNull(bookings); + assertEquals(1, bookings.size()); + }); + } + + @Test + public void addBookingWithUnavailableItemTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + BookingRequestDto bookingRequestDto = new BookingRequestDto(); + bookingRequestDto.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + bookingRequestDto.setEnd(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + bookingRequestDto.setItemId(item.getItemId()); + ItemResponseDto itemUpdated = itemService.updateItem(item.getItemId(), itemForUpdate(), user.getUserId()); + + assertThrows(ValidationException.class, () -> bookingService.addBooking(bookingRequestDto, user.getUserId())); + + } + + @Test + public void getBookingByOwnerTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + UserResponseDto user1 = userService.createUser(user2ForCreate()); + + BookingRequestDto booking1 = booking1ForCreate(); + booking1.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + booking1.setEnd(LocalDateTime.of(2025, 7, 25, 18, 12, 43)); + booking1.setItemId(item.getItemId()); + booking1.setBookerId(user1.getUserId()); + bookingService.addBooking(booking1, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + booking2.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + booking2.setItemId(item.getItemId()); + booking2.setBookerId(user.getUserId()); + bookingService.addBooking(booking2, user.getUserId()); + + BookingRequestDto booking3 = booking1ForCreate(); + booking3.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + booking3.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + booking3.setItemId(item.getItemId()); + booking3.setBookerId(user.getUserId()); + bookingService.addBooking(booking3, user.getUserId()); + + Collection bookings = bookingService.getBookingsByOwner(item.getOwnerId(), "ALL"); + + assertAll(() -> { + assertNotNull(bookings); + assertEquals(3, bookings.size()); + }); + } + + @Test + public void getBookingByOwnerCurrentTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + UserResponseDto user1 = userService.createUser(user2ForCreate()); + + BookingRequestDto booking1 = booking1ForCreate(); + booking1.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + booking1.setEnd(LocalDateTime.of(2025, 7, 25, 18, 12, 43)); + booking1.setItemId(item.getItemId()); + booking1.setBookerId(user1.getUserId()); + bookingService.addBooking(booking1, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 3, 7, 14, 40, 35)); + booking2.setEnd(LocalDateTime.of(2025, 5, 25, 18, 12, 43)); + booking2.setItemId(item.getItemId()); + booking2.setBookerId(user1.getUserId()); + bookingService.addBooking(booking2, user.getUserId()); + + Collection bookings = bookingService.getBookingsByOwner(item.getOwnerId(), "CURRENT"); + + assertAll(() -> { + assertNotNull(bookings); + assertEquals(1, bookings.size()); + }); + } + + @Test + public void getBookingByOwnerPastTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + UserResponseDto user1 = userService.createUser(user2ForCreate()); + + BookingRequestDto booking1 = booking1ForCreate(); + booking1.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + booking1.setEnd(LocalDateTime.of(2025, 7, 25, 18, 12, 43)); + booking1.setItemId(item.getItemId()); + booking1.setBookerId(user1.getUserId()); + bookingService.addBooking(booking1, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 3, 7, 14, 40, 35)); + booking2.setEnd(LocalDateTime.of(2025, 5, 25, 18, 12, 43)); + booking2.setItemId(item.getItemId()); + booking2.setBookerId(user1.getUserId()); + bookingService.addBooking(booking2, user.getUserId()); + + Collection bookings = bookingService.getBookingsByOwner(item.getOwnerId(), "PAST"); + + assertAll(() -> { + assertNotNull(bookings); + assertEquals(1, bookings.size()); + }); + } + + @Test + public void getBookingByOwnerFutureTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + UserResponseDto user1 = userService.createUser(user2ForCreate()); + + BookingRequestDto booking1 = booking1ForCreate(); + booking1.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + booking1.setEnd(LocalDateTime.of(2025, 7, 25, 18, 12, 43)); + booking1.setItemId(item.getItemId()); + booking1.setBookerId(user1.getUserId()); + bookingService.addBooking(booking1, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + booking2.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + booking2.setItemId(item.getItemId()); + booking2.setBookerId(user.getUserId()); + bookingService.addBooking(booking2, user.getUserId()); + + Collection bookings = bookingService.getBookingsByOwner(item.getOwnerId(), "FUTURE"); + + assertAll(() -> { + assertNotNull(bookings); + assertEquals(1, bookings.size()); + }); + } + + @Test + public void getBookingByOwnerWaitingTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + UserResponseDto user1 = userService.createUser(user2ForCreate()); + + BookingRequestDto booking1 = booking1ForCreate(); + booking1.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + booking1.setEnd(LocalDateTime.of(2025, 7, 25, 18, 12, 43)); + booking1.setItemId(item.getItemId()); + booking1.setBookerId(user1.getUserId()); + bookingService.addBooking(booking1, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + booking2.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + booking2.setItemId(item.getItemId()); + booking2.setBookerId(user.getUserId()); + bookingService.addBooking(booking2, user.getUserId()); + + BookingRequestDto booking3 = booking1ForCreate(); + booking3.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + booking3.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + booking3.setItemId(item.getItemId()); + booking3.setBookerId(user.getUserId()); + bookingService.addBooking(booking3, user.getUserId()); + + Collection bookings = bookingService.getBookingsByOwner(item.getOwnerId(), "WAITING"); + assertAll(() -> { + assertNotNull(bookings); + assertEquals(3, bookings.size()); + }); + } + + @Test + public void getBookingByOwnerRejectedTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + UserResponseDto user1 = userService.createUser(user2ForCreate()); + ItemResponseDto item2 = itemService.createItem(item2ForCreate(), user.getUserId()); + + BookingRequestDto booking1 = booking1ForCreate(); + booking1.setStart(LocalDateTime.of(2025, 7, 7, 14, 40, 35)); + booking1.setEnd(LocalDateTime.of(2025, 7, 25, 18, 12, 43)); + booking1.setItemId(item.getItemId()); + booking1.setBookerId(user1.getUserId()); + bookingService.addBooking(booking1, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 8, 8, 15, 45, 30)); + booking2.setEnd(LocalDateTime.of(2025, 8, 18, 12, 10, 40)); + booking2.setItemId(item.getItemId()); + booking2.setBookerId(user1.getUserId()); + bookingService.addBooking(booking2, user.getUserId()); + + BookingRequestDto booking3 = booking1ForCreate(); + booking3.setStart(LocalDateTime.of(2025, 5, 7, 14, 40, 35)); + booking3.setEnd(LocalDateTime.of(2025, 10, 25, 18, 12, 43)); + booking3.setItemId(item2.getItemId()); + booking3.setBookerId(user1.getUserId()); + assertThrows(ValidationException.class, + () -> bookingService.addBooking(booking3, user.getUserId())); + + + Collection bookings = bookingService.getBookingsByOwner(item.getOwnerId(), "REJECTED"); + + assertAll(() -> { + assertNotNull(bookings); + assertEquals(1, bookings.size()); + }); + } + +} diff --git a/server/src/test/java/ru/practicum/shareit/serviceTest/ItemServiceTest.java b/server/src/test/java/ru/practicum/shareit/serviceTest/ItemServiceTest.java new file mode 100644 index 0000000..abb2b5d --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/serviceTest/ItemServiceTest.java @@ -0,0 +1,491 @@ +package ru.practicum.shareit.serviceTest; + +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import ru.practicum.shareit.TestData; +import ru.practicum.shareit.booking.BookingServiceImpl; +import ru.practicum.shareit.booking.dto.BookingRequestDto; +import ru.practicum.shareit.booking.dto.BookingResponseDto; +import ru.practicum.shareit.exception.AnotherUserException; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.exception.ValidationException; +import ru.practicum.shareit.item.ItemServiceImpl; +import ru.practicum.shareit.item.dto.*; +import ru.practicum.shareit.request.RequestServiceImpl; +import ru.practicum.shareit.request.dto.RequestInputDto; +import ru.practicum.shareit.request.dto.RequestOutputDto; +import ru.practicum.shareit.user.UserServiceImpl; +import ru.practicum.shareit.user.dto.UserResponseDto; + +import java.time.LocalDateTime; +import java.util.Collection; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +@Transactional +@SpringBootTest(properties = "jdbc.url=jdbc:h2:mem:testdb;MODE=PostgreSQL", + webEnvironment = SpringBootTest.WebEnvironment.NONE) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class ItemServiceTest extends TestData { + private final ItemServiceImpl itemService; + private final UserServiceImpl userService; + private final RequestServiceImpl requestService; + private final BookingServiceImpl bookingService; + + @Test + public void addItemTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + assertAll(() -> { + assertNotNull(saved); + assertThat(forCreation).usingRecursiveComparison() + .ignoringFields("itemId").isEqualTo(saved); + }); + } + + @Test + void addItem_WithNullOwnerIdTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + + assertThrows(ValidationException.class, + () -> itemService.createItem(forCreation, null)); + } + + @Test + void addItem_WithUserNotFoundTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + + assertThrows(NotFoundException.class, + () -> itemService.createItem(forCreation, 573L)); + } + + @Test + void addItem_WithRequestIdTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + RequestInputDto request = new RequestInputDto(); + request.setRequestDescription("Test request description"); + RequestOutputDto reqSaved = requestService.addItemRequest(request, user.getUserId()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + forCreation.setRequestId(reqSaved.getItemRequestId()); + + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + assertAll(() -> { + assertNotNull(saved); + assertEquals(reqSaved.getItemRequestId(), saved.getRequestId()); + }); + } + + @Test + void addItem_WithWrongRequestIdTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + forCreation.setRequestId(525L); + + assertThrows(NotFoundException.class, + () -> itemService.createItem(forCreation, user.getUserId())); + + } + + @Test + void getItemByIdTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + ItemResponseDtoWithComments found = itemService.getItemById(saved.getItemId(), user.getUserId()); + + assertAll(() -> { + assertNotNull(found); + assertThat(found.getItemId()).isEqualTo(saved.getItemId()); + }); + } + + @Test + void getItemById_WithWrongItemIdTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + ItemResponseDtoWithComments found = itemService.getItemById(saved.getItemId(), user.getUserId()); + + assertThrows(NotFoundException.class, + () -> itemService.getItemById(535L, user.getUserId())); + } + + @Test + void getItemById_WithOwnerIdTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + ItemResponseDtoWithComments found = itemService.getItemById(saved.getItemId(), user.getUserId()); + + assertAll(() -> { + assertNotNull(found); + assertNotNull(found.getComments()); + }); + } + + @Test + void getItemById_WithWrongOwnerIdTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + ItemResponseDtoWithComments found = itemService.getItemById(saved.getItemId(), 237L); + assertAll(() -> { + assertNotNull(found); + assertEquals(0, found.getComments().size()); + }); + } + + @Test + void getItemById_WithOwnerId_AndLastBookingTest() { + UserResponseDto user = userService.createUser(userForCreate()); + UserResponseDto user2 = userService.createUser(user2ForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 3, 7, 14, 40, 35)); + booking2.setEnd(LocalDateTime.of(2025, 5, 25, 18, 12, 43)); + booking2.setItemId(saved.getItemId()); + booking2.setBookerId(user2.getUserId()); + bookingService.addBooking(booking2, user2.getUserId()); + + ItemResponseDtoWithComments found = itemService.getItemById(saved.getItemId(), user.getUserId()); + assertAll(() -> { + assertNotNull(found); + assertNotNull(found.getLastBooking()); + }); + } + + @Test + void getItemById_WithOwnerId_AndNextBookingTest() { + UserResponseDto user = userService.createUser(userForCreate()); + UserResponseDto user2 = userService.createUser(user2ForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 9, 7, 14, 40, 35)); + booking2.setEnd(LocalDateTime.of(2025, 10, 25, 18, 12, 43)); + booking2.setItemId(saved.getItemId()); + booking2.setBookerId(user2.getUserId()); + bookingService.addBooking(booking2, user2.getUserId()); + + ItemResponseDtoWithComments found = itemService.getItemById(saved.getItemId(), user.getUserId()); + assertAll(() -> { + assertNotNull(found); + assertNotNull(found.getNextBooking()); + }); + } + + @Test + void updateItemTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + ItemRequestDto forUpdate = new ItemRequestDto(); + forUpdate.setItemName("TestName"); + forUpdate.setItemDescription("Test description"); + forUpdate.setIsAvailable(true); + + ItemResponseDto updated = itemService.updateItem(saved.getItemId(), forUpdate, user.getUserId()); + + assertAll(() -> { + assertNotNull(updated); + assertThat(forUpdate).usingRecursiveComparison() + .ignoringFields("itemId", "requestId").isEqualTo(updated); + }); + } + + @Test + void updateItem_WithWrongOwnerTest() { + UserResponseDto user = userService.createUser(userForCreate()); + UserResponseDto user2 = userService.createUser(user2ForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + ItemRequestDto forUpdate = new ItemRequestDto(); + forUpdate.setItemName("TestName"); + forUpdate.setItemDescription("Test description"); + forUpdate.setIsAvailable(true); + + assertThrows(AnotherUserException.class, + () -> itemService.updateItem(saved.getItemId(), forUpdate, user2.getUserId())); + + } + + @Test + void updateItem_WithoutItemNameTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + ItemRequestDto forUpdate = new ItemRequestDto(); + forUpdate.setItemDescription("Test item description"); + forUpdate.setIsAvailable(true); + + ItemResponseDto updated = itemService.updateItem(saved.getItemId(), forUpdate, user.getUserId()); + assertAll(() -> { + assertNotNull(updated); + assertThat(forUpdate).usingRecursiveComparison() + .ignoringFields("itemId", "requestId", "itemName").isEqualTo(updated); + }); + } + + @Test + void updateItem_WithoutItemDescriptionTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + ItemRequestDto forUpdate = new ItemRequestDto(); + forUpdate.setItemName("Test"); + forUpdate.setIsAvailable(true); + + ItemResponseDto updated = itemService.updateItem(saved.getItemId(), forUpdate, user.getUserId()); + assertAll(() -> { + assertNotNull(updated); + assertThat(forUpdate).usingRecursiveComparison() + .ignoringFields("itemId", "requestId", "itemDescription").isEqualTo(updated); + }); + } + + @Test + void updateItem_WithoutIsAvailableTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + ItemRequestDto forUpdate = new ItemRequestDto(); + forUpdate.setItemName("Test"); + forUpdate.setItemDescription("Test item description"); + + ItemResponseDto updated = itemService.updateItem(saved.getItemId(), forUpdate, user.getUserId()); + assertAll(() -> { + assertNotNull(updated); + assertThat(forUpdate).usingRecursiveComparison() + .ignoringFields("itemId", "requestId", "isAvailable").isEqualTo(updated); + }); + } + + @Test + void getAllItemsForOwnerTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + ItemRequestDto forCreation2 = new ItemRequestDto(); + forCreation2.setItemName("Test2"); + forCreation2.setItemDescription("Test item description2"); + forCreation2.setIsAvailable(true); + ItemResponseDto saved2 = itemService.createItem(forCreation2, user.getUserId()); + + Collection items = itemService.getAllItemsForOwner(user.getUserId()); + assertAll(() -> { + assertNotNull(items); + assertEquals(2, items.size()); + }); + } + + @Test + void getAllItemsForOwner_WithWrongUserTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + ItemRequestDto forCreation2 = new ItemRequestDto(); + forCreation2.setItemName("Test2"); + forCreation2.setItemDescription("Test item description2"); + forCreation2.setIsAvailable(true); + ItemResponseDto saved2 = itemService.createItem(forCreation2, user.getUserId()); + + assertThrows(NotFoundException.class, + () -> itemService.getAllItemsForOwner(458L)); + } + + @Test + void addCommentTest() { + UserResponseDto user = userService.createUser(userForCreate()); + UserResponseDto user2 = userService.createUser(user2ForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 3, 7, 14, 40, 35)); + booking2.setEnd(LocalDateTime.of(2025, 5, 25, 18, 12, 43)); + booking2.setItemId(saved.getItemId()); + booking2.setBookerId(user2.getUserId()); + BookingResponseDto bookingSaved = bookingService.addBooking(booking2, user.getUserId()); + bookingService.bookingApprove(bookingSaved.getBookingId(), user.getUserId(), false); + + CommentRequestDto comment = new CommentRequestDto(); + comment.setText("Test comment"); + + CommentResponseCreatedDto created = itemService.addComment(user.getUserId(), comment, saved.getItemId()); + + assertAll(() -> { + assertNotNull(created); + assertEquals("Test comment", created.getText()); + }); + } + + @Test + void addComment_WithWrongCommentatorTest() { + UserResponseDto user = userService.createUser(userForCreate()); + UserResponseDto user2 = userService.createUser(user2ForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 3, 7, 14, 40, 35)); + booking2.setEnd(LocalDateTime.of(2025, 5, 25, 18, 12, 43)); + booking2.setItemId(saved.getItemId()); + booking2.setBookerId(user2.getUserId()); + BookingResponseDto bookingSaved = bookingService.addBooking(booking2, user.getUserId()); + bookingService.bookingApprove(bookingSaved.getBookingId(), user.getUserId(), false); + + CommentRequestDto comment = new CommentRequestDto(); + comment.setText("Test comment"); + + assertThrows(NotFoundException.class, + () -> itemService.addComment(256L, comment, saved.getItemId())); + } + + @Test + void addComment_WithWrongDateTest() { + UserResponseDto user = userService.createUser(userForCreate()); + UserResponseDto user2 = userService.createUser(user2ForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + BookingRequestDto booking2 = booking1ForCreate(); + booking2.setStart(LocalDateTime.of(2025, 6, 8, 14, 40, 35)); + booking2.setEnd(LocalDateTime.of(2025, 9, 25, 18, 12, 43)); + booking2.setItemId(saved.getItemId()); + booking2.setBookerId(user2.getUserId()); + BookingResponseDto bookingSaved = bookingService.addBooking(booking2, user.getUserId()); + bookingService.bookingApprove(bookingSaved.getBookingId(), user.getUserId(), false); + + CommentRequestDto comment = new CommentRequestDto(); + comment.setText("Test comment"); + + assertThrows(ValidationException.class, + () -> itemService.addComment(user.getUserId(), comment, saved.getItemId())); + } + + @Test + void searchByTextTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + ItemRequestDto forCreation = new ItemRequestDto(); + forCreation.setItemName("Test"); + forCreation.setItemDescription("Test item description"); + forCreation.setIsAvailable(true); + ItemResponseDto saved = itemService.createItem(forCreation, user.getUserId()); + + Collection items = itemService.searchByText("Test"); + + assertAll(() -> { + assertNotNull(items); + assertEquals(1, items.size()); + }); + } +} diff --git a/server/src/test/java/ru/practicum/shareit/serviceTest/RequestServiceTest.java b/server/src/test/java/ru/practicum/shareit/serviceTest/RequestServiceTest.java new file mode 100644 index 0000000..f03f612 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/serviceTest/RequestServiceTest.java @@ -0,0 +1,148 @@ +package ru.practicum.shareit.serviceTest; + +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import ru.practicum.shareit.TestData; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.item.ItemServiceImpl; +import ru.practicum.shareit.item.dto.ItemResponseDto; +import ru.practicum.shareit.request.RequestServiceImpl; +import ru.practicum.shareit.request.dto.RequestInputDto; +import ru.practicum.shareit.request.dto.RequestOutputDto; +import ru.practicum.shareit.request.dto.RequestOutputWithItemsDto; +import ru.practicum.shareit.user.UserServiceImpl; +import ru.practicum.shareit.user.dto.UserResponseDto; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + + +@Transactional +@SpringBootTest(properties = "jdbc.url=jdbc:h2:mem:testdb;MODE=PostgreSQL", + webEnvironment = SpringBootTest.WebEnvironment.NONE) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class RequestServiceTest extends TestData { + private final RequestServiceImpl requestService; + private final UserServiceImpl userService; + private final ItemServiceImpl itemService; + + @Test + void addRequestTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + RequestInputDto request = new RequestInputDto(); + request.setRequestDescription("Test request description1"); + + RequestOutputDto requestOutputDto = requestService.addItemRequest(request, user.getUserId()); + + assertThat(requestOutputDto).usingRecursiveComparison() + .ignoringFields("itemRequestId", "requester").isEqualTo(request); + } + + @Test + void addRequest_WithWrongUserTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + RequestInputDto requestInputDto = new RequestInputDto(); + requestInputDto.setRequestDescription("RequestDescription"); + + assertThrows(NotFoundException.class, + () -> requestService.addItemRequest(requestInputDto, 125L)); + } + + @Test + void getRequestByUserTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + RequestInputDto request = new RequestInputDto(); + request.setRequestDescription("RequestDescription"); + requestService.addItemRequest(request, user.getUserId()); + + List requests = requestService.getRequestByUser(user.getUserId()); + + assertAll(() -> { + assertNotNull(requests); + assertThat(requests.size()).isEqualTo(1); + }); + } + + @Test + void getRequestByWrongUserTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + RequestInputDto request = new RequestInputDto(); + request.setRequestDescription("RequestDescription"); + requestService.addItemRequest(request, user.getUserId()); + + assertThrows(NotFoundException.class, + () -> requestService.getRequestByUser(526L)); + } + + @Test + void getAllRequestsTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + RequestInputDto request = new RequestInputDto(); + request.setRequestDescription("RequestDescription"); + requestService.addItemRequest(request, user.getUserId()); + + RequestInputDto request1 = new RequestInputDto(); + request1.setRequestDescription("RequestDescription1"); + requestService.addItemRequest(request1, user.getUserId()); + + List requests = requestService.getAllRequests(); + + assertAll(() -> { + assertNotNull(requests); + assertThat(requests.size()).isEqualTo(2); + }); + } + + @Test + void getRequestByIdTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + RequestInputDto request = new RequestInputDto(); + request.setRequestDescription("RequestDescription"); + requestService.addItemRequest(request, user.getUserId()); + + RequestInputDto request1 = new RequestInputDto(); + request1.setRequestDescription("RequestDescription1"); + RequestOutputDto saved = requestService.addItemRequest(request1, user.getUserId()); + + RequestOutputWithItemsDto found = requestService.getRequestById(saved.getItemRequestId()); + + assertAll(() -> { + assertNotNull(found); + assertEquals(saved.getItemRequestId(), found.getItemRequestId()); + }); + } + + @Test + void getRequestByWrongIdTest() { + UserResponseDto user = userService.createUser(userForCreate()); + ItemResponseDto item = itemService.createItem(itemForCreate(), user.getUserId()); + + RequestInputDto request = new RequestInputDto(); + request.setRequestDescription("RequestDescription"); + requestService.addItemRequest(request, user.getUserId()); + + RequestInputDto request1 = new RequestInputDto(); + request1.setRequestDescription("RequestDescription1"); + RequestOutputDto saved = requestService.addItemRequest(request1, user.getUserId()); + + assertThrows(NotFoundException.class, + () -> requestService.getRequestById(73L)); + } +} diff --git a/server/src/test/java/ru/practicum/shareit/serviceTest/UserServiceTest.java b/server/src/test/java/ru/practicum/shareit/serviceTest/UserServiceTest.java new file mode 100644 index 0000000..a2ab1b3 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/serviceTest/UserServiceTest.java @@ -0,0 +1,120 @@ +package ru.practicum.shareit.serviceTest; + +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import ru.practicum.shareit.TestData; +import ru.practicum.shareit.exception.DuplicatedDataException; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.user.UserServiceImpl; +import ru.practicum.shareit.user.dto.UserRequestDto; +import ru.practicum.shareit.user.dto.UserResponseDto; +import ru.practicum.shareit.user.interfaces.UserRepository; +import ru.practicum.shareit.user.model.User; + +import java.util.Collection; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +@Transactional +@SpringBootTest(properties = "jdbc.url=jdbc:h2:mem:testdb;MODE=PostgreSQL", + webEnvironment = SpringBootTest.WebEnvironment.NONE) +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class UserServiceTest extends TestData { + private final UserServiceImpl userService; + private final UserRepository userRepository; + + @Test + void addUserTest() { + UserRequestDto forCreation = new UserRequestDto(); + forCreation.setUserName("User3"); + forCreation.setUserEmail("User3@mail.ru"); + UserResponseDto userResponseDto = userService.createUser(forCreation); + + assertAll(() -> + assertNotNull(userResponseDto)); + assertThat(forCreation).usingRecursiveComparison().ignoringFields("userId") + .isEqualTo(userResponseDto); + } + + @Test + void createUser_WithWrongEmailTest() { + userService.createUser(userForCreate()); + assertThrows(DuplicatedDataException.class, + () -> userService.createUser(userRequestDto1)); + } + + @Test + void getUserByIdTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + UserResponseDto found = userService.getUserById(user.getUserId()); + + assertAll(() -> { + assertNotNull(found); + assertThat(found).usingRecursiveComparison().isEqualTo(user); + }); + } + + @Test + void getUserById_NotFoundExceptionTest() { + assertThrows(NotFoundException.class, + () -> userService.getUserById(5L)); + } + + @Test + void updateUserTest() { + + UserResponseDto user = userService.createUser(userForCreate()); + + UserRequestDto forUpdate = new UserRequestDto(); + forUpdate.setUserName("UpdatedName"); + forUpdate.setUserEmail("UpdatedEmail@mail.ru"); + + UserResponseDto updated = new UserResponseDto(user.getUserId(), "UpdatedName", "UpdatedEmail@mail.ru"); + + UserResponseDto userResponseDto = userService.updateUser(user.getUserId(), forUpdate); + assertAll(() -> { + assertEquals(user.getUserId(), userResponseDto.getUserId()); + assertThat(updated).usingRecursiveComparison().ignoringFields("userId").isEqualTo(userResponseDto); + }); + } + + @Test + void updateUserTest_NotFoundExceptionTest() { + UserRequestDto forUpdate = new UserRequestDto(); + forUpdate.setUserName("UpdatedName"); + forUpdate.setUserEmail("UpdatedEmail@mail.ru"); + + assertThrows(NotFoundException.class, () -> userService.updateUser(5L, forUpdate)); + } + + @Test + void deleteUserByIdTest() { + UserResponseDto user = userService.createUser(userForCreate()); + + userService.deleteUser(user.getUserId()); + + assertThrows(NotFoundException.class, () -> userService.getUserById(user.getUserId())); + + } + + @Test + void deleteUserById_NotFoundExceptionTest() { + assertThrows(NotFoundException.class, () -> userService.deleteUser(5L)); + } + + @Test + void deleteAllUserTest() { + Collection users = userRepository.findAll(); + assertNotNull(users); + + userService.deleteAllUsers(); + + assertEquals(0, userRepository.findAll().size()); + } + +} diff --git a/src/main/java/ru/practicum/shareit/PersistenceConfig.java b/src/main/java/ru/practicum/shareit/PersistenceConfig.java deleted file mode 100644 index 04d14b2..0000000 --- a/src/main/java/ru/practicum/shareit/PersistenceConfig.java +++ /dev/null @@ -1,66 +0,0 @@ -package ru.practicum.shareit; - -import jakarta.persistence.EntityManagerFactory; -import lombok.RequiredArgsConstructor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; -import org.springframework.data.jpa.repository.config.EnableJpaRepositories; -import org.springframework.jdbc.datasource.DriverManagerDataSource; -import org.springframework.orm.jpa.JpaTransactionManager; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; -import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter; -import org.springframework.transaction.annotation.EnableTransactionManagement; - -import javax.sql.DataSource; -import java.util.Properties; - -@Configuration -@RequiredArgsConstructor -@EnableTransactionManagement -@EnableJpaRepositories(basePackages = "ru.practicum") -public class PersistenceConfig { - private final Environment environment; - - @Bean - public DataSource dataSource() { - DriverManagerDataSource dataSource = new DriverManagerDataSource(); - dataSource.setDriverClassName(environment.getRequiredProperty("spring.datasource.driverClassName")); - dataSource.setUrl(environment.getRequiredProperty("spring.datasource.url")); - dataSource.setUsername(environment.getRequiredProperty("spring.datasource.username")); - dataSource.setPassword(environment.getRequiredProperty("spring.datasource.password")); - return dataSource; - } - - private Properties hibernateProperties() { - Properties properties = new Properties(); - properties.put("spring.jpa.properties.hibernate.jdbc.time_zone", - environment.getRequiredProperty("spring.jpa.properties.hibernate.jdbc.time_zone")); - properties.put("spring.jpa.properties.hibernate.format_sql", - environment.getProperty("spring.jpa.properties.hibernate.format_sql", "false")); - return properties; - } - - @Bean - public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) { - final HibernateJpaVendorAdapter vendorAdapter = - new HibernateJpaVendorAdapter(); - - final LocalContainerEntityManagerFactoryBean emf = - new LocalContainerEntityManagerFactoryBean(); - - emf.setDataSource(dataSource); - emf.setJpaVendorAdapter(vendorAdapter); - emf.setPackagesToScan("ru.practicum"); - emf.setJpaProperties(hibernateProperties()); - - return emf; - } - - @Bean - public JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) { - JpaTransactionManager transactionManager = new JpaTransactionManager(); - transactionManager.setEntityManagerFactory(entityManagerFactory); - return transactionManager; - } -} diff --git a/src/main/java/ru/practicum/shareit/item/ItemStorage.java b/src/main/java/ru/practicum/shareit/item/ItemStorage.java deleted file mode 100644 index e9ba003..0000000 --- a/src/main/java/ru/practicum/shareit/item/ItemStorage.java +++ /dev/null @@ -1,67 +0,0 @@ -package ru.practicum.shareit.item; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Repository; -import ru.practicum.shareit.item.interfaces.ItemStorageInterface; -import ru.practicum.shareit.item.model.Item; - -import java.util.*; -import java.util.stream.Collectors; - -@Repository -@RequiredArgsConstructor -public class ItemStorage implements ItemStorageInterface { - - private Long nextItemId = 1L; - private final Map items = new HashMap<>(); - - public Item createItem(Item item) { - item.setItemId(nextItemId++); - items.put(item.getItemId(), item); - return item; - } - - public Optional getItemById(Long id) { - return Optional.ofNullable(items.get(id)); - } - - public Item updateItem(Item item) { - Item oldItem = items.get(item.getItemId()); - - if (oldItem != null) { - - if (item.getItemName() != null) { - oldItem.setItemName(item.getItemName()); - } - - if (item.getItemDescription() != null) { - oldItem.setItemDescription(item.getItemDescription()); - } - - if (item.getIsAvailable() != null) { - oldItem.setIsAvailable(item.getIsAvailable()); - } - - } - return oldItem; - } - - public Collection getAllItemsForOwner(Long ownerId) { - return items.values() - .stream() - .filter(item -> Objects.equals(item.getOwner().getUserId(), ownerId)) - .collect(Collectors.toList()); - } - - public Collection searchByText(String text) { - Collection itemCollection = new ArrayList<>(); - for (Item item : items.values()) { - if (item.getItemName().toLowerCase().contains(text.toLowerCase())) { - itemCollection.add(item); - } else if (item.getItemDescription().toLowerCase().contains(text.toLowerCase())) { - itemCollection.add(item); - } - } - return itemCollection; - } -} 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/dto/ItemRequestDto.java b/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java deleted file mode 100644 index 7b3ed54..0000000 --- a/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.shareit.request.dto; - -/** - * TODO Sprint add-item-requests. - */ -public class ItemRequestDto { -} diff --git a/src/main/java/ru/practicum/shareit/request/interfaces/RequestRepository.java b/src/main/java/ru/practicum/shareit/request/interfaces/RequestRepository.java deleted file mode 100644 index cea05b1..0000000 --- a/src/main/java/ru/practicum/shareit/request/interfaces/RequestRepository.java +++ /dev/null @@ -1,8 +0,0 @@ -package ru.practicum.shareit.request.interfaces; - -import org.springframework.data.jpa.repository.JpaRepository; -import ru.practicum.shareit.request.ItemRequest; - -public interface RequestRepository extends JpaRepository { - ItemRequest save(ItemRequest item); -} diff --git a/src/main/java/ru/practicum/shareit/user/UserStorage.java b/src/main/java/ru/practicum/shareit/user/UserStorage.java deleted file mode 100644 index 040839d..0000000 --- a/src/main/java/ru/practicum/shareit/user/UserStorage.java +++ /dev/null @@ -1,53 +0,0 @@ -package ru.practicum.shareit.user; - -import org.springframework.stereotype.Repository; -import ru.practicum.shareit.user.interfaces.UserStorageInterface; -import ru.practicum.shareit.user.model.User; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; - -@Repository -public class UserStorage implements UserStorageInterface { - - private final Map users = new HashMap<>(); - private Long nextUserId = 1L; - - public User createUser(User user) { - user.setUserId(nextUserId++); - users.put(user.getUserId(), user); - return user; - } - - public Optional getUserById(Long userId) { - return Optional.ofNullable(users.get(userId)); - } - - public User updateUser(Long userId, User user) { - User oldUser = users.get(userId); - - if (user.getUserName() != null) { - oldUser.setUserName(user.getUserName()); - } - - if (user.getUserEmail() != null) { - oldUser.setUserEmail(user.getUserEmail()); - } - - return oldUser; - } - - public Collection getAllUsers() { - return users.values(); - } - - public void deleteUser(Long userId) { - users.remove(userId); - } - - public void deleteAllUsers() { - users.clear(); - } -} diff --git a/src/main/java/ru/practicum/shareit/user/interfaces/UserStorageInterface.java b/src/main/java/ru/practicum/shareit/user/interfaces/UserStorageInterface.java deleted file mode 100644 index dafdbf4..0000000 --- a/src/main/java/ru/practicum/shareit/user/interfaces/UserStorageInterface.java +++ /dev/null @@ -1,20 +0,0 @@ -package ru.practicum.shareit.user.interfaces; - -import ru.practicum.shareit.user.model.User; - -import java.util.Collection; -import java.util.Optional; - -public interface UserStorageInterface { - User createUser(User user); - - Optional getUserById(Long userId); - - User updateUser(Long userId, User user); - - Collection getAllUsers(); - - void deleteUser(Long userId); - - void deleteAllUsers(); -} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index e1fc5d9..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1,13 +0,0 @@ -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.url=jdbc:postgresql://localhost:5432/postgres -spring.datasource.driverClassName=org.postgresql.Driver -spring.datasource.username=user -spring.datasource.password=12345 - -logging.level.org.springframework.orm.jpa=INFO -logging.level.org.springframework.transaction=INFO -logging.level.org.springframework.transaction.interceptor=TRACE -logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG \ No newline at end of file diff --git a/src/test/resources/application.properties b/src/test/resources/application.properties deleted file mode 100644 index 0024b61..0000000 --- a/src/test/resources/application.properties +++ /dev/null @@ -1,10 +0,0 @@ -spring.sql.init.mode=always -spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE -spring.datasource.driverClassName=org.h2.Driver -spring.datasource.username=user -spring.datasource.password=12345 - -spring.jpa.properties.hibernate.format_sql=true -spring.jpa.properties.hibernate.jdbc.time_zone=UTC -spring.jpa.hibernate.ddl-auto=create-drop -spring.jpa.database-platform=org.hibernate.dialect.H2Dialect \ No newline at end of file