Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f7f4b08
feat: implement all sprint 15 features (database, bookings, comments)
Darya346 Mar 26, 2026
79ca6a3
fix: fix syntax errors in ItemServiceImpl and remove duplicate depend…
Darya346 Mar 26, 2026
9ad64b1
fix: fix syntax errors in ItemServiceImpl and remove duplicate depend…
Darya346 Mar 26, 2026
0f607a6
fix: resolve merge conflicts by preferring current version
Darya346 Mar 26, 2026
9102608
fix: remove old repository implementations for good
Darya346 Mar 26, 2026
ba5ee5c
style: final clean up of whitespaces
Darya346 Mar 26, 2026
d8224ac
fix: resolve ApplicationContext failure and fix datasource config
Darya346 Mar 26, 2026
1564f5f
style: remove unused import in ShareItApp
Darya346 Mar 26, 2026
2ebfbc3
fix: cleanup pom
Darya346 Mar 26, 2026
a223815
fix: add no-args constructors
Darya346 Mar 26, 2026
37a854b
fix: sync repository and service method names
Darya346 Mar 26, 2026
d006571
fix: sync repository and service method names
Darya346 Mar 26, 2026
b7037e1
style: fix trailing whitespace
Darya346 Mar 26, 2026
ad3cc8a
fix: resolve maven duplicate and missing constructors
Darya346 Mar 26, 2026
e247bef
fix: align java models with sql schema and add fielddefaults
Darya346 Mar 26, 2026
3bce5b2
fix: finalize maven config and resolve startup issues for CI tests
Darya346 Mar 26, 2026
e68e6d4
fix: force database schema recreation for CI tests
Darya346 Mar 26, 2026
fe5417a
fix: make datasource config universal for CI environment
Darya346 Mar 26, 2026
2191216
refactor: shorten booking repository method names using @Query
Darya346 Mar 27, 2026
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
24 changes: 22 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
Expand All @@ -25,18 +26,37 @@

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

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

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

