Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
8592062
feat: спринт 16 добавить запросы на вещи
andrej1307 Apr 20, 2025
08cd2f4
feat: добавить просмотрр запросов
andrej1307 Apr 20, 2025
0580570
feat: просмотрр запросов по идентификатору
andrej1307 Apr 20, 2025
de6a871
feat: привязка вещей к запросам
andrej1307 Apr 21, 2025
6abd6e5
feat: реализована работа с запросами вещей.
andrej1307 Apr 22, 2025
b105ad1
fix: тесты Postman - OK
andrej1307 Apr 23, 2025
12e6206
feat: приложение разбито на модули: server, gateway
andrej1307 Apr 23, 2025
4fc8994
feat: добавлен пакет user в модуль gateway
andrej1307 Apr 24, 2025
bd53ca8
feat: реализована работа шлюза с объектами User
andrej1307 Apr 24, 2025
e047f2a
feat: реализована работа шлюза с объектами Item
andrej1307 Apr 24, 2025
584213f
feat: реализована работа шлюза с объектами ItemRequest
andrej1307 Apr 25, 2025
ec01591
feat: добавляем фунции для работы с Booking
andrej1307 Apr 25, 2025
ea9bd88
fix: Gateway + Server - Postman OK
andrej1307 Apr 25, 2025
153ebee
feat: пишем тесты
andrej1307 Apr 26, 2025
5ba142f
feat: пишем тесты UserControllerTest
andrej1307 Apr 27, 2025
c9a2cee
feat: пишем тесты ItemControllerTest
andrej1307 Apr 27, 2025
70e4e8f
feat: пишем тесты ItemControllerTest 2
andrej1307 Apr 28, 2025
76c3478
feat: пишем тесты ItemRequestServiceImplTest
andrej1307 Apr 29, 2025
5737702
feat: добавляем тесты для Booking
andrej1307 Apr 30, 2025
0063583
feat: пишем тесты BookingServiceImplTest
andrej1307 Apr 30, 2025
9d7f870
feat: пишем тесты для модуля Gateway
andrej1307 May 1, 2025
9ce2f9d
feat: реализована работа модулей Gateway и Server
andrej1307 May 2, 2025
babe65a
fix: исправление ошибок стиля
andrej1307 May 2, 2025
d13777a
fix: исправление ошибок стиля 2
andrej1307 May 2, 2025
da751f0
fix: исправление подключения к базе данных
andrej1307 May 2, 2025
1375b48
fix: исправление подключения к базе данных 2
andrej1307 May 3, 2025
27214a1
fix: исправление подключения к базе данных 3
andrej1307 May 3, 2025
d629cc6
fix: исправление подключения к базе данных 4
andrej1307 May 3, 2025
2afb091
fix: танцы с бубном
andrej1307 May 3, 2025
b170364
fix: танцы с бубном 2
andrej1307 May 3, 2025
5ad0449
fix: танцы с бубном 3
andrej1307 May 3, 2025
7a0e4e3
fix: танцы с бубном 4
andrej1307 May 3, 2025
6b322a8
fix: AutoConfigureTestDatabase
andrej1307 May 3, 2025
43cb640
fix: JdbcTest
andrej1307 May 3, 2025
c71667f
fix: H2
andrej1307 May 3, 2025
e7bd5eb
fix: добавлен охват тестами ShahtitServer
andrej1307 May 3, 2025
428f0ba
fix: добавлен охват тестами ShahtitServer 2
andrej1307 May 3, 2025
dc82215
fix: исправление ошибок стиля
andrej1307 May 3, 2025
eb2f77b
fix: исправление ошибок UserServiceImpl
andrej1307 May 4, 2025
ed086ce
fix: увеличиваем покрытие тестами
andrej1307 May 4, 2025
bad9a2c
fix: увеличиваем покрытие тестами 1
andrej1307 May 4, 2025
f3dcb37
fix: увеличиваем покрытие тестами 2
andrej1307 May 4, 2025
350bfe7
fix: изменяем параметры DataSource
andrej1307 May 4, 2025
1e2cab8
fix: изменяем параметры DataSource 2
andrej1307 May 4, 2025
342beb4
fix: исправление замечаний ревьювера
andrej1307 May 5, 2025
17ac5b8
fix: исправление замечаний ревьювера 2
andrej1307 May 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# java-shareit
учебный проект

## Спринт №15
## Спринт №16
Добавляем запросы на вещи и "шлюз"
39 changes: 39 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions gateway/Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]
70 changes: 70 additions & 0 deletions gateway/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>ru.practicum</groupId>
<artifactId>shareit</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>

<artifactId>shareit-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>

<name>ShareIt Gateway</name>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
</dependency>

<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
12 changes: 12 additions & 0 deletions gateway/src/main/java/ru/practicum/shareit/ShareItGateway.java
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package ru.practicum.shareit.advisor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.List;
import java.util.stream.Collectors;

