diff --git a/.run/ShareItGateway.run.xml b/.run/ShareItGateway.run.xml new file mode 100644 index 0000000..32c8129 --- /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..5f869f3 --- /dev/null +++ b/.run/ShareItServer.run.xml @@ -0,0 +1,17 @@ + + + + \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..abe6570 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,39 @@ +services: + gateway: + build: gateway + image: shareit-gateway + container_name: shareit-gateway + ports: + - "8080:8080" + depends_on: + - server + environment: + - SHAREIT_SERVER_URL=http://server:9090 + + server: + build: server + image: shareit-server + container_name: shareit-server + ports: + - "9090:9090" + depends_on: + - db + environment: + - SPRING_DATASOURCE_URL=jdbc:postgresql://db:5432/shareit + - SPRING_DATASOURCE_USERNAME=shareit + - SPRING_DATASOURCE_PASSWORD=shareit + + db: + image: postgres:16.1 + container_name: postgres + ports: + - "6541:5432" + environment: + - POSTGRES_PASSWORD=shareit + - POSTGRES_USER=shareit + - POSTGRES_DB=shareit + healthcheck: + test: pg_isready -q -d $$POSTGRES_DB -U $$POSTGRES_USER + timeout: 5s + interval: 5s + retries: 10 \ No newline at end of file diff --git a/gateway/Dockerfile b/gateway/Dockerfile new file mode 100644 index 0000000..0ff1817 --- /dev/null +++ b/gateway/Dockerfile @@ -0,0 +1,5 @@ +FROM eclipse-temurin:21-jre-jammy +VOLUME /tmp +ARG JAR_FILE=target/*.jar +COPY ${JAR_FILE} app.jar +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"] \ No newline at end of file diff --git a/gateway/pom.xml b/gateway/pom.xml new file mode 100644 index 0000000..0a2eaf2 --- /dev/null +++ b/gateway/pom.xml @@ -0,0 +1,75 @@ + + + 4.0.0 + + ru.practicum + shareit + 0.0.1-SNAPSHOT + + + shareit-gateway + 0.0.1-SNAPSHOT + + ShareIt Gateway + + + + org.zalando + logbook-spring-boot-starter + 3.7.2 + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-validation + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.hibernate.validator + hibernate-validator + + + + org.apache.httpcomponents.client5 + httpclient5 + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + \ No newline at end of file diff --git a/gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java b/gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java new file mode 100644 index 0000000..0aa75c3 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java @@ -0,0 +1,12 @@ +package ru.practicum.shareit; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ShareItGateway { + public static void main(String[] args) { + SpringApplication.run(ShareItGateway.class, args); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java new file mode 100644 index 0000000..7d3b272 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingClient.java @@ -0,0 +1,62 @@ +package ru.practicum.shareit.booking; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.http.ResponseEntity; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.stereotype.Service; +import org.springframework.web.util.DefaultUriBuilderFactory; +import ru.practicum.shareit.booking.dto.BookingCreateDto; +import ru.practicum.shareit.booking.dto.BookingState; +import ru.practicum.shareit.client.BaseClient; + +import java.util.Map; + +@Service +public class BookingClient extends BaseClient { + private static final String API_PREFIX = "/bookings"; + + @Autowired + public BookingClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { + super( + builder + .uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()) + .build() + ); + } + + public ResponseEntity getBookingByBookerIdWhereTime(long userId, BookingState state, + Integer from, Integer size) { + Map parameters = Map.of( + "state", state.name(), + "from", from, + "size", size + ); + return get("?state={state}&from={from}&size={size}", userId, parameters); + } + + public ResponseEntity getBookingByOwnerIdWhereTime(long userId, BookingState state, + Integer from, Integer size) { + Map parameters = Map.of( + "state", state.name(), + "from", from, + "size", size + ); + return get("/owner?state={state}&from={from}&size={size}", userId, parameters); + } + + + public ResponseEntity setBookingItem(long userId, BookingCreateDto requestDto) { + return post("", userId, requestDto); + } + + public ResponseEntity setBookingStatus(long userId, Long bookingId, boolean approved) { + return patch("/" + bookingId + "?approved=" + approved, userId); + } + + public ResponseEntity getBookingById(long userId, Long bookingId) { + return get("/" + bookingId, userId); + } +} diff --git a/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java new file mode 100644 index 0000000..b5e0fc6 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -0,0 +1,67 @@ +package ru.practicum.shareit.booking; + +import jakarta.validation.Valid; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.booking.dto.BookingCreateDto; +import ru.practicum.shareit.booking.dto.BookingState; + + +@Controller +@RequestMapping(path = "/bookings") +@RequiredArgsConstructor +@Slf4j +@Validated +public class BookingController { + private final BookingClient bookingClient; + + @PostMapping + public ResponseEntity setBookingItem(@RequestHeader("X-Sharer-User-Id") long userId, + @RequestBody @Valid BookingCreateDto requestDto) { + log.info("Creating booking {}, userId={}", requestDto, userId); + return bookingClient.setBookingItem(userId, requestDto); + } + + @PatchMapping("/{bookingId}") + public ResponseEntity setBookingStatus(@RequestHeader("X-Sharer-User-Id") long userId, + @PathVariable Long bookingId, @RequestParam boolean approved) { + log.info("set BookingStatus userId {}, bookingId={}, approved={}", userId, bookingId, approved); + return bookingClient.setBookingStatus(userId, bookingId, approved); + } + + @GetMapping("/{bookingId}") + public ResponseEntity getBookingById(@RequestHeader("X-Sharer-User-Id") long userId, + @PathVariable Long bookingId) { + log.info("Get bookingById {}, userId={}", bookingId, userId); + return bookingClient.getBookingById(userId, bookingId); + } + + @GetMapping + public ResponseEntity getBookingByBookerIdWhereTime(@RequestHeader("X-Sharer-User-Id") long userId, + @RequestParam(name = "state", defaultValue = "all") String stateParam, + @PositiveOrZero @RequestParam(name = "from", defaultValue = "0") Integer from, + @Positive @RequestParam(name = "size", defaultValue = "10") Integer size) { + BookingState state = BookingState.from(stateParam) + .orElseThrow(() -> new IllegalArgumentException("Unknown state: " + stateParam)); + log.info("Get booking with state {}, userId={}, from={}, size={}", stateParam, userId, from, size); + return bookingClient.getBookingByBookerIdWhereTime(userId, state, from, size); + } + + @GetMapping("/owner") + public ResponseEntity getBookingByOwnerIdWhereTime(@RequestHeader("X-Sharer-User-Id") long userId, + @RequestParam(name = "state", defaultValue = "all") String stateParam, + @PositiveOrZero @RequestParam(name = "from", defaultValue = "0") Integer from, + @Positive @RequestParam(name = "size", defaultValue = "10") Integer size) { + BookingState state = BookingState.from(stateParam) + .orElseThrow(() -> new IllegalArgumentException("Unknown state: " + stateParam)); + log.info("Get booking with state {}, userId={}, from={}, size={}", stateParam, userId, from, size); + return bookingClient.getBookingByOwnerIdWhereTime(userId, state, from, size); + } + +} diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java similarity index 85% rename from src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java rename to gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java index ffa28b5..3a21056 100644 --- a/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java +++ b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java @@ -4,23 +4,23 @@ import jakarta.validation.constraints.Future; import jakarta.validation.constraints.FutureOrPresent; import jakarta.validation.constraints.NotNull; -import lombok.Data; -import org.springframework.stereotype.Component; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; -@Component -@Data +@Getter +@NoArgsConstructor +@AllArgsConstructor public class BookingCreateDto { @NotNull + private long itemId; @FutureOrPresent private LocalDateTime start; - @NotNull @Future private LocalDateTime end; - @NotNull - private Long itemId; @AssertTrue(message = "Дата окончания должна быть позже даты начала") boolean isStartBeforeEnd() { 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..135de62 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/booking/dto/BookingState.java @@ -0,0 +1,27 @@ +package ru.practicum.shareit.booking.dto; + +import java.util.Optional; + +public enum BookingState { + // Все + ALL, + // Текущие + CURRENT, + // Будущие + FUTURE, + // Завершенные + PAST, + // Отклоненные + REJECTED, + // Ожидающие подтверждения + WAITING; + + public static Optional from(String stringState) { + for (BookingState state : values()) { + if (state.name().equalsIgnoreCase(stringState)) { + return Optional.of(state); + } + } + return Optional.empty(); + } +} diff --git a/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java new file mode 100644 index 0000000..0c1b422 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/client/BaseClient.java @@ -0,0 +1,123 @@ +package ru.practicum.shareit.client; + +import java.util.List; +import java.util.Map; + +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.lang.Nullable; +import org.springframework.web.client.HttpStatusCodeException; +import org.springframework.web.client.RestTemplate; + +public class BaseClient { + protected final RestTemplate rest; + + public BaseClient(RestTemplate rest) { + this.rest = rest; + } + + protected ResponseEntity get(String path) { + return get(path, null, null); + } + + protected ResponseEntity get(String path, long userId) { + return get(path, userId, null); + } + + protected ResponseEntity get(String path, Long userId, @Nullable Map parameters) { + return makeAndSendRequest(HttpMethod.GET, path, userId, parameters, null); + } + + protected ResponseEntity post(String path, T body) { + return post(path, null, null, body); + } + + protected ResponseEntity post(String path, long userId, T body) { + return post(path, userId, null, body); + } + + protected ResponseEntity post(String path, Long userId, @Nullable Map parameters, T body) { + return makeAndSendRequest(HttpMethod.POST, path, userId, parameters, body); + } + + protected ResponseEntity put(String path, long userId, T body) { + return put(path, userId, null, body); + } + + protected ResponseEntity put(String path, long userId, @Nullable Map parameters, T body) { + return makeAndSendRequest(HttpMethod.PUT, path, userId, parameters, body); + } + + protected ResponseEntity patch(String path, T body) { + return patch(path, null, null, body); + } + + protected ResponseEntity patch(String path, long userId) { + return patch(path, userId, null, null); + } + + protected ResponseEntity patch(String path, long userId, T body) { + return patch(path, userId, null, body); + } + + protected ResponseEntity patch(String path, Long userId, @Nullable Map parameters, T body) { + return makeAndSendRequest(HttpMethod.PATCH, path, userId, parameters, body); + } + + protected ResponseEntity delete(String path) { + return delete(path, null, null); + } + + protected ResponseEntity delete(String path, long userId) { + return delete(path, userId, null); + } + + protected ResponseEntity delete(String path, Long userId, @Nullable Map parameters) { + return makeAndSendRequest(HttpMethod.DELETE, path, userId, parameters, null); + } + + private ResponseEntity makeAndSendRequest(HttpMethod method, String path, + Long userId, @Nullable Map parameters, + @Nullable T body) { + HttpEntity requestEntity = new HttpEntity<>(body, defaultHeaders(userId)); + + ResponseEntity shareitServerResponse; + try { + if (parameters != null) { + shareitServerResponse = rest.exchange(path, method, requestEntity, Object.class, parameters); + } else { + shareitServerResponse = rest.exchange(path, method, requestEntity, Object.class); + } + } catch (HttpStatusCodeException e) { + return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBodyAsByteArray()); + } + return prepareGatewayResponse(shareitServerResponse); + } + + private HttpHeaders defaultHeaders(Long userId) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setAccept(List.of(MediaType.APPLICATION_JSON)); + if (userId != null) { + headers.set("X-Sharer-User-Id", String.valueOf(userId)); + } + return headers; + } + + private static ResponseEntity prepareGatewayResponse(ResponseEntity response) { + if (response.getStatusCode().is2xxSuccessful()) { + return response; + } + + ResponseEntity.BodyBuilder responseBuilder = ResponseEntity.status(response.getStatusCode()); + + if (response.hasBody()) { + return responseBuilder.body(response.getBody()); + } + + return responseBuilder.build(); + } +} diff --git a/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java new file mode 100644 index 0000000..f943e23 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemClient.java @@ -0,0 +1,57 @@ +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.CommentCreateDto; +import ru.practicum.shareit.item.dto.ItemCreateDto; +import ru.practicum.shareit.item.dto.ItemUpdateDto; + +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 createComment(long userId, Long itemId, CommentCreateDto createDto) { + return post("/" + itemId + "/comment", userId, createDto); + } + + public ResponseEntity createItem(Long userId, ItemCreateDto createDto) { + return post("", userId, createDto); + } + + public ResponseEntity updateItem(Long userId, Long itemId, ItemUpdateDto updateDto) { + return patch("/" + itemId, userId, updateDto); + } + + public ResponseEntity getByItemIdWithComment(Long userId, Long itemId) { + return get("/" + itemId, userId); + } + + public ResponseEntity allItemByOwnerIdWithComment(Long userId) { + return get("", userId); + } + + public ResponseEntity getItemsByNameOrDescription(Long userId, String text) { + Map parameters = Map.of("text", text); + return get("/search?text={text}", userId, parameters); + } + + +} 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..afb90a6 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -0,0 +1,65 @@ +package ru.practicum.shareit.item; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.item.dto.CommentCreateDto; +import ru.practicum.shareit.item.dto.ItemCreateDto; +import ru.practicum.shareit.item.dto.ItemUpdateDto; + +@Controller +@RequestMapping(path = "/items") +@RequiredArgsConstructor +@Slf4j +@Validated +public class ItemController { + private final ItemClient itemClient; + + @PostMapping("/{itemId}/comment") + public ResponseEntity createComment(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long itemId, + @RequestBody @Valid CommentCreateDto createDto) { + log.info("Creating Comment {}, userId={}, itemId={}", createDto, userId, itemId); + return itemClient.createComment(userId, itemId, createDto); + } + + @PostMapping + public ResponseEntity createItem(@RequestHeader("X-Sharer-User-Id") Long userId, + @RequestBody @Valid ItemCreateDto createDto) { + log.info("Creating Item {}, userId={}", createDto, userId); + return itemClient.createItem(userId, createDto); + } + + @PatchMapping("/{itemId}") + public ResponseEntity updateItem(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long itemId, + @RequestBody @Valid ItemUpdateDto updateDto) { + log.info("Patch updateItem {}, userId={}, itemId={}", updateDto, userId, itemId); + return itemClient.updateItem(userId, itemId, updateDto); + } + + @GetMapping("/{itemId}") + public ResponseEntity getByItemIdWithComment(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long itemId) { + log.info("Get getByItemIdWithComment, userId={}, itemId={}", userId, itemId); + return itemClient.getByItemIdWithComment(userId, itemId); + } + + @GetMapping + public ResponseEntity allItemByOwnerIdWithComment(@RequestHeader("X-Sharer-User-Id") Long userId) { + log.info("Get allItemByOwnerIdWithComment , userId={}", userId); + return itemClient.allItemByOwnerIdWithComment(userId); + } + + @GetMapping("/search") + public ResponseEntity getItemsByNameOrDescription(@RequestHeader("X-Sharer-User-Id") Long userId, + @RequestParam String text) { + log.info("Get getItemsByNameOrDescription, userId={}, text={}", userId, text); + return itemClient.getItemsByNameOrDescription(userId, text); + } + +} diff --git a/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java rename to gateway/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java similarity index 93% rename from src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java rename to gateway/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java index ed2d63d..a9b9043 100644 --- a/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java +++ b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java @@ -16,4 +16,5 @@ public class ItemCreateDto { @NotNull private Boolean available; private Long owner; + private Long requestId; } diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java b/gateway/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java rename to gateway/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java diff --git a/gateway/src/main/java/ru/practicum/shareit/request/ItemRequestClient.java b/gateway/src/main/java/ru/practicum/shareit/request/ItemRequestClient.java new file mode 100644 index 0000000..72b9c90 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/ItemRequestClient.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.ItemRequestCreateDto; + +@Service +public class ItemRequestClient extends BaseClient { + private static final String API_PREFIX = "/requests"; + + @Autowired + public ItemRequestClient(@Value("${shareit-server.url}") String serverUrl, RestTemplateBuilder builder) { + super( + builder + .uriTemplateHandler(new DefaultUriBuilderFactory(serverUrl + API_PREFIX)) + .requestFactory(() -> new HttpComponentsClientHttpRequestFactory()) + .build() + ); + } + + public ResponseEntity createItemRequest(Long userId, ItemRequestCreateDto createDto) { + return post("", userId, createDto); + } + + public ResponseEntity getItemRequestsByUserId(Long userId) { + return get("", userId); + } + + public ResponseEntity getItemRequestsByNotUserId(Long userId) { + return get("/all", userId); + } + + public ResponseEntity getItemRequestById(Long userId, Long requestId) { + return get("/" + requestId, userId); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/request/ItemRequestController.java b/gateway/src/main/java/ru/practicum/shareit/request/ItemRequestController.java new file mode 100644 index 0000000..c4131f0 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/ItemRequestController.java @@ -0,0 +1,46 @@ +package ru.practicum.shareit.request; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; + + +@Controller +@RequestMapping(path = "/requests") +@RequiredArgsConstructor +@Slf4j +@Validated +public class ItemRequestController { + private final ItemRequestClient itemRequestClient; + + @PostMapping + public ResponseEntity createItemRequest(@RequestHeader("X-Sharer-User-Id") Long userId, + @Validated @RequestBody ItemRequestCreateDto createDto) { + log.info("Creating ItemRequest {}, userId={}", createDto, userId); + return itemRequestClient.createItemRequest(userId, createDto); + } + + @GetMapping + public ResponseEntity getItemRequestsByUserId(@RequestHeader("X-Sharer-User-Id") Long userId) { + log.info("Get getItemRequestsByUserId, userId={}", userId); + return itemRequestClient.getItemRequestsByUserId(userId); + } + + @GetMapping("/all") + public ResponseEntity getItemRequestsByNotUserId(@RequestHeader("X-Sharer-User-Id") Long userId) { + log.info("Get getItemRequestsByNotUserId, userId={}", userId); + return itemRequestClient.getItemRequestsByNotUserId(userId); + } + + @GetMapping("/{requestId}") + public ResponseEntity getItemRequestById(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long requestId) { + log.info("Get getItemRequestById, userId={}, requestId={}", userId, requestId); + return itemRequestClient.getItemRequestById(userId, requestId); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java b/gateway/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java new file mode 100644 index 0000000..90c82d7 --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java @@ -0,0 +1,16 @@ +package ru.practicum.shareit.request.dto; + + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; + + +@Getter +@Setter +public class ItemRequestCreateDto { + @NotNull + @NotBlank + private String description; +} diff --git a/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java b/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java new file mode 100644 index 0000000..481411a --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserClient.java @@ -0,0 +1,48 @@ +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.UserCreateDto; +import ru.practicum.shareit.user.dto.UserUpdateDto; + +@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 create(UserCreateDto userDto) { + return post("", userDto); + } + + public ResponseEntity update(Long userId, UserUpdateDto userDto) { + return patch("/" + userId, userDto); + } + + public ResponseEntity getUserById(Long userId) { + return get("/" + userId); + } + + public ResponseEntity getAll(Integer size) { + return get("?size={size}", size); + } + + public void deleteUser(Long userId) { + delete("/" + userId); + } + +} diff --git a/gateway/src/main/java/ru/practicum/shareit/user/UserController.java b/gateway/src/main/java/ru/practicum/shareit/user/UserController.java new file mode 100644 index 0000000..f3df2ad --- /dev/null +++ b/gateway/src/main/java/ru/practicum/shareit/user/UserController.java @@ -0,0 +1,55 @@ +package ru.practicum.shareit.user; + +import jakarta.validation.constraints.Positive; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.user.dto.UserCreateDto; +import ru.practicum.shareit.user.dto.UserUpdateDto; + +@Controller +@RequestMapping(path = "/users") +@RequiredArgsConstructor +@Slf4j +@Validated +public class UserController { + private final UserClient userClient; + + + @PostMapping + public ResponseEntity create(@RequestBody @Validated UserCreateDto userDto) { + log.info("Creating user {}", userDto); + return userClient.create(userDto); + } + + @PatchMapping("/{userId}") + public ResponseEntity update(@PathVariable Long userId, + @RequestBody @Validated UserUpdateDto userDto) { + log.info("Update user {}, userId={}", userDto, userId); + return userClient.update(userId, userDto); + } + + @GetMapping("/{userId}") + public ResponseEntity getUserById(@PathVariable Long userId) { + log.info("Get userId {}", userId); + return userClient.getUserById(userId); + } + + @GetMapping() + public ResponseEntity getAll(@Positive @RequestParam(name = "size", defaultValue = "10") Integer size) { + log.info("Get getAll size{}", size); + return userClient.getAll(size); + } + + @ResponseStatus(HttpStatus.OK) + @DeleteMapping("/{userId}") + public void deleteUser(@PathVariable Long userId) { + log.info("Delete User userId{}", userId); + userClient.deleteUser(userId); + } + +} diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java b/gateway/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java rename to gateway/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java b/gateway/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java rename to gateway/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java diff --git a/gateway/src/main/resources/application.properties b/gateway/src/main/resources/application.properties new file mode 100644 index 0000000..85c8096 --- /dev/null +++ b/gateway/src/main/resources/application.properties @@ -0,0 +1,9 @@ +logging.level.org.springframework.web.client.RestTemplate=DEBUG +#logging.level.org.apache.http=DEBUG +#logging.level.httpclient.wire=DEBUG + +spring.output.ansi.enabled=ALWAYS + +server.port=8080 + +shareit-server.url=http://localhost:9090 \ No newline at end of file diff --git a/pom.xml b/pom.xml index a4e00c4..79cba37 100644 --- a/pom.xml +++ b/pom.xml @@ -11,6 +11,7 @@ ru.practicum shareit + pom 0.0.1-SNAPSHOT ShareIt @@ -19,86 +20,29 @@ 21 - - - org.springframework.boot - spring-boot-starter-jdbc - - - org.springframework.boot - spring-boot-starter-web - - - org.zalando - logbook-spring-boot-starter - 3.7.2 - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-configuration-processor - true - - - - org.postgresql - postgresql - runtime - - - - org.springframework.boot - spring-boot-starter-data-jpa - - - - org.projectlombok - lombok - true - - - - com.h2database - h2 - test - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.boot - spring-boot-starter-validation - - + + gateway + server + - - - src/main/resources - true - - - - - org.springframework.boot - spring-boot-maven-plugin - - - - org.projectlombok - lombok - - - - - + + org.springframework.boot + spring-boot-maven-plugin + + + true + + + + org.projectlombok + lombok + + + + org.apache.maven.plugins maven-surefire-plugin @@ -245,17 +189,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..fa64133 --- /dev/null +++ b/server/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + + ru.practicum + shareit + 0.0.1-SNAPSHOT + + + shareit-server + 0.0.1-SNAPSHOT + + ShareIt Server + + + + org.zalando + logbook-spring-boot-starter + 3.7.2 + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-actuator + + + + org.postgresql + postgresql + runtime + + + + com.h2database + h2 + runtime + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.projectlombok + lombok + true + + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + + + coverage + + + + org.jacoco + jacoco-maven-plugin + + + + + + + diff --git a/src/main/java/ru/practicum/shareit/ShareItApp.java b/server/src/main/java/ru/practicum/shareit/ShareItServer.java similarity index 57% rename from src/main/java/ru/practicum/shareit/ShareItApp.java rename to server/src/main/java/ru/practicum/shareit/ShareItServer.java index a10a87d..303541d 100644 --- a/src/main/java/ru/practicum/shareit/ShareItApp.java +++ b/server/src/main/java/ru/practicum/shareit/ShareItServer.java @@ -4,10 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class ShareItApp { +public class ShareItServer { - public static void main(String[] args) { - SpringApplication.run(ShareItApp.class, args); - } + public static void main(String[] args) { + SpringApplication.run(ShareItServer.class, args); + } } diff --git a/src/main/java/ru/practicum/shareit/booking/Booking.java b/server/src/main/java/ru/practicum/shareit/booking/Booking.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/Booking.java rename to server/src/main/java/ru/practicum/shareit/booking/Booking.java diff --git a/src/main/java/ru/practicum/shareit/booking/BookingController.java b/server/src/main/java/ru/practicum/shareit/booking/BookingController.java similarity index 87% rename from src/main/java/ru/practicum/shareit/booking/BookingController.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingController.java index 99fca72..359fed3 100644 --- a/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -1,7 +1,6 @@ package ru.practicum.shareit.booking; import lombok.RequiredArgsConstructor; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import ru.practicum.shareit.booking.dto.BookingCreateDto; import ru.practicum.shareit.booking.dto.BookingDto; @@ -9,19 +8,15 @@ import java.util.List; - -@Validated @RestController @RequiredArgsConstructor @RequestMapping(path = "/bookings") public class BookingController { private final BookingService bookingService; - private final BookingCreateDto createDto; @PostMapping public BookingDto setBookingItem(@RequestHeader("X-Sharer-User-Id") Long userId, - @Validated @RequestBody BookingCreateDto createDto) { - createDto.normalizeTimestamps(); + @RequestBody BookingCreateDto createDto) { return bookingService.setBookingItem(createDto, userId); } diff --git a/src/main/java/ru/practicum/shareit/booking/BookingMapper.java b/server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/BookingMapper.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingMapper.java diff --git a/src/main/java/ru/practicum/shareit/booking/BookingRepository.java b/server/src/main/java/ru/practicum/shareit/booking/BookingRepository.java similarity index 73% rename from src/main/java/ru/practicum/shareit/booking/BookingRepository.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingRepository.java index 4838242..555fce8 100644 --- a/src/main/java/ru/practicum/shareit/booking/BookingRepository.java +++ b/server/src/main/java/ru/practicum/shareit/booking/BookingRepository.java @@ -9,17 +9,19 @@ public interface BookingRepository extends JpaRepository { + // Проверка на возможность оставить отзыв на вещь после успешной брони (Добавлять отзыв может только тот, кто брал вещь в аренду) @Query(value = """ SELECT EXISTS ( SELECT 1 FROM booking WHERE item_id = :itemId AND booker_id = :userId AND status = 'APPROVED' - AND end_time < CURRENT_TIMESTAMP + AND end_time < CURRENT_TIMESTAMP + INTERVAL '1' SECOND )""", nativeQuery = true) boolean hasUserBookedItem(@Param("userId") Long userId, @Param("itemId") Long itemId); + // Проверка свободных дат для новой брони @Query(""" SELECT COUNT(b) > 0 FROM Booking b @@ -35,6 +37,7 @@ boolean existsByItemIdAndTimeRange(@Param("itemId") Long itemId, @Param("start") LocalDateTime start, @Param("end") LocalDateTime end); + // Получение списка всех бронирований (с учетом их статуса) текущего пользователя @Query(value = """ SELECT * FROM booking WHERE booker_id = :bookerId @@ -51,19 +54,22 @@ boolean existsByItemIdAndTimeRange(@Param("itemId") Long itemId, List getBookingByBookerIdWhereTime(@Param("bookerId") Long bookerId, @Param("state") String state); + // Получение списка бронирований для всех вещей текущего пользователя. @Query(value = """ SELECT b.* FROM booking b JOIN items i ON b.item_id = i.id WHERE i.owner_id = :ownerId AND ( (:state = 'ALL') OR - (:state = 'CURRENT' AND NOW() BETWEEN b.start_time AND b.end_time) OR + (:state = 'CURRENT' AND start_time <= NOW() AND end_time >= NOW()) OR (:state = 'PAST' AND b.end_time < NOW()) OR (:state = 'FUTURE' AND b.start_time > NOW()) OR - (:state = b.status) + (:state = 'WAITING' AND status = 'WAITING') OR + (:state = 'REJECTED' AND status = 'REJECTED') ) ORDER BY b.start_time DESC""", nativeQuery = true) List getBookingByOwnerIdWhereTime(@Param("ownerId") Long ownerId, @Param("state") String state); + } diff --git a/src/main/java/ru/practicum/shareit/booking/BookingStatus.java b/server/src/main/java/ru/practicum/shareit/booking/BookingStatus.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/BookingStatus.java rename to server/src/main/java/ru/practicum/shareit/booking/BookingStatus.java diff --git a/server/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java new file mode 100644 index 0000000..89d2942 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingCreateDto.java @@ -0,0 +1,14 @@ +package ru.practicum.shareit.booking.dto; + +import lombok.Builder; +import lombok.Data; + +import java.time.LocalDateTime; + +@Builder +@Data +public class BookingCreateDto { + private LocalDateTime start; + private LocalDateTime end; + private Long itemId; +} diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java b/server/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java rename to server/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java diff --git a/src/main/java/ru/practicum/shareit/booking/service/BookingService.java b/server/src/main/java/ru/practicum/shareit/booking/service/BookingService.java similarity index 100% rename from src/main/java/ru/practicum/shareit/booking/service/BookingService.java rename to server/src/main/java/ru/practicum/shareit/booking/service/BookingService.java diff --git a/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java b/server/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java similarity index 98% rename from src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java rename to server/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java index b562b38..47a820f 100644 --- a/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import ru.practicum.shareit.booking.Booking; import ru.practicum.shareit.booking.BookingMapper; import ru.practicum.shareit.booking.BookingRepository; @@ -18,6 +19,7 @@ import java.util.List; +@Transactional @Service @RequiredArgsConstructor public class BookingServiceImpl implements BookingService { diff --git a/src/main/java/ru/practicum/shareit/exception/BadRequestException.java b/server/src/main/java/ru/practicum/shareit/exception/BadRequestException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/BadRequestException.java rename to server/src/main/java/ru/practicum/shareit/exception/BadRequestException.java diff --git a/src/main/java/ru/practicum/shareit/exception/ConflictException.java b/server/src/main/java/ru/practicum/shareit/exception/ConflictException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/ConflictException.java rename to server/src/main/java/ru/practicum/shareit/exception/ConflictException.java diff --git a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/ErrorHandler.java rename to server/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java diff --git a/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java b/server/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 server/src/main/java/ru/practicum/shareit/exception/ErrorResponse.java diff --git a/src/main/java/ru/practicum/shareit/exception/InternalServerErrorException.java b/server/src/main/java/ru/practicum/shareit/exception/InternalServerErrorException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/InternalServerErrorException.java rename to server/src/main/java/ru/practicum/shareit/exception/InternalServerErrorException.java diff --git a/src/main/java/ru/practicum/shareit/exception/NotFoundException.java b/server/src/main/java/ru/practicum/shareit/exception/NotFoundException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/NotFoundException.java rename to server/src/main/java/ru/practicum/shareit/exception/NotFoundException.java diff --git a/src/main/java/ru/practicum/shareit/exception/ValidationException.java b/server/src/main/java/ru/practicum/shareit/exception/ValidationException.java similarity index 100% rename from src/main/java/ru/practicum/shareit/exception/ValidationException.java rename to server/src/main/java/ru/practicum/shareit/exception/ValidationException.java diff --git a/src/main/java/ru/practicum/shareit/item/CommentMapper.java b/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java similarity index 50% rename from src/main/java/ru/practicum/shareit/item/CommentMapper.java rename to server/src/main/java/ru/practicum/shareit/item/CommentMapper.java index 3da8aa0..a4ecc8e 100644 --- a/src/main/java/ru/practicum/shareit/item/CommentMapper.java +++ b/server/src/main/java/ru/practicum/shareit/item/CommentMapper.java @@ -3,20 +3,14 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; import ru.practicum.shareit.item.dto.CommentCreateDto; -import ru.practicum.shareit.item.dto.CommentDto; import ru.practicum.shareit.item.dto.CommentDtoReturn; -import ru.practicum.shareit.item.dto.ItemDto; import ru.practicum.shareit.item.model.Comment; -import ru.practicum.shareit.item.model.Item; -import ru.practicum.shareit.user.UserMapper; -import ru.practicum.shareit.user.dto.UserDto; import java.time.LocalDateTime; @RequiredArgsConstructor @Component public class CommentMapper { - private final UserMapper userMapper; public Comment commentCreateDtoToModel(CommentCreateDto createDto) { return Comment.builder() @@ -33,23 +27,4 @@ public CommentDtoReturn modelToReturnDto(Comment comment) { .created(comment.getCreated()) .build(); } - - public CommentDto modelToDto(Comment comment) { - UserDto userDto = userMapper.modelToDto(comment.getAuthor()); - Item item = comment.getItem(); - ItemDto itemDto = ItemDto.builder() - .id(item.getId()) - .name(item.getName()) - .description(item.getDescription()) - .available(item.getAvailable()) - .owner(userDto) - .build(); - - return CommentDto.builder() - .id(comment.getId()) - .text(comment.getText()) - .item(itemDto) - .author(userDto) - .build(); - } } 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 51% rename from src/main/java/ru/practicum/shareit/item/ItemController.java rename to server/src/main/java/ru/practicum/shareit/item/ItemController.java index 26c15e3..621eac6 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemController.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -1,14 +1,12 @@ package ru.practicum.shareit.item; import lombok.RequiredArgsConstructor; -import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import ru.practicum.shareit.item.dto.*; import ru.practicum.shareit.item.service.ItemService; import java.util.List; -@Validated @RestController @RequiredArgsConstructor @RequestMapping("/items") @@ -16,35 +14,37 @@ public class ItemController { private final ItemService itemService; @PostMapping("/{itemId}/comment") - public CommentDtoReturn createComment(@RequestHeader("X-Sharer-User-Id") Long userId, @PathVariable long itemId, - @Validated @RequestBody CommentCreateDto createDto) { + public CommentDtoReturn createComment(@RequestHeader("X-Sharer-User-Id") Long userId, @PathVariable Long itemId, + @RequestBody CommentCreateDto createDto) { return itemService.createComment(userId, itemId, createDto); } @PostMapping public ItemDto createItem(@RequestHeader("X-Sharer-User-Id") Long userId, - @Validated @RequestBody ItemCreateDto createDto) { + @RequestBody ItemCreateDto createDto) { return itemService.createItem(createDto, userId); } @PatchMapping("/{itemId}") - public ItemDto update(@RequestHeader("X-Sharer-User-Id") Long userId, @PathVariable Long itemId, - @Validated @RequestBody ItemUpdateDto updateDto) { - return itemService.update(updateDto, userId, itemId); + public ItemDto updateItem(@RequestHeader("X-Sharer-User-Id") Long userId, @PathVariable Long itemId, + @RequestBody ItemUpdateDto updateDto) { + return itemService.updateItem(updateDto, userId, itemId); } @GetMapping("/{itemId}") - public ItemCommentDto getItemById(@RequestHeader("X-Sharer-User-Id") Long userId, @PathVariable Long itemId) { + public ItemCommentDto getByItemIdWithComment(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long itemId) { return itemService.getByItemIdWithComment(userId, itemId); } @GetMapping - public List getItemByOwnerId(@RequestHeader("X-Sharer-User-Id") Long userId) { - return itemService.findAllByOwnerIdWithBookings(userId); + public List allItemByOwnerIdWithComment(@RequestHeader("X-Sharer-User-Id") Long userId) { + return itemService.allItemByOwnerIdWithComment(userId); } @GetMapping("/search") - public List getItemsByNameOrDescription(@RequestParam String text) { + public List getItemsByNameOrDescription(@RequestHeader("X-Sharer-User-Id") Long userId, + @RequestParam String text) { return itemService.getItemsByNameOrDescription(text); } diff --git a/src/main/java/ru/practicum/shareit/item/ItemMapper.java b/server/src/main/java/ru/practicum/shareit/item/ItemMapper.java similarity index 59% rename from src/main/java/ru/practicum/shareit/item/ItemMapper.java rename to server/src/main/java/ru/practicum/shareit/item/ItemMapper.java index 088edeb..d2792fa 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemMapper.java +++ b/server/src/main/java/ru/practicum/shareit/item/ItemMapper.java @@ -4,9 +4,8 @@ import org.springframework.stereotype.Component; import ru.practicum.shareit.item.dto.ItemCreateDto; import ru.practicum.shareit.item.dto.ItemDto; -import ru.practicum.shareit.item.dto.ItemUpdateDto; import ru.practicum.shareit.item.model.Item; -import ru.practicum.shareit.user.dto.UserDto; +import ru.practicum.shareit.user.UserMapper; import java.util.ArrayList; import java.util.List; @@ -14,6 +13,7 @@ @RequiredArgsConstructor @Component public class ItemMapper { + private final UserMapper userMapper; public Item createDtoToModel(ItemCreateDto dto) { return Item.builder() @@ -23,33 +23,23 @@ public Item createDtoToModel(ItemCreateDto dto) { .build(); } - public Item updateDtoToModel(ItemUpdateDto dto, long itemId) { - return Item.builder() - .id(itemId) - .name(dto.getName()) - .description(dto.getDescription()) - .available(dto.getAvailable()) - .build(); - } - public ItemDto modelToItemDto(Item item) { return ItemDto.builder() .id(item.getId()) .name(item.getName()) .description(item.getDescription()) .available(item.getAvailable()) - .owner(UserDto.builder() - .id(item.getOwner().getId()) - .name(item.getOwner().getName()) - .email(item.getOwner().getEmail()) - .build()) + .owner(userMapper.modelToDto(item.getOwner())) + .request(item.getRequest() != null ? item.getRequest().getId() : null) .build(); } public List listModelToDto(List items) { List list = new ArrayList<>(); - for (Item item : items) { - list.add(modelToItemDto(item)); + if (items != null) { + for (Item item : items) { + list.add(modelToItemDto(item)); + } } return list; } diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java new file mode 100644 index 0000000..e69dbcd --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/dto/CommentCreateDto.java @@ -0,0 +1,12 @@ +package ru.practicum.shareit.item.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Getter +@Setter +public class CommentCreateDto { + private String text; +} diff --git a/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/CommentDto.java rename to server/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/CommentDtoReturn.java b/server/src/main/java/ru/practicum/shareit/item/dto/CommentDtoReturn.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/CommentDtoReturn.java rename to server/src/main/java/ru/practicum/shareit/item/dto/CommentDtoReturn.java diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemCommentDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemCommentDto.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/dto/ItemCommentDto.java rename to server/src/main/java/ru/practicum/shareit/item/dto/ItemCommentDto.java diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java new file mode 100644 index 0000000..6934721 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/dto/ItemCreateDto.java @@ -0,0 +1,15 @@ +package ru.practicum.shareit.item.dto; + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Getter +@Setter +public class ItemCreateDto { + private String name; + private String description; + private Boolean available; + private Long requestId; +} diff --git a/server/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java new file mode 100644 index 0000000..3c35cba --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java @@ -0,0 +1,26 @@ +package ru.practicum.shareit.item.dto; + +import lombok.*; +import ru.practicum.shareit.user.dto.UserDto; + +@AllArgsConstructor +@NoArgsConstructor +@Getter +@Setter +@Builder +public class ItemDto { + private Long id; + private String name; + private String description; + private Boolean available; + private UserDto owner; + private Long request; + + public ItemDto(Long id, String name, String description, Boolean available, UserDto owner) { + this.id = id; + this.name = name; + this.description = description; + this.available = available; + this.owner = owner; + } +} diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java b/server/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java similarity index 56% rename from src/main/java/ru/practicum/shareit/item/dto/ItemDto.java rename to server/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java index 378ad89..e9e08e2 100644 --- a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java +++ b/server/src/main/java/ru/practicum/shareit/item/dto/ItemUpdateDto.java @@ -1,21 +1,14 @@ package ru.practicum.shareit.item.dto; -import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.Setter; -import ru.practicum.shareit.user.dto.UserDto; - @Builder @Getter @Setter -@AllArgsConstructor -public class ItemDto { - private Long id; +public class ItemUpdateDto { private String name; private String description; private Boolean available; - private UserDto owner; - } 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 78% 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 dc6145a..8b96f5f 100644 --- a/src/main/java/ru/practicum/shareit/item/model/Item.java +++ b/server/src/main/java/ru/practicum/shareit/item/model/Item.java @@ -2,6 +2,7 @@ import jakarta.persistence.*; import lombok.*; +import ru.practicum.shareit.request.ItemRequest; import ru.practicum.shareit.user.User; @AllArgsConstructor @@ -29,5 +30,7 @@ public class Item { @JoinColumn(name = "owner_id") private User owner; - + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "request_id") + private ItemRequest request; } diff --git a/src/main/java/ru/practicum/shareit/item/repository/CommentRepository.java b/server/src/main/java/ru/practicum/shareit/item/repository/CommentRepository.java similarity index 100% rename from src/main/java/ru/practicum/shareit/item/repository/CommentRepository.java rename to server/src/main/java/ru/practicum/shareit/item/repository/CommentRepository.java diff --git a/src/main/java/ru/practicum/shareit/item/repository/ItemRepository.java b/server/src/main/java/ru/practicum/shareit/item/repository/ItemRepository.java similarity index 94% rename from src/main/java/ru/practicum/shareit/item/repository/ItemRepository.java rename to server/src/main/java/ru/practicum/shareit/item/repository/ItemRepository.java index 3b7323b..3fdb3e2 100644 --- a/src/main/java/ru/practicum/shareit/item/repository/ItemRepository.java +++ b/server/src/main/java/ru/practicum/shareit/item/repository/ItemRepository.java @@ -12,8 +12,6 @@ public interface ItemRepository extends JpaRepository { - List findByOwnerId(long ownerId); - @Query(""" SELECT new ru.practicum.shareit.item.dto.ItemCommentDto( i.id, @@ -47,7 +45,7 @@ public interface ItemRepository extends JpaRepository { JOIN User u ON i.owner.id = u.id WHERE i.owner.id = :ownerId ORDER BY i.id ASC""") - List findAllByOwnerWithBookings(@Param("ownerId") Long ownerId); + List allItemByOwnerIdWithComment(@Param("ownerId") Long ownerId); @Query("SELECT i FROM Item i " + "WHERE i.available = true " + diff --git a/src/main/java/ru/practicum/shareit/item/service/ItemService.java b/server/src/main/java/ru/practicum/shareit/item/service/ItemService.java similarity index 75% rename from src/main/java/ru/practicum/shareit/item/service/ItemService.java rename to server/src/main/java/ru/practicum/shareit/item/service/ItemService.java index 333ccf9..1dc12f5 100644 --- a/src/main/java/ru/practicum/shareit/item/service/ItemService.java +++ b/server/src/main/java/ru/practicum/shareit/item/service/ItemService.java @@ -9,11 +9,11 @@ public interface ItemService { ItemCommentDto getByItemIdWithComment(long userId, long itemId); - List findAllByOwnerIdWithBookings(Long ownerId); + List allItemByOwnerIdWithComment(Long ownerId); ItemDto createItem(ItemCreateDto createDto, long ownerId); - ItemDto update(ItemUpdateDto updateDto, long ownerId, long itemId); + ItemDto updateItem(ItemUpdateDto updateDto, long ownerId, long itemId); List getItemsByNameOrDescription(String text); diff --git a/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java b/server/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java similarity index 86% rename from src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java rename to server/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java index 6638ba9..48f6014 100644 --- a/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java +++ b/server/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java @@ -2,6 +2,7 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import ru.practicum.shareit.booking.BookingRepository; import ru.practicum.shareit.exception.BadRequestException; import ru.practicum.shareit.exception.ConflictException; @@ -13,6 +14,8 @@ import ru.practicum.shareit.item.model.Item; import ru.practicum.shareit.item.repository.CommentRepository; import ru.practicum.shareit.item.repository.ItemRepository; +import ru.practicum.shareit.request.ItemRequest; +import ru.practicum.shareit.request.ItemRequestRepository; import ru.practicum.shareit.user.User; import ru.practicum.shareit.user.UserRepository; @@ -21,6 +24,7 @@ import java.util.Map; import java.util.stream.Collectors; +@Transactional @Service @RequiredArgsConstructor public class ItemServiceImpl implements ItemService { @@ -30,6 +34,7 @@ public class ItemServiceImpl implements ItemService { private final ItemMapper itemMapper; private final CommentMapper commentMapper; private final CommentRepository commentRepository; + private final ItemRequestRepository itemRequestRepository; @Override public CommentDtoReturn createComment(long authorId, long itemId, CommentCreateDto createDto) { @@ -59,13 +64,12 @@ public ItemCommentDto getByItemIdWithComment(long userId, long itemId) { } @Override - public List findAllByOwnerIdWithBookings(Long ownerId) { - List items = itemRepository.findAllByOwnerWithBookings(ownerId); + public List allItemByOwnerIdWithComment(Long ownerId) { + List items = itemRepository.allItemByOwnerIdWithComment(ownerId); if (items.isEmpty()) { throw new NotFoundException("У пользователя с ID " + ownerId + " нет вещей"); } - List itemIds = items.stream() .map(ItemCommentDto::getId) .collect(Collectors.toList()); @@ -76,7 +80,6 @@ public List findAllByOwnerIdWithBookings(Long ownerId) { comment -> comment.getItem().getId(), Collectors.toList() )); - items.forEach(item -> item.setComments(commentsMap.getOrDefault(item.getId(), Collections.emptyList())) ); @@ -88,11 +91,16 @@ public ItemDto createItem(ItemCreateDto createDto, long ownerId) { Item item = itemMapper.createDtoToModel(createDto); User user = checkAndReturnUser(ownerId); item.setOwner(user); + if (createDto.getRequestId() != null) { + ItemRequest itemRequest = itemRequestRepository.findById(createDto.getRequestId()) + .orElseThrow(() -> new NotFoundException("ItemRequest не найден")); + item.setRequest(itemRequest); + } return itemMapper.modelToItemDto(itemRepository.save(item)); } @Override - public ItemDto update(ItemUpdateDto updateDto, long ownerId, long itemId) { + public ItemDto updateItem(ItemUpdateDto updateDto, long ownerId, long itemId) { User owner = checkAndReturnUser(ownerId); Item item = checkAndReturnItem(itemId); diff --git a/server/src/main/java/ru/practicum/shareit/request/ItemRequest.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequest.java new file mode 100644 index 0000000..0c945cb --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/ItemRequest.java @@ -0,0 +1,37 @@ +package ru.practicum.shareit.request; + + +import jakarta.persistence.*; +import lombok.*; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.User; + +import java.time.LocalDateTime; +import java.util.List; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Getter +@Setter +@ToString +@Entity +@Table(name = "requests") +public class ItemRequest { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String description; + + @ManyToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "requestor_id", nullable = false) + private User requestor; + + @Column(name = "created", nullable = false) + private LocalDateTime created; + + @OneToMany(mappedBy = "request", cascade = CascadeType.ALL) + private List items; +} diff --git a/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java new file mode 100644 index 0000000..0b3d450 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/ItemRequestController.java @@ -0,0 +1,40 @@ +package ru.practicum.shareit.request; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; +import ru.practicum.shareit.request.dto.ItemRequestDto; +import ru.practicum.shareit.request.service.ItemRequestService; + +import java.util.List; + + +@RequiredArgsConstructor +@RestController +@RequestMapping(path = "/requests") +public class ItemRequestController { + private final ItemRequestService itemRequestService; + + @PostMapping + public ItemRequestDto createItemRequest(@RequestHeader("X-Sharer-User-Id") Long userId, + @RequestBody ItemRequestCreateDto createDto) { + return itemRequestService.createItemRequest(userId, createDto); + } + + @GetMapping + public List getItemRequestsByUserId(@RequestHeader("X-Sharer-User-Id") Long userId) { + return itemRequestService.getItemRequestsByUserId(userId); + } + + @GetMapping("/all") + public List getItemRequestsByNotUserId(@RequestHeader("X-Sharer-User-Id") Long userId) { + return itemRequestService.findAllByNotRequestorIdSorted(userId); + } + + @GetMapping("{requestId}") + public ItemRequestDto getItemRequestById(@RequestHeader("X-Sharer-User-Id") Long userId, + @PathVariable Long requestId) { + return itemRequestService.getItemRequestById(requestId); + } + +} diff --git a/server/src/main/java/ru/practicum/shareit/request/ItemRequestMapper.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequestMapper.java new file mode 100644 index 0000000..335a330 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/ItemRequestMapper.java @@ -0,0 +1,45 @@ +package ru.practicum.shareit.request; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import ru.practicum.shareit.item.ItemMapper; +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; +import ru.practicum.shareit.request.dto.ItemRequestDto; +import ru.practicum.shareit.user.UserMapper; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +@RequiredArgsConstructor +@Component +public class ItemRequestMapper { + private final UserMapper userMapper; + private final ItemMapper itemMapper; + + public ItemRequest itemRequestCreateDtoToModel(ItemRequestCreateDto createDto) { + return ItemRequest.builder() + .description(createDto.getDescription()) + .created(LocalDateTime.now()) + .build(); + } + + public ItemRequestDto modelToItemRequestDto(ItemRequest itemRequest) { + return ItemRequestDto.builder() + .id(itemRequest.getId()) + .description(itemRequest.getDescription()) + .requestor(userMapper.modelToDto(itemRequest.getRequestor())) + .created(itemRequest.getCreated()) + .items(itemMapper.listModelToDto(itemRequest.getItems())) + .build(); + } + + public List listModelToItemRequestDto(List requests) { + List listDto = new ArrayList<>(); + for (ItemRequest request : requests) { + listDto.add(modelToItemRequestDto(request)); + } + return listDto; + } + +} diff --git a/server/src/main/java/ru/practicum/shareit/request/ItemRequestRepository.java b/server/src/main/java/ru/practicum/shareit/request/ItemRequestRepository.java new file mode 100644 index 0000000..82432af --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/ItemRequestRepository.java @@ -0,0 +1,27 @@ +package ru.practicum.shareit.request; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; + +import java.util.List; + +public interface ItemRequestRepository extends JpaRepository { + + @Query(""" + SELECT ir + FROM ItemRequest ir + WHERE ir.requestor.id = :requestorId + ORDER BY ir.created DESC + """) + List findAllByRequestorIdSorted(@Param("requestorId") Long requestorId); + + @Query(""" + SELECT ir + FROM ItemRequest ir + WHERE ir.requestor.id <> :requestorId + ORDER BY ir.created DESC + """) + List findAllByNotRequestorIdSorted(@Param("requestorId") Long requestorId); + +} diff --git a/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java b/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java new file mode 100644 index 0000000..b8b078b --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestCreateDto.java @@ -0,0 +1,13 @@ +package ru.practicum.shareit.request.dto; + + +import lombok.*; + +@AllArgsConstructor +@NoArgsConstructor +@Builder +@Getter +@Setter +public class ItemRequestCreateDto { + private String description; +} diff --git a/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java b/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java new file mode 100644 index 0000000..561874d --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java @@ -0,0 +1,24 @@ +package ru.practicum.shareit.request.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.user.dto.UserDto; + +import java.time.LocalDateTime; +import java.util.List; + +@Builder +@Getter +@Setter +@AllArgsConstructor +public class ItemRequestDto { + private Long id; + private String description; + private UserDto requestor; + private LocalDateTime created; + + private List items; +} diff --git a/server/src/main/java/ru/practicum/shareit/request/service/ItemRequestService.java b/server/src/main/java/ru/practicum/shareit/request/service/ItemRequestService.java new file mode 100644 index 0000000..4394a09 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/service/ItemRequestService.java @@ -0,0 +1,17 @@ +package ru.practicum.shareit.request.service; + +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; +import ru.practicum.shareit.request.dto.ItemRequestDto; + +import java.util.List; + +public interface ItemRequestService { + + ItemRequestDto createItemRequest(long requestorId, ItemRequestCreateDto createDto); + + List getItemRequestsByUserId(long requestorId); + + List findAllByNotRequestorIdSorted(long requestorId); + + ItemRequestDto getItemRequestById(long itemRequestId); +} diff --git a/server/src/main/java/ru/practicum/shareit/request/service/ItemRequestServiceImpl.java b/server/src/main/java/ru/practicum/shareit/request/service/ItemRequestServiceImpl.java new file mode 100644 index 0000000..a4e5a3b --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/request/service/ItemRequestServiceImpl.java @@ -0,0 +1,65 @@ +package ru.practicum.shareit.request.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.request.ItemRequest; +import ru.practicum.shareit.request.ItemRequestMapper; +import ru.practicum.shareit.request.ItemRequestRepository; +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; +import ru.practicum.shareit.request.dto.ItemRequestDto; +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.UserRepository; + +import java.util.List; + +@Transactional +@Service +@RequiredArgsConstructor +public class ItemRequestServiceImpl implements ItemRequestService { + private final ItemRequestRepository itemRequestRepository; + private final UserRepository userRepository; + private final ItemRequestMapper itemRequestMapper; + + @Override + public ItemRequestDto createItemRequest(long requestorId, ItemRequestCreateDto createDto) { + User requestor = checkAndReturnUser(requestorId); + ItemRequest itemRequest = itemRequestMapper.itemRequestCreateDtoToModel(createDto); + itemRequest.setRequestor(requestor); + itemRequest = itemRequestRepository.save(itemRequest); + return itemRequestMapper.modelToItemRequestDto(itemRequest); + } + + @Override + public List getItemRequestsByUserId(long requestorId) { + checkAndReturnUser(requestorId); + List requests = itemRequestRepository.findAllByRequestorIdSorted(requestorId); + if (requests.isEmpty()) { + throw new NotFoundException("У пользователя с ID " + requestorId + " нет запросов"); + } + return itemRequestMapper.listModelToItemRequestDto(requests); + } + + @Override + public List findAllByNotRequestorIdSorted(long requestorId) { + checkAndReturnUser(requestorId); + List requests = itemRequestRepository.findAllByNotRequestorIdSorted(requestorId); + if (requests.isEmpty()) { + throw new NotFoundException("У пользователя с ID " + requestorId + " нет запросов"); + } + return itemRequestMapper.listModelToItemRequestDto(requests); + } + + @Override + public ItemRequestDto getItemRequestById(long itemRequestId) { + ItemRequest itemRequest = itemRequestRepository.findById(itemRequestId) + .orElseThrow(() -> new NotFoundException("ItemRequest не найден")); + return itemRequestMapper.modelToItemRequestDto(itemRequest); + } + + private User checkAndReturnUser(long id) { + return userRepository.findById(id).orElseThrow(() -> new NotFoundException("User не найден")); + } + +} diff --git a/src/main/java/ru/practicum/shareit/user/User.java b/server/src/main/java/ru/practicum/shareit/user/User.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/User.java rename to server/src/main/java/ru/practicum/shareit/user/User.java diff --git a/src/main/java/ru/practicum/shareit/user/UserController.java b/server/src/main/java/ru/practicum/shareit/user/UserController.java similarity index 55% rename from src/main/java/ru/practicum/shareit/user/UserController.java rename to server/src/main/java/ru/practicum/shareit/user/UserController.java index 9d688ce..4c430c0 100644 --- a/src/main/java/ru/practicum/shareit/user/UserController.java +++ b/server/src/main/java/ru/practicum/shareit/user/UserController.java @@ -1,7 +1,7 @@ package ru.practicum.shareit.user; import lombok.RequiredArgsConstructor; -import org.springframework.validation.annotation.Validated; +import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import ru.practicum.shareit.user.dto.UserCreateDto; import ru.practicum.shareit.user.dto.UserDto; @@ -10,37 +10,33 @@ import java.util.Collection; -@Validated @RestController @RequiredArgsConstructor @RequestMapping("/users") public class UserController { private final UserService userService; - private final UserMapper userMapper; @PostMapping - public UserDto create(@RequestBody @Validated UserCreateDto userDto) { - User user = userMapper.createDtoToModel(userDto); - return userMapper.modelToDto(userService.create(user)); + public UserDto create(@RequestBody UserCreateDto userDto) { + return userService.create(userDto); } @PatchMapping("/{userId}") - public UserDto update(@PathVariable Long userId, - @RequestBody @Validated UserUpdateDto userDto) { - User user = userMapper.updateDtoToModel(userDto, userId); - return userMapper.modelToDto(userService.update(user)); + public UserDto update(@PathVariable Long userId, @RequestBody UserUpdateDto userDto) { + return userService.update(userId, userDto); } @GetMapping("/{userId}") public UserDto getUserById(@PathVariable Long userId) { - return userMapper.modelToDto(userService.getUserById(userId)); + return userService.getUserById(userId); } @GetMapping() public Collection getAll() { - return userMapper.listModelToDto(userService.getAll()); + return userService.getAll(); } + @ResponseStatus(HttpStatus.OK) @DeleteMapping("/{userId}") public void deleteUser(@PathVariable long userId) { userService.deleteUser(userId); diff --git a/src/main/java/ru/practicum/shareit/user/UserMapper.java b/server/src/main/java/ru/practicum/shareit/user/UserMapper.java similarity index 71% rename from src/main/java/ru/practicum/shareit/user/UserMapper.java rename to server/src/main/java/ru/practicum/shareit/user/UserMapper.java index 0d28209..2ad8498 100644 --- a/src/main/java/ru/practicum/shareit/user/UserMapper.java +++ b/server/src/main/java/ru/practicum/shareit/user/UserMapper.java @@ -3,7 +3,6 @@ import org.springframework.stereotype.Component; import ru.practicum.shareit.user.dto.UserCreateDto; import ru.practicum.shareit.user.dto.UserDto; -import ru.practicum.shareit.user.dto.UserUpdateDto; import java.util.ArrayList; import java.util.Collection; @@ -19,14 +18,6 @@ public User createDtoToModel(UserCreateDto dto) { .build(); } - public User updateDtoToModel(UserUpdateDto dto, Long userId) { - return User.builder() - .id(userId) - .name(dto.getName()) - .email(dto.getEmail()) - .build(); - } - public UserDto modelToDto(User user) { return UserDto.builder() .id(user.getId()) @@ -35,7 +26,7 @@ public UserDto modelToDto(User user) { .build(); } - public Collection listModelToDto(Collection users) { + public List listModelToDto(Collection users) { List list = new ArrayList<>(); for (User user : users) { list.add(modelToDto(user)); diff --git a/src/main/java/ru/practicum/shareit/user/UserRepository.java b/server/src/main/java/ru/practicum/shareit/user/UserRepository.java similarity index 100% rename from src/main/java/ru/practicum/shareit/user/UserRepository.java rename to server/src/main/java/ru/practicum/shareit/user/UserRepository.java diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserDto.java b/server/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java similarity index 85% rename from src/main/java/ru/practicum/shareit/user/dto/UserDto.java rename to server/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java index babd3d4..eefb912 100644 --- a/src/main/java/ru/practicum/shareit/user/dto/UserDto.java +++ b/server/src/main/java/ru/practicum/shareit/user/dto/UserCreateDto.java @@ -7,10 +7,9 @@ @AllArgsConstructor @NoArgsConstructor -@Data @Builder -public class UserDto { - private Long id; +@Data +public class UserCreateDto { private String name; private String email; } diff --git a/server/src/main/java/ru/practicum/shareit/user/dto/UserDto.java b/server/src/main/java/ru/practicum/shareit/user/dto/UserDto.java new file mode 100644 index 0000000..8d975e4 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/dto/UserDto.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.user.dto; + +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + +@NoArgsConstructor +@Data +@Builder +public class UserDto { + private Long id; + private String name; + private String email; + + public UserDto(Long id, String name, String email) { + this.id = id; + this.name = name; + this.email = email; + } +} diff --git a/server/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java b/server/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java new file mode 100644 index 0000000..5e0f73c --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/dto/UserUpdateDto.java @@ -0,0 +1,11 @@ +package ru.practicum.shareit.user.dto; + +import lombok.Builder; +import lombok.Data; + +@Builder +@Data +public class UserUpdateDto { + private String name; + private String email; +} diff --git a/server/src/main/java/ru/practicum/shareit/user/service/UserService.java b/server/src/main/java/ru/practicum/shareit/user/service/UserService.java new file mode 100644 index 0000000..5b67449 --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/service/UserService.java @@ -0,0 +1,19 @@ +package ru.practicum.shareit.user.service; + +import ru.practicum.shareit.user.dto.UserCreateDto; +import ru.practicum.shareit.user.dto.UserDto; +import ru.practicum.shareit.user.dto.UserUpdateDto; + +import java.util.List; + +public interface UserService { + UserDto create(UserCreateDto userDto); + + UserDto update(Long userId, UserUpdateDto userDto); + + UserDto getUserById(long id); + + List getAll(); + + void deleteUser(long userId); +} diff --git a/server/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java b/server/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java new file mode 100644 index 0000000..2203a8a --- /dev/null +++ b/server/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java @@ -0,0 +1,60 @@ +package ru.practicum.shareit.user.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.UserMapper; +import ru.practicum.shareit.user.UserRepository; +import ru.practicum.shareit.user.dto.UserCreateDto; +import ru.practicum.shareit.user.dto.UserDto; +import ru.practicum.shareit.user.dto.UserUpdateDto; + +import java.util.List; + +@Transactional +@Service +@RequiredArgsConstructor +public class UserServiceImpl implements UserService { + private final UserRepository userRepository; + private final UserMapper userMapper; + + @Override + public UserDto create(UserCreateDto userDto) { + User user = userMapper.createDtoToModel(userDto); + return userMapper.modelToDto(userRepository.save(user)); + } + + @Override + public UserDto update(Long userId, UserUpdateDto userDto) { + User user = checkAndReturnUser(userId); + + if (userDto.getName() != null) { + user.setName(userDto.getName()); + } + if (userDto.getEmail() != null) { + user.setEmail(userDto.getEmail()); + } + return userMapper.modelToDto(userRepository.save(user)); + } + + @Override + public UserDto getUserById(long id) { + return userMapper.modelToDto(checkAndReturnUser(id)); + } + + @Override + public List getAll() { + return userMapper.listModelToDto(userRepository.findAll()); + } + + @Override + public void deleteUser(long userId) { + userRepository.deleteById(userId); + } + + private User checkAndReturnUser(long id) { + return userRepository.findById(id).orElseThrow(() -> new NotFoundException("User не найден")); + } +} diff --git a/src/main/resources/application.properties b/server/src/main/resources/application.properties similarity index 82% rename from src/main/resources/application.properties rename to server/src/main/resources/application.properties index 2201553..82fc045 100644 --- a/src/main/resources/application.properties +++ b/server/src/main/resources/application.properties @@ -1,6 +1,8 @@ + +server.port=9090 +spring.output.ansi.enabled=ALWAYS spring.jpa.hibernate.ddl-auto=none spring.jpa.properties.hibernate.format_sql=true - spring.sql.init.mode=always logging.level.org.zalando.logbook= TRACE @@ -12,9 +14,9 @@ logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG spring.datasource.driverClassName=org.postgresql.Driver -spring.datasource.url=jdbc:postgresql://localhost:5432/shareit +spring.datasource.url=jdbc:postgresql://localhost:6541/shareit spring.datasource.username=shareit -spring.datasource.password=12345 +spring.datasource.password=shareit diff --git a/src/main/resources/schema.sql b/server/src/main/resources/schema.sql similarity index 85% rename from src/main/resources/schema.sql rename to server/src/main/resources/schema.sql index 9e5a425..4cd0b8b 100644 --- a/src/main/resources/schema.sql +++ b/server/src/main/resources/schema.sql @@ -8,10 +8,10 @@ CREATE TABLE IF NOT EXISTS users ( CREATE TABLE IF NOT EXISTS requests ( id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, - description VARCHAR(255), - requestor INTEGER NOT NULL, - time_request TIMESTAMP WITHOUT TIME ZONE, - FOREIGN KEY (requestor) REFERENCES users(id) ON DELETE CASCADE + description VARCHAR(255) NOT NULL, + requestor_id INTEGER NOT NULL, + created TIMESTAMP WITHOUT TIME ZONE, + FOREIGN KEY (requestor_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS items ( @@ -20,6 +20,8 @@ CREATE TABLE IF NOT EXISTS items ( description VARCHAR(255), available boolean NOT NULL, owner_id INTEGER NOT NULL, + request_id INTEGER, + FOREIGN KEY (request_id) REFERENCES requests(id), FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE CASCADE ); diff --git a/server/src/test/java/ru/practicum/shareit/ExceptionTest.java b/server/src/test/java/ru/practicum/shareit/ExceptionTest.java new file mode 100644 index 0000000..96c95a8 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/ExceptionTest.java @@ -0,0 +1,37 @@ +package ru.practicum.shareit; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import ru.practicum.shareit.exception.ErrorResponse; +import ru.practicum.shareit.exception.InternalServerErrorException; +import ru.practicum.shareit.exception.ValidationException; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +@SpringBootTest +class ExceptionTest { + + @Test + void contextLoads() { + } + + @Test + void errorResponseFieldsShouldBeSetCorrectly() { + ErrorResponse response = new ErrorResponse("Ошибка", "Описание ошибки"); + assertEquals("Ошибка", response.getError()); + assertEquals("Описание ошибки", response.getDescription()); + } + + @Test + void validationExceptionShouldStoreMessage() { + ValidationException ex = new ValidationException("Некорректные данные"); + assertEquals("Некорректные данные", ex.getMessage()); + } + + @Test + void internalServerErrorExceptionShouldStoreMessage() { + InternalServerErrorException ex = new InternalServerErrorException("Внутренняя ошибка"); + assertEquals("Внутренняя ошибка", ex.getMessage()); + } + +} diff --git a/server/src/test/java/ru/practicum/shareit/booking/BookingControllerTest.java b/server/src/test/java/ru/practicum/shareit/booking/BookingControllerTest.java new file mode 100644 index 0000000..1b8b7c9 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/booking/BookingControllerTest.java @@ -0,0 +1,135 @@ +package ru.practicum.shareit.booking; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import ru.practicum.shareit.booking.dto.BookingCreateDto; +import ru.practicum.shareit.booking.dto.BookingDto; +import ru.practicum.shareit.booking.service.BookingService; +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.user.dto.UserDto; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(BookingController.class) +@AutoConfigureMockMvc +class BookingControllerTest { + @MockBean + private BookingService bookingService; + + @Autowired + private MockMvc mockMvc; + + private final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()); + + @Test + void setBookingItem() throws Exception { + BookingCreateDto createDto = BookingCreateDto.builder() + .start(LocalDateTime.now().plusDays(1)) + .end(LocalDateTime.now().plusDays(2)) + .itemId(1L) + .build(); + BookingDto responseDto = BookingDto.builder() + .id(1L) + .start(createDto.getStart()) + .end(createDto.getEnd()) + .item(ItemDto.builder().id(1L).name("Drill").description("Power drill").available(true).build()) + .booker(UserDto.builder().id(1L).name("John").email("john@example.com").build()) + .status(BookingStatus.WAITING) + .build(); + Mockito.when(bookingService.setBookingItem(any(BookingCreateDto.class), eq(1L))).thenReturn(responseDto); + + mockMvc.perform(MockMvcRequestBuilders.post("/bookings") + .contentType(MediaType.APPLICATION_JSON) + .header("X-Sharer-User-Id", 1L) + .content(objectMapper.writeValueAsString(createDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(1L)) + .andExpect(jsonPath("$.status").value("WAITING")); + + Mockito.verify(bookingService, Mockito.times(1)).setBookingItem(any(BookingCreateDto.class), eq(1L)); + } + + @Test + void setBookingStatus() throws Exception { + BookingDto responseDto = BookingDto.builder() + .id(2L) + .status(BookingStatus.APPROVED) + .build(); + Mockito.when(bookingService.setBookingStatus(eq(1L), eq(2L), eq(true))).thenReturn(responseDto); + + mockMvc.perform(MockMvcRequestBuilders.patch("/bookings/2") + .param("approved", "true") + .header("X-Sharer-User-Id", 1L)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(2L)) + .andExpect(jsonPath("$.status").value("APPROVED")); + + Mockito.verify(bookingService, Mockito.times(1)).setBookingStatus(eq(1L), eq(2L), eq(true)); + } + + @Test + void getBookingById() throws Exception { + BookingDto responseDto = BookingDto.builder() + .id(3L) + .status(BookingStatus.REJECTED) + .build(); + Mockito.when(bookingService.getBookingById(eq(1L), eq(3L))).thenReturn(responseDto); + + mockMvc.perform(MockMvcRequestBuilders.get("/bookings/3") + .header("X-Sharer-User-Id", 1L)) + .andExpect(status().isOk()) + .andExpect(jsonPath(".id").value(3)) + .andExpect(jsonPath(".status").value("REJECTED")); + + Mockito.verify(bookingService, Mockito.times(1)).getBookingById(eq(1L), eq(3L)); + } + + @Test + void getBookingByBookerIdWhereTime() throws Exception { + List bookings = List.of( + BookingDto.builder().id(4L).status(BookingStatus.APPROVED).build() + ); + Mockito.when(bookingService.getBookingByBookerIdWhereTime(eq(1L), eq("ALL"))).thenReturn(bookings); + + mockMvc.perform(MockMvcRequestBuilders.get("/bookings") + .param("state", "ALL") + .header("X-Sharer-User-Id", 1L)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id").value(4L)) + .andExpect(jsonPath("$[0].status").value("APPROVED")); + + Mockito.verify(bookingService, Mockito.times(1)).getBookingByBookerIdWhereTime(eq(1L), eq("ALL")); + } + + @Test + void getBookingByOwnerIdWhereTime() throws Exception { + List bookings = List.of( + BookingDto.builder().id(5L).status(BookingStatus.CANCELED).build() + ); + Mockito.when(bookingService.getBookingByOwnerIdWhereTime(eq(1L), eq("ALL"))).thenReturn(bookings); + + mockMvc.perform(MockMvcRequestBuilders.get("/bookings/owner") + .param("state", "ALL") + .header("X-Sharer-User-Id", 1L)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id").value(5L)) + .andExpect(jsonPath("$[0].status").value("CANCELED")); + + Mockito.verify(bookingService, Mockito.times(1)).getBookingByOwnerIdWhereTime(eq(1L), eq("ALL")); + } +} \ No newline at end of file diff --git a/server/src/test/java/ru/practicum/shareit/booking/BookingRepositoryTest.java b/server/src/test/java/ru/practicum/shareit/booking/BookingRepositoryTest.java new file mode 100644 index 0000000..ca56b9f --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/booking/BookingRepositoryTest.java @@ -0,0 +1,105 @@ +package ru.practicum.shareit.booking; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.jdbc.Sql; +import ru.practicum.shareit.booking.dto.BookingCreateDto; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +@DataJpaTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@Sql(scripts = {"/data/cleanup.sql", "/data/data.sql"}) +class BookingRepositoryTest { // Данные для теста класса подготовлены в файле data.sql + @Autowired + private BookingRepository bookingRepository; + + @Test + void hasUserBookedItem() { + assertThat(bookingRepository.hasUserBookedItem(2L, 1L), equalTo(true)); + assertThat(bookingRepository.hasUserBookedItem(3L, 2L), equalTo(false)); + } + + @Test + void existsByItemIdAndTimeRange() { + BookingCreateDto dto1 = BookingCreateDto.builder() + .itemId(3L) + .start(LocalDateTime.parse("2025-07-10T22:00:00")) + .end(LocalDateTime.parse("2025-07-10T22:00:00")) + .build(); + + BookingCreateDto dto2 = BookingCreateDto.builder() + .itemId(4L) + .start(LocalDateTime.parse("2025-05-10T20:00:00")) + .end(LocalDateTime.parse("2025-05-11T20:00:00")) + .build(); + + assertThat(bookingRepository.existsByItemIdAndTimeRange(dto1.getItemId(), dto1.getStart(), dto1.getEnd()), + equalTo(false)); // Этот промежуток времени свободен, бронь состоится + assertThat(bookingRepository.existsByItemIdAndTimeRange(dto2.getItemId(), dto2.getStart(), dto2.getEnd()), + equalTo(true)); // Этот промежуток времени занят, бронь невозможна + } + + @Test + void getBookingByBookerIdWhereTime() { + Long bookerId = 1L; + List listAll = bookingRepository.getBookingByBookerIdWhereTime(bookerId, "ALL"); + assertThat(listAll.size(), equalTo(2)); + assertThat(listAll.getFirst().getBooker().getId(), equalTo(1L)); + assertThat(listAll.getFirst().getItem(), notNullValue()); + assertThat(listAll.getLast().getBooker().getId(), equalTo(1L)); + assertThat(listAll.getLast().getItem(), notNullValue()); + + List listPast = bookingRepository.getBookingByBookerIdWhereTime(bookerId, "PAST"); + assertThat(listPast.size(), equalTo(1)); + assertThat(listPast.getFirst().getBooker().getId(), equalTo(bookerId)); + assertThat(listPast.getLast().getItem(), notNullValue()); + + List listFWaiting = bookingRepository.getBookingByBookerIdWhereTime(bookerId, "WAITING"); + assertThat(listFWaiting.size(), equalTo(0)); + + List listRejected = bookingRepository.getBookingByBookerIdWhereTime(bookerId, "REJECTED"); + assertThat(listRejected.size(), equalTo(1)); + assertThat(listRejected.getFirst().getBooker().getId(), equalTo(bookerId)); + assertThat(listRejected.getLast().getItem(), notNullValue()); + } + + @Test + void getBookingByOwnerIdWhereTime() { + Long ownerId = 2L; + List listAll = bookingRepository.getBookingByOwnerIdWhereTime(ownerId, "ALL"); + assertThat(listAll.size(), equalTo(2)); + assertThat(listAll.getFirst().getItem().getOwner().getId(), equalTo(ownerId)); + assertThat(listAll.getFirst().getBooker(), notNullValue()); + assertThat(listAll.getLast().getItem().getOwner().getId(), equalTo(ownerId)); + assertThat(listAll.getLast().getBooker(), notNullValue()); + + List listCurrent = bookingRepository.getBookingByOwnerIdWhereTime(ownerId, "CURRENT"); + assertThat(listCurrent.size(), equalTo(0)); + + List listPast = bookingRepository.getBookingByOwnerIdWhereTime(ownerId, "PAST"); + assertThat(listPast.size(), equalTo(2)); + assertThat(listPast.getFirst().getItem().getOwner().getId(), equalTo(ownerId)); + assertThat(listPast.getFirst().getBooker(), notNullValue()); + assertThat(listPast.getLast().getItem().getOwner().getId(), equalTo(ownerId)); + assertThat(listPast.getLast().getBooker(), notNullValue()); + + List listFuture = bookingRepository.getBookingByOwnerIdWhereTime(ownerId, "FUTURE"); + assertThat(listFuture.size(), equalTo(0)); + + List listFWaiting = bookingRepository.getBookingByOwnerIdWhereTime(ownerId, "WAITING"); + assertThat(listFWaiting.size(), equalTo(1)); + assertThat(listFWaiting.getFirst().getItem().getOwner().getId(), equalTo(ownerId)); + assertThat(listFWaiting.getFirst().getBooker(), notNullValue()); + + List listRejected = bookingRepository.getBookingByOwnerIdWhereTime(ownerId, "REJECTED"); + assertThat(listRejected.size(), equalTo(0)); + } +} \ No newline at end of file diff --git a/server/src/test/java/ru/practicum/shareit/booking/service/BookingServiceTest.java b/server/src/test/java/ru/practicum/shareit/booking/service/BookingServiceTest.java new file mode 100644 index 0000000..69ba171 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/booking/service/BookingServiceTest.java @@ -0,0 +1,133 @@ +package ru.practicum.shareit.booking.service; + +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 org.springframework.test.context.jdbc.Sql; +import ru.practicum.shareit.booking.BookingStatus; +import ru.practicum.shareit.booking.dto.BookingCreateDto; +import ru.practicum.shareit.booking.dto.BookingDto; +import ru.practicum.shareit.exception.BadRequestException; +import ru.practicum.shareit.exception.ConflictException; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@Transactional +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@SpringBootTest +@Sql(scripts = "/data/data_repository.sql") +class BookingServiceTest { + + private final BookingService bookingService; + + BookingCreateDto createDto1 = BookingCreateDto.builder() + .itemId(1L) + .start(LocalDateTime.parse("2050-07-01T08:00:00")) + .end(LocalDateTime.parse("2050-08-01T08:00:00")) + .build(); + + BookingCreateDto createDto2 = BookingCreateDto.builder() + .itemId(3L) + .start(LocalDateTime.parse("2050-07-01T08:00:00")) + .end(LocalDateTime.parse("2050-08-01T08:00:00")) + .build(); + + + @Test + void setBookingItem() { + BookingDto bookingDto = bookingService.setBookingItem(createDto1, 1); + + assertThat(bookingDto.getStart(), equalTo(createDto1.getStart())); + assertThat(bookingDto.getEnd(), equalTo(createDto1.getEnd())); + assertThat(bookingDto.getItem().getId(), equalTo(createDto1.getItemId())); + assertThrows(BadRequestException.class, () -> bookingService.setBookingItem(createDto2, 1)); + assertThrows(ConflictException.class, () -> bookingService.setBookingItem(createDto1, 1)); + } + + @Test + void setBookingStatus() { + BookingDto bookingDto1 = bookingService.setBookingStatus(2, 2, true); + BookingDto bookingDto2 = bookingService.setBookingStatus(3, 3, false); + + assertThat(bookingDto1.getStatus(), equalTo(BookingStatus.APPROVED)); + assertThat(bookingDto2.getStatus(), equalTo(BookingStatus.REJECTED)); + assertThrows(BadRequestException.class, () -> + bookingService.setBookingStatus(1, 3, true)); + } + + @Test + void getBookingById() { + long userId = 1L; + BookingDto bookingDtoCreated = bookingService.setBookingItem(createDto1, userId); + BookingDto bookingDto = bookingService.getBookingById(userId, bookingDtoCreated.getId()); + + assertThat(bookingDto.getStart(), equalTo(createDto1.getStart())); + assertThat(bookingDto.getEnd(), equalTo(createDto1.getEnd())); + assertThat(bookingDto.getItem().getId(), equalTo(createDto1.getItemId())); + assertThat(bookingDto.getBooker().getId(), equalTo(userId)); + assertThrows(ConflictException.class, () -> + bookingService.getBookingById(999, 1)); + } + + @Test + void getBookingByBookerIdWhereTime() { + long bookerId = 1L; + List listAll = bookingService.getBookingByBookerIdWhereTime(bookerId, "ALL"); + assertThat(listAll.size(), equalTo(2)); + assertThat(listAll.getFirst().getBooker().getId(), equalTo(1L)); + assertThat(listAll.getFirst().getItem(), notNullValue()); + assertThat(listAll.getLast().getBooker().getId(), equalTo(1L)); + assertThat(listAll.getLast().getItem(), notNullValue()); + + List listPast = bookingService.getBookingByBookerIdWhereTime(bookerId, "PAST"); + assertThat(listPast.size(), equalTo(1)); + assertThat(listPast.getFirst().getBooker().getId(), equalTo(bookerId)); + assertThat(listPast.getLast().getItem(), notNullValue()); + + + assertThrows(ConflictException.class, () -> + bookingService.getBookingByBookerIdWhereTime(bookerId, "WAITING")); + + List listRejected = bookingService.getBookingByBookerIdWhereTime(bookerId, "REJECTED"); + assertThat(listRejected.size(), equalTo(1)); + assertThat(listRejected.getFirst().getBooker().getId(), equalTo(bookerId)); + assertThat(listRejected.getLast().getItem(), notNullValue()); + + } + + @Test + void getBookingByOwnerIdWhereTime() { + long ownerId = 2L; + List listAll = bookingService.getBookingByOwnerIdWhereTime(ownerId, "ALL"); + assertThat(listAll.size(), equalTo(1)); + assertThat(listAll.getFirst().getItem().getOwner().getId(), equalTo(ownerId)); + assertThat(listAll.getFirst().getBooker(), notNullValue()); + + assertThrows(ConflictException.class, () -> + bookingService.getBookingByOwnerIdWhereTime(ownerId, "CURRENT")); + + List listPast = bookingService.getBookingByOwnerIdWhereTime(ownerId, "PAST"); + assertThat(listPast.size(), equalTo(1)); + assertThat(listPast.getFirst().getItem().getOwner().getId(), equalTo(ownerId)); + assertThat(listPast.getFirst().getBooker(), notNullValue()); + + assertThrows(ConflictException.class, () -> + bookingService.getBookingByOwnerIdWhereTime(ownerId, "FUTURE")); + + List listFWaiting = bookingService.getBookingByOwnerIdWhereTime(ownerId, "WAITING"); + assertThat(listFWaiting.size(), equalTo(1)); + assertThat(listFWaiting.getFirst().getItem().getOwner().getId(), equalTo(ownerId)); + assertThat(listFWaiting.getFirst().getBooker(), notNullValue()); + + assertThrows(ConflictException.class, () -> + bookingService.getBookingByOwnerIdWhereTime(ownerId, "REJECTED")); + } +} \ No newline at end of file diff --git a/server/src/test/java/ru/practicum/shareit/item/ItemControllerTest.java b/server/src/test/java/ru/practicum/shareit/item/ItemControllerTest.java new file mode 100644 index 0000000..f0aa278 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/item/ItemControllerTest.java @@ -0,0 +1,158 @@ +package ru.practicum.shareit.item; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import ru.practicum.shareit.item.dto.*; +import ru.practicum.shareit.item.service.ItemService; +import ru.practicum.shareit.user.dto.UserDto; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(ItemController.class) +@AutoConfigureMockMvc +class ItemControllerTest { + @MockBean + private ItemService itemService; + + @Autowired + private MockMvc mockMvc; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void createComment() throws Exception { + CommentCreateDto createDto = CommentCreateDto.builder().text("Nice item").build(); + CommentDtoReturn responseDto = CommentDtoReturn.builder() + .id(1L) + .text("Nice item") + .authorName("John") + .created(LocalDateTime.now()) + .build(); + Mockito.when(itemService.createComment(eq(1L), eq(2L), any(CommentCreateDto.class))).thenReturn(responseDto); + + mockMvc.perform(MockMvcRequestBuilders.post("/items/2/comment") + .contentType(MediaType.APPLICATION_JSON) + .header("X-Sharer-User-Id", 1L) + .content(objectMapper.writeValueAsString(createDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(1L)) + .andExpect(jsonPath("$.text").value("Nice item")); + + Mockito.verify(itemService, Mockito.times(1)).createComment(eq(1L), eq(2L), any(CommentCreateDto.class)); + } + + @Test + void createItem() throws Exception { + ItemCreateDto createDto = ItemCreateDto.builder().name("Drill").description("Powerful drill").available(true).build(); + ItemDto responseDto = ItemDto.builder() + .id(1L) + .name("Drill") + .description("Powerful drill") + .available(true) + .owner(UserDto.builder().id(1L).name("John").email("john@example.com").build()) + .build(); + Mockito.when(itemService.createItem(any(ItemCreateDto.class), eq(1L))).thenReturn(responseDto); + + mockMvc.perform(MockMvcRequestBuilders.post("/items") + .contentType(MediaType.APPLICATION_JSON) + .header("X-Sharer-User-Id", 1L) + .content(objectMapper.writeValueAsString(createDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(1L)) + .andExpect(jsonPath("$.name").value("Drill")); + + Mockito.verify(itemService, Mockito.times(1)).createItem(any(ItemCreateDto.class), eq(1L)); + } + + @Test + void updateItem() throws Exception { + ItemUpdateDto updateDto = ItemUpdateDto.builder().name("Updated Drill").description("Updated desc").available(false).build(); + ItemDto responseDto = ItemDto.builder() + .id(1L) + .name("Updated Drill") + .description("Updated desc") + .available(false) + .owner(UserDto.builder().id(1L).name("John").email("john@example.com").build()) + .build(); + Mockito.when(itemService.updateItem(any(ItemUpdateDto.class), eq(1L), eq(1L))).thenReturn(responseDto); + + mockMvc.perform(MockMvcRequestBuilders.patch("/items/1") + .contentType(MediaType.APPLICATION_JSON) + .header("X-Sharer-User-Id", 1L) + .content(objectMapper.writeValueAsString(updateDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(1L)) + .andExpect(jsonPath("$.name").value("Updated Drill")); + + Mockito.verify(itemService, Mockito.times(1)).updateItem(any(ItemUpdateDto.class), eq(1L), eq(1L)); + } + + @Test + void getByItemIdWithComment() throws Exception { + ItemCommentDto responseDto = ItemCommentDto.builder() + .id(1L) + .name("Drill") + .description("Powerful drill") + .available(true) + .owner(UserDto.builder().id(1L).name("John").email("john@example.com").build()) + .comments(Collections.emptyList()) + .build(); + Mockito.when(itemService.getByItemIdWithComment(1L, 1L)).thenReturn(responseDto); + + mockMvc.perform(MockMvcRequestBuilders.get("/items/1") + .header("X-Sharer-User-Id", 1L)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(1L)) + .andExpect(jsonPath("$.name").value("Drill")); + + Mockito.verify(itemService, Mockito.times(1)).getByItemIdWithComment(1L, 1L); + } + + @Test + void allItemByOwnerIdWithComment() throws Exception { + List items = List.of( + ItemCommentDto.builder().id(1L).name("Drill").description("Powerful drill").available(true).owner(null).comments(Collections.emptyList()).build() + ); + Mockito.when(itemService.allItemByOwnerIdWithComment(1L)).thenReturn(items); + + mockMvc.perform(MockMvcRequestBuilders.get("/items") + .header("X-Sharer-User-Id", 1L)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id").value(1L)) + .andExpect(jsonPath("$[0].name").value("Drill")); + + Mockito.verify(itemService, Mockito.times(1)).allItemByOwnerIdWithComment(1L); + } + + @Test + void getItemsByNameOrDescription() throws Exception { + List items = List.of( + ItemDto.builder().id(2L).name("Hammer").description("Heavy hammer").available(true).owner(null).build() + ); + Mockito.when(itemService.getItemsByNameOrDescription("hammer")).thenReturn(items); + + mockMvc.perform(MockMvcRequestBuilders.get("/items/search") + .header("X-Sharer-User-Id", 1L) + .param("text", "hammer")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id").value(2L)) + .andExpect(jsonPath("$[0].name").value("Hammer")); + + Mockito.verify(itemService, Mockito.times(1)).getItemsByNameOrDescription("hammer"); + } +} \ No newline at end of file diff --git a/server/src/test/java/ru/practicum/shareit/item/repository/CommentRepositoryTest.java b/server/src/test/java/ru/practicum/shareit/item/repository/CommentRepositoryTest.java new file mode 100644 index 0000000..3b1cfca --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/item/repository/CommentRepositoryTest.java @@ -0,0 +1,91 @@ +package ru.practicum.shareit.item.repository; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.jdbc.Sql; +import ru.practicum.shareit.item.dto.CommentDto; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +@DataJpaTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@Sql(scripts = {"/data/cleanup.sql", "/data/data.sql"}) +class CommentRepositoryTest { // Данные для теста класса подготовлены в файле data.sql + + @Autowired + private CommentRepository commentRepository; + + @Test + void findByItemId() { + CommentDto commentDto1 = CommentDto.builder() + .id(1L) + .text("Отличная дрель, спасибо!") + .created(LocalDateTime.parse("2025-06-13T11:20:00")) + .build(); + CommentDto commentDto2 = CommentDto.builder() + .id(5L) + .text("Дрель норм") + .created(LocalDateTime.parse("2025-07-13T10:15:00")) + .build(); + + List list = commentRepository.findByItemId(1L); // Отсортирован по дате создания: от нового к старому + CommentDto listComment1 = list.getFirst(); + CommentDto listComment2 = list.getLast(); + + assertThat(list.size(), equalTo(2)); + assertThat(listComment1.getId(), equalTo(commentDto2.getId())); + assertThat(listComment1.getText(), equalTo(commentDto2.getText())); + assertThat(listComment1.getCreated(), equalTo(commentDto2.getCreated())); + assertThat(listComment1.getItem(), notNullValue()); + assertThat(listComment1.getItem().getOwner(), notNullValue()); + assertThat(listComment1.getAuthor(), notNullValue()); + + assertThat(listComment2.getId(), equalTo(commentDto1.getId())); + assertThat(listComment2.getText(), equalTo(commentDto1.getText())); + assertThat(listComment2.getCreated(), equalTo(commentDto1.getCreated())); + assertThat(listComment2.getItem(), notNullValue()); + assertThat(listComment2.getItem().getOwner(), notNullValue()); + assertThat(listComment2.getAuthor(), notNullValue()); + } + + @Test + void findByItemIds() { + CommentDto commentDto1 = CommentDto.builder() + .id(3L) + .text("Палатка с небольшим дефектом") + .created(LocalDateTime.parse("2025-07-11T10:15:00")) + .build(); + CommentDto commentDto2 = CommentDto.builder() + .id(4L) + .text("Рогатка как рогатка") + .created(LocalDateTime.parse("2025-07-12T10:15:00")) + .build(); + + List list = commentRepository.findByItemIds(List.of(3L, 4L)); // Отсортирован по дате создания: от нового к старому + CommentDto listComment1 = list.getFirst(); + CommentDto listComment2 = list.getLast(); + + assertThat(list.size(), equalTo(2)); + assertThat(listComment1.getId(), equalTo(commentDto2.getId())); + assertThat(listComment1.getText(), equalTo(commentDto2.getText())); + assertThat(listComment1.getCreated(), equalTo(commentDto2.getCreated())); + assertThat(listComment1.getItem(), notNullValue()); + assertThat(listComment1.getItem().getOwner(), notNullValue()); + assertThat(listComment1.getAuthor(), notNullValue()); + + assertThat(listComment2.getId(), equalTo(commentDto1.getId())); + assertThat(listComment2.getText(), equalTo(commentDto1.getText())); + assertThat(listComment2.getCreated(), equalTo(commentDto1.getCreated())); + assertThat(listComment2.getItem(), notNullValue()); + assertThat(listComment2.getItem().getOwner(), notNullValue()); + assertThat(listComment2.getAuthor(), notNullValue()); + } + +} \ No newline at end of file diff --git a/server/src/test/java/ru/practicum/shareit/item/repository/ItemRepositoryTest.java b/server/src/test/java/ru/practicum/shareit/item/repository/ItemRepositoryTest.java new file mode 100644 index 0000000..70efe90 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/item/repository/ItemRepositoryTest.java @@ -0,0 +1,95 @@ +package ru.practicum.shareit.item.repository; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.jdbc.Sql; +import ru.practicum.shareit.item.dto.ItemCommentDto; +import ru.practicum.shareit.item.model.Item; + +import java.util.List; + +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + + +@DataJpaTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@Sql(scripts = {"/data/cleanup.sql", "/data/data.sql"}) +class ItemRepositoryTest { // Данные для теста класса подготовлены в файле data.sql + @Autowired + private ItemRepository itemRepository; + + @Test + void findItemWithBookings() { + ItemCommentDto itemCommentDto = ItemCommentDto.builder() + .id(2L) + .name("Лопата") + .description("Совковая лопата") + .available(true) + .build(); + + ItemCommentDto query = itemRepository.findItemWithBookings(itemCommentDto.getId()).get(); + + assertThat(query.getId(), equalTo(itemCommentDto.getId())); + assertThat(query.getName(), equalTo(itemCommentDto.getName())); + assertThat(query.getDescription(), equalTo(itemCommentDto.getDescription())); + assertThat(query.getAvailable(), equalTo(itemCommentDto.getAvailable())); + assertThat(query.getOwner(), notNullValue()); + } + + @Test + void allItemByOwnerIdWithComment() { + ItemCommentDto itemCommentDto1 = ItemCommentDto.builder() + .id(2L) + .name("Лопата") + .description("Совковая лопата") + .available(true) + .build(); + + ItemCommentDto itemCommentDto2 = ItemCommentDto.builder() + .id(4L) + .name("Рогатка") + .description("Деревянная рогатка") + .available(true) + .build(); + + List list = itemRepository.allItemByOwnerIdWithComment(2L); // Сортировка по Id + ItemCommentDto dtoTest1 = list.getFirst(); + ItemCommentDto dtoTest2 = list.getLast(); + + assertThat(list.size(), equalTo(2)); + assertThat(dtoTest1.getId(), equalTo(itemCommentDto1.getId())); + assertThat(dtoTest1.getName(), equalTo(itemCommentDto1.getName())); + assertThat(dtoTest1.getDescription(), equalTo(itemCommentDto1.getDescription())); + assertThat(dtoTest1.getAvailable(), equalTo(itemCommentDto1.getAvailable())); + assertThat(dtoTest1.getOwner(), notNullValue()); + + assertThat(dtoTest2.getId(), equalTo(itemCommentDto2.getId())); + assertThat(dtoTest2.getName(), equalTo(itemCommentDto2.getName())); + assertThat(dtoTest2.getDescription(), equalTo(itemCommentDto2.getDescription())); + assertThat(dtoTest2.getAvailable(), equalTo(itemCommentDto2.getAvailable())); + assertThat(dtoTest2.getOwner(), notNullValue()); + } + + @Test + void getItemsByNameOrDescription() { + Item model = Item.builder() + .name("Дрель") + .description("Аккумуляторная дрель") + .available(true) + .build(); + + List list = itemRepository.getItemsByNameOrDescription("дрель"); + Item itemQuery = list.getFirst(); + + assertThat(list.size(), equalTo(1)); + assertThat(itemQuery.getName(), equalTo(model.getName())); + assertThat(itemQuery.getDescription(), equalTo(model.getDescription())); + assertThat(itemQuery.getAvailable(), equalTo(model.getAvailable())); + assertThat(itemQuery.getOwner(), notNullValue()); + assertThat(itemQuery.getRequest(), notNullValue()); + } +} \ No newline at end of file diff --git a/server/src/test/java/ru/practicum/shareit/item/service/ItemServiceTest.java b/server/src/test/java/ru/practicum/shareit/item/service/ItemServiceTest.java new file mode 100644 index 0000000..e251eed --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/item/service/ItemServiceTest.java @@ -0,0 +1,106 @@ +package ru.practicum.shareit.item.service; + +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 org.springframework.test.context.jdbc.Sql; +import ru.practicum.shareit.exception.BadRequestException; +import ru.practicum.shareit.exception.ConflictException; +import ru.practicum.shareit.item.dto.*; +import ru.practicum.shareit.user.dto.UserCreateDto; +import ru.practicum.shareit.user.dto.UserDto; +import ru.practicum.shareit.user.service.UserService; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@Transactional +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@SpringBootTest +@Sql(scripts = "/data/data_repository.sql") +class ItemServiceTest { + private final ItemService itemService; + private final UserService userService; + + ItemCreateDto itemCreateDto = ItemCreateDto.builder() + .name("Ящик") + .description("Ящик деревянный") + .available(true) + .build(); + UserCreateDto userCreateDto = UserCreateDto.builder() + .name("Samson") + .email("user@mail.ru") + .build(); + + + @Test + void createComment() { + CommentCreateDto createDto = new CommentCreateDto(); + createDto.setText("Супер!"); + + CommentDtoReturn commentDtoReturn = itemService.createComment(2, 1, createDto); + + assertThat(commentDtoReturn.getText(), equalTo(createDto.getText())); + assertThrows(BadRequestException.class, () -> itemService.createComment(99, 99, createDto)); + } + + @Test + void getByItemIdWithComment() { + ItemCommentDto itemCommentDto = itemService.getByItemIdWithComment(1, 1); + assertThat(itemCommentDto.getId(), equalTo(1L)); + assertThat(itemCommentDto.getOwner(), notNullValue()); + assertThat(itemCommentDto.getComments().size(), equalTo(1)); + } + + @Test + void allItemByOwnerIdWithComment() { + List itemCommentDto = itemService.allItemByOwnerIdWithComment(1L); + assertThat(itemCommentDto.size(), equalTo(1)); + assertThat(itemCommentDto.getFirst().getOwner().getId(), equalTo(1L)); + assertThat(itemCommentDto.getFirst().getComments().size(), equalTo(1)); + } + + @Test + void createItem() { + UserDto userCreatedDto = userService.create(userCreateDto); + ItemDto itemCreatedDto = itemService.createItem(itemCreateDto, userCreatedDto.getId()); + + assertThat(itemCreatedDto.getName(), equalTo(itemCreateDto.getName())); + assertThat(itemCreatedDto.getDescription(), equalTo(itemCreateDto.getDescription())); + assertThat(itemCreatedDto.getAvailable(), equalTo(itemCreateDto.getAvailable())); + assertThat(itemCreatedDto.getOwner(), equalTo(userCreatedDto)); + } + + @Test + void updateItem() { + ItemUpdateDto updateDto = ItemUpdateDto.builder() + .name("Велосипед") + .description("Детский велосипед") + .available(false) + .build(); + + + UserDto userCreatedDto = userService.create(userCreateDto); + ItemDto itemCreatedDto = itemService.createItem(itemCreateDto, userCreatedDto.getId()); + ItemDto itemUpdatedDto = itemService.updateItem(updateDto, userCreatedDto.getId(), itemCreatedDto.getId()); + + assertThrows(ConflictException.class, () -> itemService.updateItem(updateDto, 1, 3)); + assertThat(updateDto.getName(), equalTo(itemUpdatedDto.getName())); + assertThat(updateDto.getDescription(), equalTo(itemUpdatedDto.getDescription())); + assertThat(updateDto.getAvailable(), equalTo(itemUpdatedDto.getAvailable())); + assertThat(itemUpdatedDto.getOwner(), equalTo(userCreatedDto)); + } + + @Test + void getItemsByNameOrDescription() { + List items = itemService.getItemsByNameOrDescription("Лопата"); + assertThat(items.size(), equalTo(1)); + assertThat(items.getFirst().getName(), equalTo("Лопата")); + } +} \ No newline at end of file diff --git a/server/src/test/java/ru/practicum/shareit/request/ItemRequestControllerTest.java b/server/src/test/java/ru/practicum/shareit/request/ItemRequestControllerTest.java new file mode 100644 index 0000000..0fddca9 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/request/ItemRequestControllerTest.java @@ -0,0 +1,115 @@ +package ru.practicum.shareit.request; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; +import ru.practicum.shareit.request.dto.ItemRequestDto; +import ru.practicum.shareit.request.service.ItemRequestService; +import ru.practicum.shareit.user.dto.UserDto; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(ItemRequestController.class) +@AutoConfigureMockMvc +class ItemRequestControllerTest { + @MockBean + private ItemRequestService itemRequestService; + + @Autowired + private MockMvc mockMvc; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void createItemRequest() throws Exception { + ItemRequestCreateDto createDto = ItemRequestCreateDto.builder().description("Need a drill").build(); + ItemRequestDto responseDto = ItemRequestDto.builder() + .id(1L) + .description("Need a drill") + .requestor(UserDto.builder().id(1L).name("John").email("john@example.com").build()) + .created(LocalDateTime.now()) + .items(Collections.emptyList()) + .build(); + Mockito.when(itemRequestService.createItemRequest(eq(1L), any(ItemRequestCreateDto.class))).thenReturn(responseDto); + + mockMvc.perform(MockMvcRequestBuilders.post("/requests") + .contentType(MediaType.APPLICATION_JSON) + .header("X-Sharer-User-Id", 1L) + .content(objectMapper.writeValueAsString(createDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(1L)) + .andExpect(jsonPath("$.description").value("Need a drill")); + + Mockito.verify(itemRequestService, Mockito.times(1)).createItemRequest(eq(1L), any(ItemRequestCreateDto.class)); + } + + @Test + void getItemRequestsByUserId() throws Exception { + List requests = List.of( + ItemRequestDto.builder().id(1L).description("Need a drill").requestor(null).created(LocalDateTime.now()).items(Collections.emptyList()).build() + ); + Mockito.when(itemRequestService.getItemRequestsByUserId(1L)).thenReturn(requests); + + mockMvc.perform(MockMvcRequestBuilders.get("/requests") + .header("X-Sharer-User-Id", 1L)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id").value(1L)) + .andExpect(jsonPath("$[0].description").value("Need a drill")); + + Mockito.verify(itemRequestService, Mockito.times(1)).getItemRequestsByUserId(1L); + } + + @Test + void getItemRequestsByNotUserId() throws Exception { + List requests = List.of( + ItemRequestDto.builder().id(2L).description("Need a hammer").requestor(null).created(LocalDateTime.now()).items(Collections.emptyList()).build() + ); + Mockito.when(itemRequestService.findAllByNotRequestorIdSorted(1L)).thenReturn(requests); + + mockMvc.perform(MockMvcRequestBuilders.get("/requests/all") + .header("X-Sharer-User-Id", 1L)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id").value(2L)) + .andExpect(jsonPath("$[0].description").value("Need a hammer")); + + Mockito.verify(itemRequestService, Mockito.times(1)).findAllByNotRequestorIdSorted(1L); + } + + @Test + void getItemRequestById() throws Exception { + ItemRequestDto responseDto = ItemRequestDto.builder() + .id(3L) + .description("Need a saw") + .requestor(null) + .created(LocalDateTime.now()) + .items(List.of(ItemDto.builder().id(1L).name("Saw").description("Sharp saw").available(true).build())) + .build(); + Mockito.when(itemRequestService.getItemRequestById(3L)).thenReturn(responseDto); + + mockMvc.perform(MockMvcRequestBuilders.get("/requests/3") + .header("X-Sharer-User-Id", 1L)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(3L)) + .andExpect(jsonPath("$.description").value("Need a saw")) + .andExpect(jsonPath("$.items[0].id").value(1L)) + .andExpect(jsonPath("$.items[0].name").value("Saw")); + + Mockito.verify(itemRequestService, Mockito.times(1)).getItemRequestById(3L); + } +} \ No newline at end of file diff --git a/server/src/test/java/ru/practicum/shareit/request/ItemRequestRepositoryTest.java b/server/src/test/java/ru/practicum/shareit/request/ItemRequestRepositoryTest.java new file mode 100644 index 0000000..152ace2 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/request/ItemRequestRepositoryTest.java @@ -0,0 +1,42 @@ +package ru.practicum.shareit.request; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase; +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; +import org.springframework.test.context.jdbc.Sql; + +import java.util.List; + +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.core.IsNot.not; + +@DataJpaTest +@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) +@Sql(scripts = {"/data/cleanup.sql", "/data/data.sql"}) +class ItemRequestRepositoryTest { // Данные для теста класса подготовлены в файле data.sql + @Autowired + private ItemRequestRepository repository; + + @Test + void findAllByRequestorIdSorted() { + Long requestorId = 2L; + List list = repository.findAllByRequestorIdSorted(requestorId); + assertThat(list.size(), equalTo(1)); + assertThat(list.getFirst().getRequestor().getId(), equalTo(requestorId)); + assertThat(list.getFirst().getItems().getFirst(), notNullValue()); + } + + @Test + void findAllByNotRequestorIdSorted() { + Long requestorId = 2L; + List list = repository.findAllByNotRequestorIdSorted(requestorId); + assertThat(list.size(), equalTo(2)); + assertThat(list.getFirst().getRequestor().getId(), not(equalTo(requestorId))); + assertThat(list.getFirst().getItems().getFirst(), notNullValue()); + assertThat(list.getLast().getRequestor().getId(), not(equalTo(requestorId))); + assertThat(list.getLast().getItems().getFirst(), notNullValue()); + } +} \ No newline at end of file diff --git a/server/src/test/java/ru/practicum/shareit/request/service/ItemRequestServiceTest.java b/server/src/test/java/ru/practicum/shareit/request/service/ItemRequestServiceTest.java new file mode 100644 index 0000000..8166bbe --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/request/service/ItemRequestServiceTest.java @@ -0,0 +1,101 @@ +package ru.practicum.shareit.request.service; + +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 org.springframework.test.context.jdbc.Sql; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.request.dto.ItemRequestCreateDto; +import ru.practicum.shareit.request.dto.ItemRequestDto; +import ru.practicum.shareit.user.dto.UserCreateDto; +import ru.practicum.shareit.user.dto.UserDto; +import ru.practicum.shareit.user.service.UserService; + +import java.util.List; + +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.core.IsNot.not; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@Transactional +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@SpringBootTest +@Sql(scripts = "/data/data_repository.sql") +class ItemRequestServiceTest { + private final ItemRequestService itemRequestService; + private final UserService service; + + ItemRequestCreateDto createDto = ItemRequestCreateDto.builder() + .description("Нужен ноутбук") + .build(); + + @Test + void createItemRequest() { + ItemRequestDto createdRequestDto = itemRequestService.createItemRequest(1, createDto); + assertThat(createDto.getDescription(), equalTo(createdRequestDto.getDescription())); + assertThat(createdRequestDto.getRequestor(), notNullValue()); + assertThat(createdRequestDto.getItems().size(), equalTo(0)); + assertThrows(NotFoundException.class, () -> + itemRequestService.createItemRequest(999, createDto)); + } + + @Test + void getItemRequestsByUserId() { + long requestorId = 1L; + ItemRequestCreateDto createDto = ItemRequestCreateDto.builder() + .description("Нужен ноутбук") + .build(); + UserCreateDto usercreateDto = UserCreateDto.builder() + .name("Samson") + .email("user@mail.ru") + .build(); + + UserDto userDtoCreated = service.create(usercreateDto); + ItemRequestDto createdRequestDto = itemRequestService.createItemRequest(2, createDto); + List list = itemRequestService.getItemRequestsByUserId(requestorId); + + assertThat(list.size(), equalTo(1)); + assertThat(list.getFirst().getRequestor().getId(), equalTo(requestorId)); + assertThat(list.getFirst().getItems().getFirst(), notNullValue()); + assertThrows(NotFoundException.class, () -> + itemRequestService.getItemRequestsByUserId(999)); + assertThrows(NotFoundException.class, () -> + itemRequestService.getItemRequestsByUserId(createdRequestDto.getId())); + assertThrows(NotFoundException.class, () -> + itemRequestService.getItemRequestsByUserId(userDtoCreated.getId())); + } + + @Test + void findAllByNotRequestorIdSorted() { + long requestorId = 1L; + List list = itemRequestService.findAllByNotRequestorIdSorted(requestorId); + + assertThat(list.size(), equalTo(4)); + assertThrows(NotFoundException.class, () -> + itemRequestService.findAllByNotRequestorIdSorted(999)); + for (ItemRequestDto dto : list) { + assertThat(dto.getRequestor().getId(), not(equalTo(requestorId))); + assertThat(dto.getItems().getFirst(), notNullValue()); + } + + ItemRequestDto createdRequestDto = itemRequestService.createItemRequest(2, createDto); + assertThrows(NotFoundException.class, () -> + itemRequestService.findAllByNotRequestorIdSorted(createdRequestDto.getId())); + } + + @Test + void getItemRequestById() { + ItemRequestDto createdRequestDto = itemRequestService.createItemRequest(2, createDto); + ItemRequestDto findRequestDto = itemRequestService.getItemRequestById(createdRequestDto.getId()); + + assertThat(createDto.getDescription(), equalTo(findRequestDto.getDescription())); + assertThat(createdRequestDto.getRequestor(), equalTo(findRequestDto.getRequestor())); + assertThat(createdRequestDto.getItems().size(), equalTo(findRequestDto.getItems().size())); + assertThrows(NotFoundException.class, () -> + itemRequestService.getItemRequestById(999)); + } +} \ No newline at end of file diff --git a/server/src/test/java/ru/practicum/shareit/user/UserControllerTest.java b/server/src/test/java/ru/practicum/shareit/user/UserControllerTest.java new file mode 100644 index 0000000..7a000a4 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/user/UserControllerTest.java @@ -0,0 +1,112 @@ +package ru.practicum.shareit.user; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; +import ru.practicum.shareit.user.dto.UserCreateDto; +import ru.practicum.shareit.user.dto.UserDto; +import ru.practicum.shareit.user.dto.UserUpdateDto; +import ru.practicum.shareit.user.service.UserService; + +import java.util.List; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(UserController.class) +@AutoConfigureMockMvc +class UserControllerTest { + @MockBean + private UserService service; + + @Autowired + private MockMvc mockMvc; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + @Test + void create() throws Exception { + UserCreateDto createDto = UserCreateDto.builder().name("John").email("john@example.com").build(); + UserDto responseDto = UserDto.builder().id(1L).name("John").email("john@example.com").build(); + Mockito.when(service.create(any(UserCreateDto.class))).thenReturn(responseDto); + + mockMvc.perform(MockMvcRequestBuilders.post("/users") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(createDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(1L)) + .andExpect(jsonPath("$.name").value("John")) + .andExpect(jsonPath("$.email").value("john@example.com")); + + Mockito.verify(service, Mockito.times(1)).create(any(UserCreateDto.class)); + } + + @Test + void update() throws Exception { + UserUpdateDto updateDto = UserUpdateDto.builder().name("Jane").email("jane@example.com").build(); + UserDto responseDto = UserDto.builder().id(1L).name("Jane").email("jane@example.com").build(); + Mockito.when(service.update(eq(1L), any(UserUpdateDto.class))).thenReturn(responseDto); + + mockMvc.perform(MockMvcRequestBuilders.patch("/users/1") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(updateDto))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(1L)) + .andExpect(jsonPath("$.name").value("Jane")) + .andExpect(jsonPath("$.email").value("jane@example.com")); + + Mockito.verify(service, Mockito.times(1)).update(eq(1L), any(UserUpdateDto.class)); + } + + @Test + void getUserById() throws Exception { + UserDto responseDto = UserDto.builder().id(1L).name("John").email("john@example.com").build(); + Mockito.when(service.getUserById(1L)).thenReturn(responseDto); + + mockMvc.perform(MockMvcRequestBuilders.get("/users/1")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value(1L)) + .andExpect(jsonPath("$.name").value("John")) + .andExpect(jsonPath("$.email").value("john@example.com")); + + Mockito.verify(service, Mockito.times(1)).getUserById(1L); + } + + @Test + void getAll() throws Exception { + List users = List.of( + UserDto.builder().id(1L).name("John").email("john@example.com").build(), + UserDto.builder().id(2L).name("Jane").email("jane@example.com").build() + ); + Mockito.when(service.getAll()).thenReturn(users); + + mockMvc.perform(MockMvcRequestBuilders.get("/users")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id").value(1L)) + .andExpect(jsonPath("$[0].name").value("John")) + .andExpect(jsonPath("$[0].email").value("john@example.com")) + .andExpect(jsonPath("$[1].id").value(2L)) + .andExpect(jsonPath("$[1].name").value("Jane")) + .andExpect(jsonPath("$[1].email").value("jane@example.com")); + + Mockito.verify(service, Mockito.times(1)).getAll(); + } + + @Test + void deleteUser() throws Exception { + Mockito.doNothing().when(service).deleteUser(1L); + mockMvc.perform(MockMvcRequestBuilders.delete("/users/1")) + .andExpect(status().isOk()); + + Mockito.verify(service, Mockito.times(1)).deleteUser(1L); + } +} \ No newline at end of file diff --git a/server/src/test/java/ru/practicum/shareit/user/service/UserServiceTest.java b/server/src/test/java/ru/practicum/shareit/user/service/UserServiceTest.java new file mode 100644 index 0000000..d37b934 --- /dev/null +++ b/server/src/test/java/ru/practicum/shareit/user/service/UserServiceTest.java @@ -0,0 +1,91 @@ +package ru.practicum.shareit.user.service; + +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.jdbc.core.JdbcTemplate; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.user.dto.UserCreateDto; +import ru.practicum.shareit.user.dto.UserDto; +import ru.practicum.shareit.user.dto.UserUpdateDto; + +import java.util.List; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertThrows; + +@Transactional +@RequiredArgsConstructor(onConstructor_ = @Autowired) +@SpringBootTest +class UserServiceTest { + private final UserService service; + private final JdbcTemplate jdbcTemplate; + + UserCreateDto createDto1 = UserCreateDto.builder() + .name("Samson") + .email("user@mail.ru") + .build(); + + @BeforeEach + void setUp() { + jdbcTemplate.update("DELETE FROM comments"); + jdbcTemplate.update("DELETE FROM booking"); + jdbcTemplate.update("DELETE FROM items"); + jdbcTemplate.update("DELETE FROM requests"); + jdbcTemplate.update("DELETE FROM users"); + jdbcTemplate.update("ALTER TABLE users ALTER COLUMN id RESTART WITH 1"); + } + + @Test + void create() { + UserDto dtoCreated = service.create(createDto1); + UserDto dtoGet = service.getUserById(dtoCreated.getId()); + + assertThat(dtoCreated, equalTo(dtoGet)); + } + + @Test + void update() { + UserUpdateDto updateDto = UserUpdateDto.builder() + .name("Ivan") + .email("ivan@mail.ru") + .build(); + + UserDto dtoCreated = service.create(createDto1); + UserDto dtoUpdated = service.update(dtoCreated.getId(), updateDto); + + assertThat(dtoUpdated.getId(), equalTo(dtoCreated.getId())); + assertThat(dtoUpdated.getName(), equalTo(updateDto.getName())); + assertThat(dtoUpdated.getEmail(), equalTo(updateDto.getEmail())); + } + + @Test + void getUserById() { + UserDto dtoCreated = service.create(createDto1); + UserDto dtoGet = service.getUserById(dtoCreated.getId()); + + assertThat(dtoCreated, equalTo(dtoGet)); + } + + @Test + void getAll() { + UserDto dtoCreated = service.create(createDto1); + List list = service.getAll(); + + assertThat(list.size(), equalTo(1)); + + assertThat(list.getFirst(), equalTo(dtoCreated)); + } + + @Test + void deleteUser() { + UserDto dtoCreated = service.create(createDto1); + service.deleteUser(dtoCreated.getId()); + + assertThrows(NotFoundException.class, () -> service.getUserById(dtoCreated.getId())); + } +} \ No newline at end of file diff --git a/src/test/resources/application.properties b/server/src/test/resources/application.properties similarity index 70% rename from src/test/resources/application.properties rename to server/src/test/resources/application.properties index 8ae339c..9586ed7 100644 --- a/src/test/resources/application.properties +++ b/server/src/test/resources/application.properties @@ -1,3 +1,7 @@ +spring.output.ansi.enabled=ALWAYS + +spring.jpa.defer-datasource-initialization=true + spring.config.activate.on-profile=test spring.datasource.driverClassName=org.h2.Driver spring.datasource.url=jdbc:h2:mem:shareit diff --git a/server/src/test/resources/data/cleanup.sql b/server/src/test/resources/data/cleanup.sql new file mode 100644 index 0000000..f14b336 --- /dev/null +++ b/server/src/test/resources/data/cleanup.sql @@ -0,0 +1,5 @@ +DELETE FROM comments; +DELETE FROM booking; +DELETE FROM items; +DELETE FROM requests; +DELETE FROM users; \ No newline at end of file diff --git a/server/src/test/resources/data/data.sql b/server/src/test/resources/data/data.sql new file mode 100644 index 0000000..0f8d6df --- /dev/null +++ b/server/src/test/resources/data/data.sql @@ -0,0 +1,35 @@ + +-- Пользователи +INSERT INTO users (id, name, email) VALUES +(1, 'Иван Петров', 'ivan.petrov@example.com'), +(2, 'Мария Сидорова', 'maria.sidorova@mail.ru'), +(3, 'Алексей Иванов', 'alex.ivanov@gmail.com'); + +-- Запросы +INSERT INTO requests (id, description, requestor_id, created) VALUES +(1, 'Нужна дрель для ремонта', 2, '2025-06-01 10:00:00'), +(2, 'Ищу лопату для дачи', 1, '2025-06-02 15:30:00'), +(3, 'Требуется палатка для похода', 3, '2025-06-03 09:15:00'); + +-- Вещи +INSERT INTO items (id, name, description, available, owner_id, request_id) VALUES +(1, 'Дрель', 'Аккумуляторная дрель', true, 1, 1), +(2, 'Лопата', 'Совковая лопата', true, 2, 2), +(3, 'Палатка', '4-местная палатка', false, 3, 3), +(4, 'Рогатка', 'Деревянная рогатка', true, 2, null); + +-- Бронирования +INSERT INTO booking (id, start_time, end_time, item_id, booker_id, status) VALUES +(1, '2025-06-10 09:00:00', '2025-06-12 18:00:00', 1, 2, 'APPROVED'), +(2, '2025-06-15 10:00:00', '2025-06-20 20:00:00', 2, 3, 'WAITING'), +(3, '2025-07-01 08:00:00', '2025-07-10 22:00:00', 3, 1, 'REJECTED'), +(4, '2025-05-01 08:00:00', '2025-05-10 22:00:00', 4, 1, 'APPROVED'), +(5, '2025-04-01 08:00:00', '2025-04-10 22:00:00', 1, 2, 'APPROVED'); + +-- Комментарии +INSERT INTO comments (id, text, item_id, author_id, created) VALUES +(1, 'Отличная дрель, спасибо!', 1, 2, '2025-06-13 11:20:00'), +(2, 'Лопата немного старая, но работает', 2, 3, '2025-06-21 14:30:00'), +(3, 'Палатка с небольшим дефектом', 3, 1, '2025-07-11 10:15:00'), +(4, 'Рогатка как рогатка', 4, 1, '2025-07-12 10:15:00'), +(5, 'Дрель норм', 1, 2, '2025-07-13 10:15:00'); \ No newline at end of file diff --git a/server/src/test/resources/data/data_repository.sql b/server/src/test/resources/data/data_repository.sql new file mode 100644 index 0000000..9aa2ec8 --- /dev/null +++ b/server/src/test/resources/data/data_repository.sql @@ -0,0 +1,54 @@ + +-- Очистка таблиц +DELETE FROM comments; +DELETE FROM booking; +DELETE FROM items; +DELETE FROM requests; +DELETE FROM users; + +-- Сброс последовательностей ID +ALTER TABLE users ALTER COLUMN id RESTART WITH 1; +ALTER TABLE requests ALTER COLUMN id RESTART WITH 1; +ALTER TABLE items ALTER COLUMN id RESTART WITH 1; +ALTER TABLE booking ALTER COLUMN id RESTART WITH 1; +ALTER TABLE comments ALTER COLUMN id RESTART WITH 1; + +-- Пользователи +INSERT INTO users (name, email) VALUES +('Иван Петров', 'ivan.petrov@example.com'), +('Мария Сидорова', 'maria.sidorova@mail.ru'), +('Алексей Иванов', 'alex.ivanov@gmail.com'), +('Елена Козлова', 'elena.koz@yandex.ru'), +('Дмитрий Смирнов', 'dmitry.smirn@mail.com'); + +-- Запросы +INSERT INTO requests (description, requestor_id, created) VALUES +('Нужна дрель для ремонта', 2, '2025-06-01 10:00:00'), +('Ищу лопату для дачи', 1, '2025-06-02 15:30:00'), +('Требуется палатка для похода', 3, '2025-06-03 09:15:00'), +('Нужен генератор на выходные', 4, '2025-06-04 12:00:00'), +('Ищу бензопилу', 5, '2025-06-05 11:20:00'); + +-- Вещи +INSERT INTO items (name, description, available, owner_id, request_id) VALUES +('Дрель', 'Аккумуляторная дрель + 2 батареи', true, 1, 1), +('Лопата', 'Совковая лопата с деревянной ручкой', true, 2, 2), +('Палатка', '4-местная палатка с москитной сеткой', false, 3, 3), +('Генератор', 'Бензиновый генератор 3.5 кВт', true, 4, 4), +('Бензопила', 'Мощная бензопила 45см', true, 5, 5); + +-- Бронирования +INSERT INTO booking (start_time, end_time, item_id, booker_id, status) VALUES +('2025-06-10 09:00:00', '2025-06-12 18:00:00', 1, 2, 'APPROVED'), +('2025-06-15 10:00:00', '2025-06-20 20:00:00', 2, 3, 'WAITING'), +('2025-07-01 08:00:00', '2025-07-10 22:00:00', 3, 1, 'REJECTED'), +('2025-05-01 08:00:00', '2025-05-10 22:00:00', 4, 1, 'APPROVED'), +('2025-04-01 08:00:00', '2025-04-10 22:00:00', 5, 2, 'CANCELED'); + +-- Комментарии +INSERT INTO comments (text, item_id, author_id, created) VALUES +('Отличная дрель, спасибо! Работает без нареканий', 1, 2, '2025-06-13 11:20:00'), +('Лопата немного старая, но полностью функциональна', 2, 3, '2025-06-21 14:30:00'), +('Палатка с небольшим дефектом - нет одной стойки', 3, 1, '2025-07-11 10:15:00'), +('Генератор мощный, но шумный', 4, 4, '2025-05-15 09:10:00'), +('Бензопила как новая, отлично пилит', 5, 5, '2025-04-15 16:45:00'); \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingUpdateDto.java b/src/main/java/ru/practicum/shareit/booking/dto/BookingUpdateDto.java deleted file mode 100644 index 0673edf..0000000 --- a/src/main/java/ru/practicum/shareit/booking/dto/BookingUpdateDto.java +++ /dev/null @@ -1,26 +0,0 @@ -package ru.practicum.shareit.booking.dto; - -import jakarta.validation.constraints.AssertTrue; -import jakarta.validation.constraints.Null; -import lombok.Data; -import ru.practicum.shareit.booking.BookingStatus; - -import java.time.LocalDateTime; - -@Data -public class BookingUpdateDto { - @Null - private Long id; - private LocalDateTime start; - private LocalDateTime end; - private Long itemId; - private Long booker; - private BookingStatus bookingStatus; - - @AssertTrue(message = "Дата окончания должна быть позже даты начала") - boolean isStartBeforeEnd() { - return start != null && - end != null && - start.isBefore(end); - } -} diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequest.java b/src/main/java/ru/practicum/shareit/request/ItemRequest.java deleted file mode 100644 index 1bd5ea1..0000000 --- a/src/main/java/ru/practicum/shareit/request/ItemRequest.java +++ /dev/null @@ -1,33 +0,0 @@ -package ru.practicum.shareit.request; - - -import jakarta.persistence.*; -import lombok.*; - -import java.time.LocalDateTime; - - -@Builder -@Getter -@Setter -@ToString -@Entity -@Table(name = "requests") -public class ItemRequest { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String description; - - @Column(name = "requestor", nullable = false) - private Long requestor; - - @Column(name = "time_request", nullable = false) - private LocalDateTime dateTimeRequest; - - @Override - public int hashCode() { - return getClass().hashCode(); - } -} 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 a30555e..0000000 --- a/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java +++ /dev/null @@ -1,14 +0,0 @@ -package ru.practicum.shareit.request.dto; - - -import lombok.Data; - -import java.time.LocalDateTime; - -@Data -public class ItemRequestDto { - private Long id; - private String description; - private Long requestor; - private LocalDateTime dateTimeRequest; -} diff --git a/src/main/java/ru/practicum/shareit/user/service/UserService.java b/src/main/java/ru/practicum/shareit/user/service/UserService.java deleted file mode 100644 index fbae19e..0000000 --- a/src/main/java/ru/practicum/shareit/user/service/UserService.java +++ /dev/null @@ -1,17 +0,0 @@ -package ru.practicum.shareit.user.service; - -import ru.practicum.shareit.user.User; - -import java.util.Collection; - -public interface UserService { - User create(User user); - - User update(User user); - - User getUserById(long id); - - Collection getAll(); - - void deleteUser(long userId); -} diff --git a/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java b/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java deleted file mode 100644 index 257e7f2..0000000 --- a/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java +++ /dev/null @@ -1,53 +0,0 @@ -package ru.practicum.shareit.user.service; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import ru.practicum.shareit.exception.NotFoundException; -import ru.practicum.shareit.user.User; -import ru.practicum.shareit.user.UserRepository; - -import java.util.Collection; - -@Service -@RequiredArgsConstructor -public class UserServiceImpl implements UserService { - private final UserRepository userRepository; - - @Override - public User create(User user) { - return userRepository.save(user); - } - - @Override - public User update(User newUser) { - User user = checkAndReturnUser(newUser.getId()); - - if (newUser.getName() != null) { - user.setName(newUser.getName()); - } - if (newUser.getEmail() != null) { - user.setEmail(newUser.getEmail()); - } - - return userRepository.save(user); - } - - @Override - public User getUserById(long id) { - return checkAndReturnUser(id); - } - - @Override - public Collection getAll() { - return userRepository.findAll(); - } - - @Override - public void deleteUser(long userId) { - userRepository.deleteById(userId); - } - - private User checkAndReturnUser(long id) { - return userRepository.findById(id).orElseThrow(() -> new NotFoundException("User не найден")); - } -} diff --git a/src/test/java/ru/practicum/shareit/ShareItTests.java b/src/test/java/ru/practicum/shareit/ShareItTests.java deleted file mode 100644 index 4d79052..0000000 --- a/src/test/java/ru/practicum/shareit/ShareItTests.java +++ /dev/null @@ -1,13 +0,0 @@ -package ru.practicum.shareit; - -import org.junit.jupiter.api.Test; -import org.springframework.boot.test.context.SpringBootTest; - -@SpringBootTest -class ShareItTests { - - @Test - void contextLoads() { - } - -}