diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/BorrowingFacade.java b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/BorrowingFacade.java index b92b833..f59b9dc 100644 --- a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/BorrowingFacade.java +++ b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/BorrowingFacade.java @@ -3,7 +3,7 @@ package io.wkrzywiec.hexagonal.library.borrowing; import io.wkrzywiec.hexagonal.library.borrowing.model.ActiveUser; import io.wkrzywiec.hexagonal.library.borrowing.model.BookReservedEvent; import io.wkrzywiec.hexagonal.library.borrowing.model.Days; -import io.wkrzywiec.hexagonal.library.borrowing.model.MaxReservationInterval; +import io.wkrzywiec.hexagonal.library.borrowing.model.DueDate; import io.wkrzywiec.hexagonal.library.borrowing.model.MakeBookAvailableCommand; import io.wkrzywiec.hexagonal.library.borrowing.model.OverdueReservation; import io.wkrzywiec.hexagonal.library.borrowing.model.ReservationDetails; @@ -18,6 +18,8 @@ import io.wkrzywiec.hexagonal.library.borrowing.ports.incoming.ReserveBook; import io.wkrzywiec.hexagonal.library.borrowing.ports.outgoing.BorrowingDatabase; import io.wkrzywiec.hexagonal.library.borrowing.ports.outgoing.BorrowingEventPublisher; +import java.time.Instant; +import java.time.temporal.ChronoUnit; import java.util.List; public class BorrowingFacade implements MakeBookAvailable, ReserveBook, CancelOverdueReservations { @@ -53,8 +55,8 @@ public class BorrowingFacade implements MakeBookAvailable, ReserveBook, CancelOv @Override public void cancelOverdueReservations() { - MaxReservationInterval maxReservationInterval = new MaxReservationInterval(new Days(3L)); - List overdueReservationList = database.findReservationsAfter(maxReservationInterval); + DueDate dueDate = new DueDate(Instant.now().plus(3L, ChronoUnit.DAYS)); + List overdueReservationList = database.findReservationsAfter(dueDate); overdueReservationList.forEach( overdue -> database.setBookAvailable(overdue.getBookIdentificationAsLong())); } diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/BorrowingDatabaseAdapter.java b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/BorrowingDatabaseAdapter.java index 4b7dc99..1f8a787 100644 --- a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/BorrowingDatabaseAdapter.java +++ b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/BorrowingDatabaseAdapter.java @@ -2,7 +2,7 @@ package io.wkrzywiec.hexagonal.library.borrowing.infrastructure; import io.wkrzywiec.hexagonal.library.borrowing.model.ActiveUser; import io.wkrzywiec.hexagonal.library.borrowing.model.AvailableBook; -import io.wkrzywiec.hexagonal.library.borrowing.model.MaxReservationInterval; +import io.wkrzywiec.hexagonal.library.borrowing.model.DueDate; import io.wkrzywiec.hexagonal.library.borrowing.model.OverdueReservation; import io.wkrzywiec.hexagonal.library.borrowing.model.ReservationDetails; import io.wkrzywiec.hexagonal.library.borrowing.model.ReservationId; @@ -10,11 +10,14 @@ import io.wkrzywiec.hexagonal.library.borrowing.model.ReservedBook; import io.wkrzywiec.hexagonal.library.borrowing.ports.outgoing.BorrowingDatabase; import lombok.RequiredArgsConstructor; import org.springframework.dao.DataAccessException; +import org.springframework.jdbc.core.BeanPropertyRowMapper; import org.springframework.jdbc.core.JdbcTemplate; +import java.sql.Timestamp; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; @RequiredArgsConstructor public class BorrowingDatabaseAdapter implements BorrowingDatabase { @@ -96,7 +99,13 @@ public class BorrowingDatabaseAdapter implements BorrowingDatabase { } @Override - public List findReservationsAfter(MaxReservationInterval maxReservationInterval) { - return null; + public List findReservationsAfter(DueDate dueDate) { + List entities = jdbcTemplate.query( + "SELECT id AS reservationId, book_id AS bookIdentification FROM reserved WHERE reserved_date > ?", + new BeanPropertyRowMapper(OverdueReservationEntity.class), + Timestamp.from(dueDate.asInstant())); + return entities.stream() + .map(entity -> new OverdueReservation(entity.getReservationId(), entity.getBookIdentification())) + .collect(Collectors.toList()); } } diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/OverdueReservationEntity.java b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/OverdueReservationEntity.java new file mode 100644 index 0000000..f999be4 --- /dev/null +++ b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/OverdueReservationEntity.java @@ -0,0 +1,9 @@ +package io.wkrzywiec.hexagonal.library.borrowing.infrastructure; + +import lombok.Data; + +@Data +public class OverdueReservationEntity { + private Long reservationId; + private Long bookIdentification; +} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/model/DueDate.java b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/model/DueDate.java new file mode 100644 index 0000000..4eda038 --- /dev/null +++ b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/model/DueDate.java @@ -0,0 +1,14 @@ +package io.wkrzywiec.hexagonal.library.borrowing.model; + +import lombok.AllArgsConstructor; + +import java.time.Instant; + +@AllArgsConstructor +public class DueDate { + private final Instant timeStamp; + + public Instant asInstant(){ + return timeStamp; + } +} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/model/MaxReservationInterval.java b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/model/MaxReservationInterval.java deleted file mode 100644 index 0e94bea..0000000 --- a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/model/MaxReservationInterval.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.wkrzywiec.hexagonal.library.borrowing.model; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -@Getter -public class MaxReservationInterval { - public Days days; -} diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/model/OverdueReservation.java b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/model/OverdueReservation.java index f0cb84d..70e1fde 100644 --- a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/model/OverdueReservation.java +++ b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/model/OverdueReservation.java @@ -4,10 +4,10 @@ import lombok.AllArgsConstructor; @AllArgsConstructor public class OverdueReservation { - private final ReservationId reservationId; - private final BookIdentification bookIdentification; + private Long reservationId; + private Long bookIdentification; public Long getBookIdentificationAsLong() { - return bookIdentification.getValueAsLong(); + return bookIdentification; } } diff --git a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/ports/outgoing/BorrowingDatabase.java b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/ports/outgoing/BorrowingDatabase.java index a455dfd..17016e8 100644 --- a/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/ports/outgoing/BorrowingDatabase.java +++ b/src/main/java/io/wkrzywiec/hexagonal/library/borrowing/ports/outgoing/BorrowingDatabase.java @@ -2,7 +2,7 @@ package io.wkrzywiec.hexagonal.library.borrowing.ports.outgoing; import io.wkrzywiec.hexagonal.library.borrowing.model.ActiveUser; import io.wkrzywiec.hexagonal.library.borrowing.model.AvailableBook; -import io.wkrzywiec.hexagonal.library.borrowing.model.MaxReservationInterval; +import io.wkrzywiec.hexagonal.library.borrowing.model.DueDate; import io.wkrzywiec.hexagonal.library.borrowing.model.OverdueReservation; import io.wkrzywiec.hexagonal.library.borrowing.model.ReservationDetails; import io.wkrzywiec.hexagonal.library.borrowing.model.ReservedBook; @@ -15,5 +15,5 @@ public interface BorrowingDatabase { Optional getAvailableBook(Long bookId); Optional getActiveUser(Long userId); ReservationDetails save(ReservedBook reservedBook); - List findReservationsAfter(MaxReservationInterval maxReservationInterval); + List findReservationsAfter(DueDate dueDate); } diff --git a/src/test/java/io/wkrzywiec/hexagonal/library/borrowing/InMemoryBorrowingDatabase.java b/src/test/java/io/wkrzywiec/hexagonal/library/borrowing/InMemoryBorrowingDatabase.java index b087597..fd42fa5 100644 --- a/src/test/java/io/wkrzywiec/hexagonal/library/borrowing/InMemoryBorrowingDatabase.java +++ b/src/test/java/io/wkrzywiec/hexagonal/library/borrowing/InMemoryBorrowingDatabase.java @@ -3,7 +3,7 @@ package io.wkrzywiec.hexagonal.library.borrowing; import io.wkrzywiec.hexagonal.library.borrowing.model.ActiveUser; import io.wkrzywiec.hexagonal.library.borrowing.model.AvailableBook; import io.wkrzywiec.hexagonal.library.borrowing.model.BookIdentification; -import io.wkrzywiec.hexagonal.library.borrowing.model.MaxReservationInterval; +import io.wkrzywiec.hexagonal.library.borrowing.model.DueDate; import io.wkrzywiec.hexagonal.library.borrowing.model.OverdueReservation; import io.wkrzywiec.hexagonal.library.borrowing.model.ReservationDetails; import io.wkrzywiec.hexagonal.library.borrowing.model.ReservationId; @@ -12,7 +12,6 @@ import io.wkrzywiec.hexagonal.library.borrowing.ports.outgoing.BorrowingDatabase import java.time.Instant; import java.time.temporal.ChronoUnit; -import java.util.ArrayList; import java.util.List; import java.util.Optional; import java.util.Random; @@ -58,15 +57,15 @@ public class InMemoryBorrowingDatabase implements BorrowingDatabase { } @Override - public List findReservationsAfter(MaxReservationInterval maxReservationInterval) { + public List findReservationsAfter(DueDate dueDate) { return reservedBooks.values().stream() .filter(reservedBook -> reservedBook.getReservedDateAsInstant() - .isAfter(Instant.now().plus(maxReservationInterval.getDays().getCount(), ChronoUnit.DAYS))) + .isAfter(dueDate.asInstant())) .map(reservedBook -> new OverdueReservation( - new ReservationId(1L), - new BookIdentification(reservedBook.getIdAsLong()))) + 1L, + reservedBook.getIdAsLong())) .collect(Collectors.toList()); } } diff --git a/src/test/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/BorrowingDatabaseAdapterITCase.java b/src/test/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/BorrowingDatabaseAdapterITCase.java index e36d1f0..aa6b229 100644 --- a/src/test/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/BorrowingDatabaseAdapterITCase.java +++ b/src/test/java/io/wkrzywiec/hexagonal/library/borrowing/infrastructure/BorrowingDatabaseAdapterITCase.java @@ -4,6 +4,8 @@ import io.wkrzywiec.hexagonal.library.BookTestData; import io.wkrzywiec.hexagonal.library.UserTestData; import io.wkrzywiec.hexagonal.library.borrowing.model.ActiveUser; import io.wkrzywiec.hexagonal.library.borrowing.model.AvailableBook; +import io.wkrzywiec.hexagonal.library.borrowing.model.DueDate; +import io.wkrzywiec.hexagonal.library.borrowing.model.OverdueReservation; import io.wkrzywiec.hexagonal.library.borrowing.model.ReservationDetails; import io.wkrzywiec.hexagonal.library.borrowing.model.ReservedBook; import org.junit.jupiter.api.BeforeEach; @@ -14,6 +16,9 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.context.jdbc.Sql; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.List; import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -39,10 +44,7 @@ public class BorrowingDatabaseAdapterITCase { @Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD) public void shouldSaveAvailableBook(){ //given - Long bookId = jdbcTemplate.queryForObject( - "SELECT id FROM book WHERE title = ?", - Long.class, - BookTestData.homoDeusBookTitle()); + Long bookId = getHomoDeusBookId(); //when database.setBookAvailable(bookId); @@ -61,10 +63,7 @@ public class BorrowingDatabaseAdapterITCase { @Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD) public void shouldGetAvailableBook(){ //given - Long bookId = jdbcTemplate.queryForObject( - "SELECT id FROM book WHERE title = ?", - Long.class, - BookTestData.homoDeusBookTitle()); + Long bookId = getHomoDeusBookId(); //when Optional availableBookOptional = database.getAvailableBook(bookId); @@ -80,10 +79,7 @@ public class BorrowingDatabaseAdapterITCase { @Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD) public void shouldGetActiveUser() { //given - Long activeUserId = jdbcTemplate.queryForObject( - "SELECT id FROM user WHERE email = ?", - Long.class, - UserTestData.johnDoeEmail()); + Long activeUserId = getJohnDoeUserId(); //when Optional activeUserOptional = database.getActiveUser(activeUserId); @@ -99,15 +95,9 @@ public class BorrowingDatabaseAdapterITCase { @Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD) public void shouldSaveReservedBook(){ //given - Long bookId = jdbcTemplate.queryForObject( - "SELECT id FROM book WHERE title = ?", - Long.class, - BookTestData.homoDeusBookTitle()); + Long bookId = getHomoDeusBookId(); - Long activeUserId = jdbcTemplate.queryForObject( - "SELECT id FROM user WHERE email = ?", - Long.class, - UserTestData.johnDoeEmail()); + Long activeUserId = getJohnDoeUserId(); ReservedBook reservedBook = new ReservedBook(bookId, activeUserId); @@ -119,4 +109,40 @@ public class BorrowingDatabaseAdapterITCase { assertEquals(activeUserId, reservationDetails.getReservedBook().getAssignedUserIdAsLong()); assertTrue(reservationDetails.getReservationId().getIdAsLong() > 0); } + + @Test + @DisplayName("Find book after 3 days of reservation") + @Sql({"/book-and-user.sql"}) + @Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD) + public void shouldFindOverdueReservations(){ + //given + DueDate thirdDayAfterReservation = new DueDate(Instant.now().plus(3, ChronoUnit.DAYS)); + Long overdueBookId = getHomoDeusBookId(); + Long johnDoeUserId = getJohnDoeUserId(); + jdbcTemplate.update( + "INSERT INTO public.reserved (book_id, user_id, reserved_date) VALUES (?, ?, ?)", + overdueBookId, + johnDoeUserId, + Instant.now().plus(3, ChronoUnit.DAYS)); + + //when + OverdueReservation overdueReservation = database.findReservationsAfter(thirdDayAfterReservation).get(0); + + //then + assertEquals(overdueBookId, overdueReservation.getBookIdentificationAsLong()); + } + + private Long getHomoDeusBookId(){ + return jdbcTemplate.queryForObject( + "SELECT id FROM book WHERE title = ?", + Long.class, + BookTestData.homoDeusBookTitle()); + } + + private Long getJohnDoeUserId(){ + return jdbcTemplate.queryForObject( + "SELECT id FROM user WHERE email = ?", + Long.class, + UserTestData.johnDoeEmail()); + } }