<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</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>
Expand Down
3 changes: 1 addition & 2 deletions src/main/java/ru/practicum/shareit/ShareItApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,8 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@SpringBootApplication
public class ShareItApp {
public static void main(String[] args) {
SpringApplication.run(ShareItApp.class, args);
Expand Down
7 changes: 0 additions & 7 deletions src/main/java/ru/practicum/shareit/booking/Booking.java

This file was deleted.

48 changes: 42 additions & 6 deletions src/main/java/ru/practicum/shareit/booking/BookingController.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,48 @@
package ru.practicum.shareit.booking;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import ru.practicum.shareit.booking.dto.BookingDto;
import ru.practicum.shareit.booking.dto.BookingResponseDto;

import jakarta.validation.Valid;
import java.util.List;

/**
* TODO Sprint add-bookings.
*/
@RestController
@RequestMapping(path = "/bookings")
@RequiredArgsConstructor
public class BookingController {
}
private final BookingService bookingService;
private static final String USER_ID_HEADER = "X-Sharer-User-Id";

@PostMapping
public BookingResponseDto create(@RequestHeader(USER_ID_HEADER) Long userId,
@Valid @RequestBody BookingDto bookingDto) {
return bookingService.create(userId, bookingDto);
}

@PatchMapping("/{bookingId}")
public BookingResponseDto approve(@RequestHeader(USER_ID_HEADER) Long userId,
@PathVariable Long bookingId,
@RequestParam boolean approved) {
return bookingService.approve(userId, bookingId, approved);
}

@GetMapping("/{bookingId}")
public BookingResponseDto getById(@RequestHeader(USER_ID_HEADER) Long userId,
@PathVariable Long bookingId) {
return bookingService.getById(userId, bookingId);
}

@GetMapping
public List<BookingResponseDto> getAllByBooker(@RequestHeader(USER_ID_HEADER) Long userId,
@RequestParam(defaultValue = "ALL") String state) {
return bookingService.getAllByBooker(userId, state);
}

@GetMapping("/owner")
public List<BookingResponseDto> getAllByOwner(@RequestHeader(USER_ID_HEADER) Long userId,
@RequestParam(defaultValue = "ALL") String state) {
return bookingService.getAllByOwner(userId, state);
}
}
44 changes: 44 additions & 0 deletions src/main/java/ru/practicum/shareit/booking/BookingRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package ru.practicum.shareit.booking;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import ru.practicum.shareit.booking.model.Booking;
import ru.practicum.shareit.booking.model.Status;
import java.time.LocalDateTime;
import java.util.List;

public interface BookingRepository extends JpaRepository<Booking, Long> {

@Query("select b from Booking b where b.booker.id = ?1 order by b.start desc")
List<Booking> findUserBookings(Long bookerId);

@Query("select b from Booking b where b.booker.id = ?1 and b.status = ?2 order by b.start desc")
List<Booking> findUserStatus(Long bookerId, Status status);

@Query("select b from Booking b where b.booker.id = ?1 and b.start <= ?2 and b.end >= ?2 order by b.start desc")
List<Booking> findUserCurrent(Long bookerId, LocalDateTime now);

@Query("select b from Booking b where b.booker.id = ?1 and b.end < ?2 order by b.start desc")
List<Booking> findUserPast(Long bookerId, LocalDateTime now);

@Query("select b from Booking b where b.booker.id = ?1 and b.start > ?2 order by b.start desc")
List<Booking> findUserFuture(Long bookerId, LocalDateTime now);

@Query("select b from Booking b where b.item.owner.id = ?1 order by b.start desc")
List<Booking> findOwnerBookings(Long ownerId);

@Query("select b from Booking b where b.item.owner.id = ?1 and b.status = ?2 order by b.start desc")
List<Booking> findOwnerStatus(Long ownerId, Status status);

@Query("select b from Booking b where b.item.owner.id = ?1 and b.start <= ?2 and b.end >= ?2 order by b.start desc")
List<Booking> findOwnerCurrent(Long ownerId, LocalDateTime now);

@Query("select b from Booking b where b.item.owner.id = ?1 and b.end < ?2 order by b.start desc")
List<Booking> findOwnerPast(Long ownerId, LocalDateTime now);

@Query("select b from Booking b where b.item.owner.id = ?1 and b.start > ?2 order by b.start desc")
List<Booking> findOwnerFuture(Long ownerId, LocalDateTime now);

@Query("select b from Booking b where b.item.id = ?1 order by b.start asc")
List<Booking> findItemBookings(Long itemId);
}
19 changes: 19 additions & 0 deletions src/main/java/ru/practicum/shareit/booking/BookingService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package ru.practicum.shareit.booking;

import ru.practicum.shareit.booking.dto.BookingDto;
import ru.practicum.shareit.booking.dto.BookingResponseDto;

import java.util.List;

public interface BookingService {

BookingResponseDto create(Long userId, BookingDto bookingDto);

BookingResponseDto approve(Long userId, Long bookingId, boolean approved);

BookingResponseDto getById(Long userId, Long bookingId);

List<BookingResponseDto> getAllByBooker(Long userId, String state);

List<BookingResponseDto> getAllByOwner(Long userId, String state);
}
100 changes: 100 additions & 0 deletions src/main/java/ru/practicum/shareit/booking/BookingServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package ru.practicum.shareit.booking;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import ru.practicum.shareit.booking.dto.BookingDto;
import ru.practicum.shareit.booking.dto.BookingResponseDto;
import ru.practicum.shareit.booking.mapper.BookingMapper;
import ru.practicum.shareit.booking.model.Booking;
import ru.practicum.shareit.booking.model.Status;
import ru.practicum.shareit.exception.NotFoundException;
import ru.practicum.shareit.exception.ValidationException;
import ru.practicum.shareit.item.ItemRepository;
import ru.practicum.shareit.item.model.Item;
import ru.practicum.shareit.user.User;
import ru.practicum.shareit.user.UserRepository;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class BookingServiceImpl implements BookingService {
private final BookingRepository bookingRepository;
private final UserRepository userRepository;
private final ItemRepository itemRepository;

@Transactional
@Override
public BookingResponseDto create(Long userId, BookingDto bookingDto) {
User booker = userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User not found"));
Item item = itemRepository.findById(bookingDto.getItemId()).orElseThrow(() -> new NotFoundException("Item not found"));
if (!item.getAvailable()) throw new ValidationException("Not available");
if (item.getOwner().getId().equals(userId)) throw new NotFoundException("Owner cannot book");
if (bookingDto.getEnd().isBefore(bookingDto.getStart()) || bookingDto.getEnd().equals(bookingDto.getStart())) {
throw new ValidationException("Wrong dates");
}
Booking booking = new Booking();
booking.setStart(bookingDto.getStart());
booking.setEnd(bookingDto.getEnd());
booking.setItem(item);
booking.setBooker(booker);
booking.setStatus(Status.WAITING);
return BookingMapper.toBookingResponse(bookingRepository.save(booking));
}

@Transactional
@Override
public BookingResponseDto approve(Long userId, Long bookingId, boolean approved) {
Booking booking = bookingRepository.findById(bookingId).orElseThrow(() -> new NotFoundException("Not found"));
if (!booking.getItem().getOwner().getId().equals(userId)) throw new ValidationException("Not an owner");
if (booking.getStatus() != Status.WAITING) throw new ValidationException("Already changed");
booking.setStatus(approved ? Status.APPROVED : Status.REJECTED);
return BookingMapper.toBookingResponse(bookingRepository.save(booking));
}

@Override
public BookingResponseDto getById(Long userId, Long bookingId) {
Booking booking = bookingRepository.findById(bookingId).orElseThrow(() -> new NotFoundException("Not found"));
if (!booking.getBooker().getId().equals(userId) && !booking.getItem().getOwner().getId().equals(userId)) {
throw new NotFoundException("No access");
}
return BookingMapper.toBookingResponse(booking);
}

@Override
public List<BookingResponseDto> getAllByBooker(Long userId, String state) {
userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User not found"));
LocalDateTime now = LocalDateTime.now();
List<Booking> bookings;
switch (state.toUpperCase()) {
case "ALL": bookings = bookingRepository.findUserBookings(userId); break;
case "CURRENT": bookings = bookingRepository.findUserCurrent(userId, now); break;
case "PAST": bookings = bookingRepository.findUserPast(userId, now); break;
case "FUTURE": bookings = bookingRepository.findUserFuture(userId, now); break;
case "WAITING": bookings = bookingRepository.findUserStatus(userId, Status.WAITING); break;
case "REJECTED": bookings = bookingRepository.findUserStatus(userId, Status.REJECTED); break;
default: throw new ValidationException("Unknown state: " + state);
}
return bookings.stream().map(BookingMapper::toBookingResponse).collect(Collectors.toList());
}

@Override
public List<BookingResponseDto> getAllByOwner(Long userId, String state) {
userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User not found"));
LocalDateTime now = LocalDateTime.now();
List<Booking> bookings;
switch (state.toUpperCase()) {
case "ALL": bookings = bookingRepository.findOwnerBookings(userId); break;
case "CURRENT": bookings = bookingRepository.findOwnerCurrent(userId, now); break;
case "PAST": bookings = bookingRepository.findOwnerPast(userId, now); break;
case "FUTURE": bookings = bookingRepository.findOwnerFuture(userId, now); break;
case "WAITING": bookings = bookingRepository.findOwnerStatus(userId, Status.WAITING); break;
case "REJECTED": bookings = bookingRepository.findOwnerStatus(userId, Status.REJECTED); break;
default: throw new ValidationException("Unknown state: " + state);
}
return bookings.stream().map(BookingMapper::toBookingResponse).collect(Collectors.toList());
}
}
28 changes: 24 additions & 4 deletions src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,27 @@
package ru.practicum.shareit.booking.dto;

/**
* TODO Sprint add-bookings.
*/
import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.FutureOrPresent;
import jakarta.validation.constraints.NotNull;
import lombok.*;
import lombok.experimental.FieldDefaults;

import java.time.LocalDateTime;

@Data
@AllArgsConstructor
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class BookingDto {
}

@NotNull
Long itemId;

@NotNull
@FutureOrPresent
LocalDateTime start;

@NotNull
@Future
LocalDateTime end;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package ru.practicum.shareit.booking.dto;

import lombok.*;
import lombok.experimental.FieldDefaults;
import ru.practicum.shareit.booking.model.Status;
import ru.practicum.shareit.item.dto.ItemDto;
import ru.practicum.shareit.user.dto.UserDto;

import java.time.LocalDateTime;

@Data
@AllArgsConstructor
@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class BookingResponseDto {

Long id;
LocalDateTime start;
LocalDateTime end;
ItemDto item;
UserDto booker;
Status status;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ru.practicum.shareit.booking.mapper;

import ru.practicum.shareit.booking.dto.BookingResponseDto;
import ru.practicum.shareit.booking.model.Booking;
import ru.practicum.shareit.item.mapper.ItemMapper;
import ru.practicum.shareit.user.mapper.UserMapper;

public class BookingMapper {

public static BookingResponseDto toBookingResponse(Booking booking) {
return new BookingResponseDto(
booking.getId(),
booking.getStart(),
booking.getEnd(),
ItemMapper.toItemDto(booking.getItem()),
UserMapper.toUserDto(booking.getBooker()),
booking.getStatus()
);
}
}
Loading
Loading