Skip to content

Commit

Permalink
Merge pull request #4 from Nikolaev-Java/add-bookings
Browse files Browse the repository at this point in the history
Add bookings
  • Loading branch information
Nikolaev-Java authored Aug 5, 2024
2 parents b12150c + 5adcb0c commit f63eb44
Show file tree
Hide file tree
Showing 48 changed files with 1,092 additions and 571 deletions.
16 changes: 16 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
services:
db:
image: postgres:16.1
ports:
- "6432:5432"
volumes:
- ./volumes/postgres:/var/lib/postgresql/data/
environment:
- POSTGRES_DB=shareit
- POSTGRES_USER=shareit
- POSTGRES_PASSWORD=12345
healthcheck:
test: pg_isready -q -d $$POSTGRES_DB -U $$POSTGRES_USER
timeout: 5s
interval: 5s
retries: 10
35 changes: 34 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
Expand Down Expand Up @@ -54,6 +57,12 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-jpa</artifactId>
<classifier>jakarta</classifier>
<version>5.1.0</version>
</dependency>
</dependencies>

<build>
Expand All @@ -64,6 +73,30 @@
</resource>
</resources>
<plugins>
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.querydsl</groupId>
<artifactId>querydsl-apt</artifactId>
<classifier>jakarta</classifier>
<version>5.1.0</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
Expand Down
7 changes: 0 additions & 7 deletions src/main/java/ru/practicum/shareit/booking/Booking.java

This file was deleted.

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

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import ru.practicum.shareit.booking.dto.BookingDtoResponse;
import ru.practicum.shareit.booking.dto.NewBookingDtoRequest;
import ru.practicum.shareit.booking.model.BookingState;
import ru.practicum.shareit.booking.service.BookingService;
import ru.practicum.shareit.validationMarker.Marker;

import java.util.List;

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

@PostMapping
@Validated(Marker.OnCreate.class)
public BookingDtoResponse createBooking(@RequestBody @Valid NewBookingDtoRequest dto,
@RequestHeader(USER_ID) long userId) {
dto.setBookerId(userId);
return bookingService.createBooking(dto);
}

@PatchMapping("/{bookingId}")
public BookingDtoResponse decideRent(@PathVariable long bookingId,
@RequestHeader(USER_ID) long userId,
@RequestParam boolean approved) {
if (approved) {
return bookingService.approveBooking(bookingId, userId);
} else
return bookingService.rejectBooking(bookingId, userId);
}

@GetMapping("/{bookingId}")
public BookingDtoResponse getBookingByIdOfBookerOrOwner(@PathVariable long bookingId,
@RequestHeader(USER_ID) long userId) {
return bookingService.getBookingByIdOfBookerOrOwner(bookingId, userId);
}

@GetMapping
public List<BookingDtoResponse> getAllBookingsByBooker(@RequestHeader(USER_ID) long userId,
@RequestParam(name = "state", defaultValue = "ALL") String stateParam) {
BookingState state = BookingState.parseString(stateParam);
return bookingService.getAllBookingsByBooker(userId, state);
}

@GetMapping("/owner")
public List<BookingDtoResponse> getAllBookingsByOwner(@RequestHeader(USER_ID) long userId,
@RequestParam(name = "state", defaultValue = "ALL") String stateParam) {
BookingState state = BookingState.parseString(stateParam);
return bookingService.getAllBookingsByOwner(userId, state);
}
}
58 changes: 58 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,58 @@
package ru.practicum.shareit.booking;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import ru.practicum.shareit.booking.model.Booking;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;