/**
* Класс обработки исключений при обработке поступивших http запросов
*/
@Slf4j
@RestControllerAdvice
public class ErrorAdvisor {

/**
* Обработка исключения MethodArgumentNotValidException - при проверке аргумента метода
*
* @param e - исключение
* @return - список нарушений для отображения в теле ответа
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorMessage onMethodArgumentNotValidException(
MethodArgumentNotValidException e
) {
final List<ErrorMessage> violations = e.getBindingResult().getFieldErrors().stream()
.map(error -> new ErrorMessage("[" + error.getField() + "] "
+ error.getDefaultMessage()))
.collect(Collectors.toList());
log.error("400 {}.", e.getMessage());
return violations.get(0);
}

/**
* Обработка непредвиденного исключения
*
* @param e - исключение
* @return - сообщение об ошибке
*/
@ExceptionHandler
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorMessage handleException(final Exception e) {
log.error("Error", e);
return new ErrorMessage(e.getMessage());
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
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.BookItemRequestDto;
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<Object> bookItem(long userId, BookItemRequestDto requestDto) {
return post("", userId, requestDto);
}

public ResponseEntity<Object> getBookings(long userId, BookingState state, Integer from, Integer size) {
Map<String, Object> parameters = Map.of(
"state", state.name(),
"from", from,
"size", size);
return get("?state={state}&from={from}&size={size}", userId, parameters);
}

public ResponseEntity<Object> getBooking(long userId, Long bookingId) {
return get("/" + bookingId, userId);
}

public ResponseEntity<Object> approvedBooking(Long id, Long editorId, Boolean approved) {
Map<String, Object> parameters = Map.of("approved", approved);
return patch("/" + id + "?approved={approved}", editorId, parameters, null);
}

public ResponseEntity<Object> findBookingsByOwner(Long ownerId,
BookingState state,
Integer from,
Integer size) {
Map<String, Object> parameters = Map.of(
"state", state.name(),
"from", from,
"size", size);
return get("/owner?state={state}&from={from}&size={size}", ownerId, parameters);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
package ru.practicum.shareit.booking;

import jakarta.validation.constraints.Positive;
import jakarta.validation.constraints.PositiveOrZero;
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.booking.dto.BookItemRequestDto;
import ru.practicum.shareit.booking.dto.BookingState;
import ru.practicum.shareit.validator.ValidAction;

/**
* Обработка запроса на бронирование вещей
*/
@Controller
@RequestMapping(path = "/bookings")
@RequiredArgsConstructor
@Slf4j
@Validated
public class BookingController {
private static final String HEADER_USER_ID = "X-Sharer-User-Id";
private final BookingClient bookingClient;

/**
* Ищем бронирования в соответствии со статусом
*
* @param userId - идентификатор пользователя
* @param stateParam - статус бронирования
* @param from
* @param size
* @return - результат поиска
*/
@GetMapping
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<Object> getBookings(@RequestHeader(HEADER_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("Неизвестный режим: " + stateParam));
log.info("Ищем запросы state {}, userId={}, from={}, size={}", stateParam, userId, from, size);
return bookingClient.getBookings(userId, state, from, size);
}

/**
* Бронирование вещии
*
* @param userId - идентификатор пользователя
* @param requestDto - данные о бронировании
* @return - созданный объект бронирования
*/
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ResponseEntity<Object> bookItem(@RequestHeader(HEADER_USER_ID) long userId,
@Validated(ValidAction.OnCreate.class)
@RequestBody BookItemRequestDto requestDto) {
log.info("Создаем запрос на бронирование {}, userId={}", requestDto, userId);
return bookingClient.bookItem(userId, requestDto);
}

/**
* Редактирукм запрос на бронироапние
*/
@PatchMapping("/{id}")
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<Object> editBooking(
@RequestHeader(HEADER_USER_ID) final Long editorId,
@PathVariable Long id,
@RequestParam Boolean approved) {
log.info("Пользователь id={} редактирует запрос на бронирование id={}, approved={}",
editorId, id, approved);
return bookingClient.approvedBooking(id, editorId, approved);
}

/**
* Ищем запрос на бронирование
*
* @param userId - идентификатор пользователя
* @param bookingId - идентификатор бронирования
* @return - результат поиска
*/
@ResponseStatus(HttpStatus.OK)
@GetMapping("/{bookingId}")
public ResponseEntity<Object> getBooking(@RequestHeader(HEADER_USER_ID) long userId,
@PathVariable Long bookingId) {
log.info("Get booking {}, userId={}", bookingId, userId);
return bookingClient.getBooking(userId, bookingId);
}

@GetMapping("/owner")
@ResponseStatus(HttpStatus.OK)
public ResponseEntity<Object> findBookingByOwner(
@RequestHeader(HEADER_USER_ID) final Long ownerId,
@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("Неизвестный режим: " + stateParam));
log.info("Пользователь id={} просматривает запроcы на все свои вещи. state {}, userId={}, from={}, size={}",
ownerId, state, from, size);
return bookingClient.findBookingsByOwner(ownerId, state, from, size);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ru.practicum.shareit.booking.dto;

import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.FutureOrPresent;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class BookItemRequestDto {
private long itemId;
@FutureOrPresent
private LocalDateTime start;
@Future
private LocalDateTime end;
}
Loading