public interface BookingRepository extends JpaRepository<Booking, Long>, QuerydslPredicateExecutor<Booking> {
@Query(value = """
select b from Booking as b where b.id=?1 and (b.booker.id=?2 or b.item.owner.id=?2)
""")
Optional<Booking> findById(long bookingId, long userId);

@Query(value = """
select b from Booking as b where b.item.id in ?1
and b.status='APPROVED'
and b.startTime < ?2
order by b.startTime desc
limit 1
""")
Optional<Booking> findByItemLastBooking(Long itemId, LocalDateTime now);

@Query(value = """
select b from Booking as b where b.item.id in ?1
and b.status='APPROVED'
and b.startTime > ?2
order by b.startTime asc
limit 1
""")
Optional<Booking> findByItemNextBooking(Long itemId, LocalDateTime now);

@Query(value = """
select b from Booking as b
join(select b.item.id as item, max(b.startTime) as firstStartTime
from Booking as b
where b.startTime < ?2 and b.status='APPROVED'
group by b.item.id) as subquery on b.item.id=subquery.item and b.startTime = subquery.firstStartTime
where b.item.id in ?1
""")
List<Booking> findAllByItemsLastBooking(List<Long> itemIds, LocalDateTime now);

@Query(value = """
select b from Booking as b
join(select b.item.id as item, min(b.startTime) as firstStartTime
from Booking as b
where b.startTime > ?2 and b.status='APPROVED'
group by b.item.id) as subquery on b.item.id=subquery.item and b.startTime = subquery.firstStartTime
where b.item.id in ?1
""")
List<Booking> findAllByItemsNextBooking(List<Long> itemIds, LocalDateTime now);

List<Booking> findAllByItemId(Long itemId);
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package ru.practicum.shareit.booking.dto;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

import java.time.LocalDateTime;

/**
* TODO Sprint add-bookings.
*/
@Getter
@Setter
@Builder
public class BookingDtoResponse {
private long id;
private ItemDtoResponse item;
private UserDtoResponse booker;
private LocalDateTime start;
private LocalDateTime end;
private String status;

@Setter
@Getter
@Builder
static class ItemDtoResponse {
private long id;
private String name;
}

@Setter
@Getter
@AllArgsConstructor
static class UserDtoResponse {
private long id;
}
}
43 changes: 43 additions & 0 deletions src/main/java/ru/practicum/shareit/booking/dto/BookingMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package ru.practicum.shareit.booking.dto;

import org.springframework.stereotype.Component;
import ru.practicum.shareit.booking.model.Booking;
import ru.practicum.shareit.booking.model.Status;
import ru.practicum.shareit.item.Item;
import ru.practicum.shareit.user.User;

import java.util.List;

@Component
public class BookingMapper {

public Booking toNewBooking(NewBookingDtoRequest dto, Item item, User booker) {
if (dto == null) return null;
return Booking.builder()
.item(item)
.booker(booker)
.startTime(dto.getStart())
.endTime(dto.getEnd())
.status(Status.WAITING)
.build();
}

public BookingDtoResponse toBookingDtoResponse(Booking booking) {
if (booking == null) return null;
return BookingDtoResponse.builder()
.id(booking.getId())
.item(BookingDtoResponse.ItemDtoResponse.builder()
.id(booking.getItem().getId())
.name(booking.getItem().getName())
.build())
.booker(new BookingDtoResponse.UserDtoResponse(booking.getBooker().getId()))
.start(booking.getStartTime())
.end(booking.getEndTime())
.status(booking.getStatus().toString())
.build();
}

public List<BookingDtoResponse> toBookingDtoResponse(List<Booking> bookings) {
return bookings.stream().map(this::toBookingDtoResponse).toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ru.practicum.shareit.booking.dto;

import jakarta.validation.constraints.Future;
import jakarta.validation.constraints.FutureOrPresent;
import jakarta.validation.constraints.NotNull;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import ru.practicum.shareit.booking.validation.StartBeforeEnd;
import ru.practicum.shareit.validationMarker.Marker;

import java.time.LocalDateTime;

@Getter
@Setter
@Builder
@StartBeforeEnd
public class NewBookingDtoRequest {
@NotNull(message = "Item id must not be null")
private Long itemId;
private long bookerId;
@NotNull(message = "Start time booking must not be null", groups = Marker.OnCreate.class)
@FutureOrPresent(message = "Start time cannot be in the past", groups = Marker.OnCreate.class)
private LocalDateTime start;
@NotNull(message = "End time booking must not be null", groups = Marker.OnCreate.class)
@Future(message = "End time cannot be in the past", groups = Marker.OnCreate.class)
private LocalDateTime end;
}
52 changes: 52 additions & 0 deletions src/main/java/ru/practicum/shareit/booking/model/Booking.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package ru.practicum.shareit.booking.model;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import ru.practicum.shareit.item.Item;
import ru.practicum.shareit.user.User;

import java.time.LocalDateTime;

/**
* TODO Sprint add-bookings.
*/
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = "id")
@Entity
@Table(name = "bookings")
public class Booking {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id")
private User booker;
@Column(name = "start_time")
private LocalDateTime startTime;
@Column(name = "end_time")
private LocalDateTime endTime;
@Enumerated(EnumType.STRING)
private Status status;
}
13 changes: 13 additions & 0 deletions src/main/java/ru/practicum/shareit/booking/model/BookingState.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.practicum.shareit.booking.model;

public enum BookingState {
ALL, CURRENT, PAST, FUTURE, WAITING, REJECTED;

public static BookingState parseString(String state) {
try {
return valueOf(state.toUpperCase());
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException("Unknown state: " + state);
}
}
}
Loading

0 comments on commit f63eb44

Please sign in to comment.