Compare commits
15 Commits
add-new-bo
...
user
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d69df25e9 | ||
|
|
fa0e83ad92 | ||
|
|
93f2eed4c8 | ||
|
|
b2b1003b0b | ||
|
|
29f3997a98 | ||
|
|
727481a520 | ||
|
|
5f4809cfd4 | ||
|
|
c8b8c26d80 | ||
|
|
9e5ba16a1c | ||
|
|
3f07eac529 | ||
|
|
a234301f1d | ||
|
|
14441bfbd6 | ||
|
|
648b877b86 | ||
|
|
fbeac054cd | ||
|
|
c9de6efc28 |
5
pom.xml
5
pom.xml
@@ -20,7 +20,10 @@
|
||||
<spring-cloud.version>Hoxton.M3</spring-cloud.version>
|
||||
<surefire.and.failsafe.report.dir>target/test-report</surefire.and.failsafe.report.dir>
|
||||
<code.coverage.exclusions>
|
||||
**/io/wkrzywiec/hexagonal/library/domain/book/dto/**,
|
||||
**/io/wkrzywiec/hexagonal/library/borrowing/model/**,
|
||||
**/io/wkrzywiec/hexagonal/library/email/model/**,
|
||||
**/io/wkrzywiec/hexagonal/library/inventory/model/**,
|
||||
**/io/wkrzywiec/hexagonal/library/user/model/**,
|
||||
**/io/wkrzywiec/hexagonal/library/infrastructure/repository/*Entity.java
|
||||
</code.coverage.exclusions>
|
||||
</properties>
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
package io.wkrzywiec.hexagonal.library;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceContext;
|
||||
import javax.persistence.Table;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
//https://gist.github.com/JorgenRingen/a56837fc07e630c32280b8e3d14c2d24
|
||||
|
||||
@Service
|
||||
public class DatabaseCleanup implements InitializingBean {
|
||||
|
||||
@PersistenceContext
|
||||
private EntityManager entityManager;
|
||||
|
||||
private List<String> tableNames;
|
||||
|
||||
@Transactional
|
||||
public void execute() {
|
||||
entityManager.flush();
|
||||
entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY FALSE").executeUpdate();
|
||||
|
||||
for (final String tableName : tableNames) {
|
||||
entityManager.createNativeQuery("TRUNCATE TABLE " + tableName).executeUpdate();
|
||||
}
|
||||
|
||||
entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY TRUE").executeUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
tableNames = entityManager.getMetamodel().getEntities().stream()
|
||||
.filter(e -> e.getJavaType().getAnnotation(Table.class) != null)
|
||||
.map(e -> e.getJavaType().getAnnotation(Table.class).name())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
package io.wkrzywiec.hexagonal.library.borrowing;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.DatabaseCleanup;
|
||||
import io.wkrzywiec.hexagonal.library.TestData;
|
||||
import io.restassured.RestAssured;
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.model.BookReservationCommand;
|
||||
import io.wkrzywiec.hexagonal.library.inventory.infrastructure.BookRepository;
|
||||
import io.wkrzywiec.hexagonal.library.inventory.model.Book;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -13,9 +11,11 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class MakeReservationComponentTest {
|
||||
@@ -29,48 +29,30 @@ public class MakeReservationComponentTest {
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
private DatabaseCleanup databaseCleanup;
|
||||
|
||||
|
||||
private String baseURL;
|
||||
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
this.baseURL = "http://localhost:" + port;
|
||||
|
||||
Book book = bookRepository.save(TestData.homoDeusBook());
|
||||
jdbcTemplate.update(
|
||||
"INSERT INTO available (book_id) VALUES (?)",
|
||||
book.getIdAsLong());
|
||||
|
||||
jdbcTemplate.update(
|
||||
"INSERT INTO user (first_name, last_name, email) VALUES (?, ?, ?)",
|
||||
"John",
|
||||
"Doe",
|
||||
"john.doe@test.com");
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() {
|
||||
databaseCleanup.execute();
|
||||
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Reserve available book")
|
||||
@Sql({"/book-and-user.sql", "/available-book.sql"})
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenBookIsAvailable_thenMakeReservation_thenBookIsReserved() {
|
||||
//given
|
||||
Long homoDeusBookId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM book WHERE title = ?",
|
||||
Long.class,
|
||||
TestData.homoDeusBookTitle());
|
||||
BookTestData.homoDeusBookTitle());
|
||||
|
||||
Long activeUserId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM user WHERE email = ?",
|
||||
Long.class,
|
||||
"john.doe@test.com");
|
||||
|
||||
|
||||
BookReservationCommand reservationCommand =
|
||||
BookReservationCommand.builder()
|
||||
.bookId(homoDeusBookId )
|
||||
|
||||
@@ -2,10 +2,8 @@ package io.wkrzywiec.hexagonal.library.inventory;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.restassured.response.ValidatableResponse;
|
||||
import io.wkrzywiec.hexagonal.library.DatabaseCleanup;
|
||||
import io.wkrzywiec.hexagonal.library.TestData;
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import io.wkrzywiec.hexagonal.library.inventory.model.AddNewBookCommand;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -14,10 +12,12 @@ import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.hamcrest.Matchers.greaterThan;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class AddNewBookComponentTest {
|
||||
@@ -28,9 +28,6 @@ public class AddNewBookComponentTest {
|
||||
@Autowired
|
||||
private JdbcTemplate jdbc;
|
||||
|
||||
@Autowired
|
||||
private DatabaseCleanup databaseCleanup;
|
||||
|
||||
private String baseURL;
|
||||
|
||||
@BeforeEach
|
||||
@@ -39,11 +36,6 @@ public class AddNewBookComponentTest {
|
||||
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void after() {
|
||||
databaseCleanup.execute();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Search for a new book in Google Books")
|
||||
public void whenSearchForBook_thenGetList(){
|
||||
@@ -63,11 +55,12 @@ public class AddNewBookComponentTest {
|
||||
|
||||
@Test
|
||||
@DisplayName("Add new book to a database & make it available")
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenGoogleBooId_whenAddNewBook_thenBookIsSaved() {
|
||||
//given
|
||||
AddNewBookCommand addNewBookCommand =
|
||||
AddNewBookCommand.builder()
|
||||
.googleBookId(TestData.homoDeusBookGoogleId())
|
||||
.googleBookId(BookTestData.homoDeusBookGoogleId())
|
||||
.build();
|
||||
|
||||
//when
|
||||
@@ -83,7 +76,7 @@ public class AddNewBookComponentTest {
|
||||
Long savedBookId = jdbc.queryForObject(
|
||||
"SELECT id FROM book WHERE book_external_id = ?",
|
||||
Long.class,
|
||||
TestData.homoDeusBookGoogleId());
|
||||
BookTestData.homoDeusBookGoogleId());
|
||||
|
||||
assertTrue(savedBookId > 0);
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package io.wkrzywiec.hexagonal.library.user;
|
||||
|
||||
import io.restassured.RestAssured;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.AddUserCommand;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.web.server.LocalServerPort;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import static io.restassured.RestAssured.given;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
|
||||
|
||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
|
||||
public class AddNewUserComponentTest {
|
||||
|
||||
@LocalServerPort
|
||||
private int port;
|
||||
|
||||
private String baseURL;
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@BeforeEach
|
||||
public void init(){
|
||||
baseURL = "http://localhost:" + port;
|
||||
RestAssured.enableLoggingOfRequestAndResponseIfValidationFails();
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Create new user")
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void shouldCreateNewUser(){
|
||||
//given
|
||||
AddUserCommand addUserCommand = AddUserCommand.builder()
|
||||
.firstName("John")
|
||||
.lastName("Doe")
|
||||
.email("john.doe@test.com")
|
||||
.build();
|
||||
|
||||
//when
|
||||
given()
|
||||
.contentType("application/json")
|
||||
.body(addUserCommand)
|
||||
.when()
|
||||
.post( baseURL + "/users")
|
||||
.prettyPeek()
|
||||
.then();
|
||||
|
||||
//then
|
||||
Long savedUserId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM user WHERE email = ?",
|
||||
Long.class,
|
||||
"john.doe@test.com");
|
||||
|
||||
assertTrue(savedUserId > 0);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,4 +4,4 @@ spring:
|
||||
driverClassName: org.h2.Driver
|
||||
username: sa
|
||||
password: password
|
||||
jpa.database-platform: org.hibernate.dialect.H2Dialect
|
||||
jpa.database-platform: org.hibernate.dialect.H2Dialect
|
||||
@@ -2,13 +2,14 @@ package io.wkrzywiec.hexagonal.library;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.BorrowingFacade;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.infrastructure.BorrowingDatabaseAdapter;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.infrastructure.SpringBorrowingEventPublisherAdapter;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.ports.incoming.MakeBookAvailable;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.ports.outgoing.BorrowingDatabase;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.ports.outgoing.BorrowingEventPublisher;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
@Configuration
|
||||
public class BorrowingDomainConfig {
|
||||
|
||||
@Bean
|
||||
@@ -17,7 +18,12 @@ public class BorrowingDomainConfig {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MakeBookAvailable makeBookAvailable(BorrowingDatabase database) {
|
||||
return new BorrowingFacade(database);
|
||||
public BorrowingEventPublisher borrowingEventPublisher(ApplicationEventPublisher applicationEventPublisher){
|
||||
return new SpringBorrowingEventPublisherAdapter(applicationEventPublisher);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public MakeBookAvailable makeBookAvailable(BorrowingDatabase database, BorrowingEventPublisher borrowingEventPublisher) {
|
||||
return new BorrowingFacade(database, borrowingEventPublisher);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.wkrzywiec.hexagonal.library;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.email.EmailFacade;
|
||||
import io.wkrzywiec.hexagonal.library.email.infrastructure.EmailDatabaseAdapter;
|
||||
import io.wkrzywiec.hexagonal.library.email.infrastructure.SendGridEmailSender;
|
||||
import io.wkrzywiec.hexagonal.library.email.ports.incoming.SendReservationConfirmation;
|
||||
import io.wkrzywiec.hexagonal.library.email.ports.outgoing.EmailSender;
|
||||
import io.wkrzywiec.hexagonal.library.email.ports.outgoing.EmailDatabase;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
public class EmailDomainConfig {
|
||||
|
||||
@Bean
|
||||
public EmailSender emailSender() {
|
||||
return new SendGridEmailSender();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EmailDatabase libraryDatabase(JdbcTemplate jdbcTemplate){
|
||||
return new EmailDatabaseAdapter(jdbcTemplate);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SendReservationConfirmation sendReservationConfirmation(EmailSender emailSender, EmailDatabase database){
|
||||
return new EmailFacade(emailSender, database);
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,8 @@ import io.wkrzywiec.hexagonal.library.inventory.infrastructure.SpringInventoryEv
|
||||
import io.wkrzywiec.hexagonal.library.inventory.ports.incoming.AddNewBook;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Configuration
|
||||
class InventoryDomainConfig {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -2,8 +2,16 @@ package io.wkrzywiec.hexagonal.library;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
@SpringBootApplication
|
||||
@Import({
|
||||
LibraryHexagonalConfig.class,
|
||||
InventoryDomainConfig.class,
|
||||
BorrowingDomainConfig.class,
|
||||
EmailDomainConfig.class,
|
||||
UserDomainConfig.class
|
||||
})
|
||||
public class LibraryHexagonalApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
package io.wkrzywiec.hexagonal.library;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
@Configuration
|
||||
public class LibraryHexagonalConfig {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package io.wkrzywiec.hexagonal.library;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.user.UserFacade;
|
||||
import io.wkrzywiec.hexagonal.library.user.infrastructure.UserDatabaseAdapter;
|
||||
import io.wkrzywiec.hexagonal.library.user.infrastructure.UserRepository;
|
||||
import io.wkrzywiec.hexagonal.library.user.ports.incoming.AddNewUser;
|
||||
import io.wkrzywiec.hexagonal.library.user.ports.outgoing.UserDatabase;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
public class UserDomainConfig {
|
||||
|
||||
@Bean
|
||||
public UserDatabase userDatabase(UserRepository userRepository){
|
||||
return new UserDatabaseAdapter(userRepository);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public AddNewUser addNewUser(UserDatabase userDatabase){
|
||||
return new UserFacade(userDatabase);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,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.MakeBookAvailableCommand;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.model.ReservationDetails;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.model.exception.ActiveUserNotFoundException;
|
||||
@@ -11,13 +12,16 @@ import io.wkrzywiec.hexagonal.library.borrowing.model.ReservedBook;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.ports.incoming.MakeBookAvailable;
|
||||
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;
|
||||
|
||||
public class BorrowingFacade implements MakeBookAvailable, ReserveBook {
|
||||
|
||||
private BorrowingDatabase database;
|
||||
private final BorrowingDatabase database;
|
||||
private final BorrowingEventPublisher eventPublisher;
|
||||
|
||||
public BorrowingFacade(BorrowingDatabase database) {
|
||||
public BorrowingFacade(BorrowingDatabase database, BorrowingEventPublisher eventPublisher) {
|
||||
this.database = database;
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -37,6 +41,7 @@ public class BorrowingFacade implements MakeBookAvailable, ReserveBook {
|
||||
|
||||
ReservedBook reservedBook = activeUser.reserve(availableBook);
|
||||
ReservationDetails reservationDetails = database.save(reservedBook);
|
||||
eventPublisher.publish(new BookReservedEvent(reservationDetails));
|
||||
return reservationDetails.getReservationId().getIdAsLong();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import io.wkrzywiec.hexagonal.library.borrowing.model.ReservationDetails;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.model.ReservationId;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.model.ReservedBook;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.ports.outgoing.BorrowingDatabase;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
@@ -14,10 +14,10 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
@AllArgsConstructor
|
||||
@RequiredArgsConstructor
|
||||
public class BorrowingDatabaseAdapter implements BorrowingDatabase {
|
||||
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public void setBookAvailable(Long bookId) {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package io.wkrzywiec.hexagonal.library.borrowing.infrastructure;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.model.BookReservedEvent;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.ports.outgoing.BorrowingEventPublisher;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class SpringBorrowingEventPublisherAdapter implements BorrowingEventPublisher {
|
||||
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
|
||||
@Override
|
||||
public void publish(BookReservedEvent event) {
|
||||
eventPublisher.publishEvent(event);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package io.wkrzywiec.hexagonal.library.borrowing.model;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
public class BookReservedEvent {
|
||||
|
||||
private final ReservationId reservationId;
|
||||
private final Long userId;
|
||||
private final ReservedBook reservedBook;
|
||||
private final Instant timestamp;
|
||||
|
||||
public BookReservedEvent(ReservationDetails reservationDetails) {
|
||||
this.reservationId = reservationDetails.getReservationId();
|
||||
this.userId = reservationDetails.getReservedBook().getAssignedUserIdAsLong();
|
||||
this.reservedBook = reservationDetails.getReservedBook();
|
||||
timestamp = Instant.now();
|
||||
}
|
||||
|
||||
public Long getReservationIdAsLong() {
|
||||
return reservationId.getIdAsLong();
|
||||
}
|
||||
|
||||
public Long getUserIdAsLong() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public Long getBookIdAsLong() {
|
||||
return reservedBook.getIdAsLong();
|
||||
}
|
||||
|
||||
public String getEventTimeStampAsString() {
|
||||
return timestamp.toString();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package io.wkrzywiec.hexagonal.library.borrowing.ports.incoming;
|
||||
|
||||
public interface BorrowBook {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package io.wkrzywiec.hexagonal.library.borrowing.ports.incoming;
|
||||
|
||||
public interface CancelReservation {
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package io.wkrzywiec.hexagonal.library.borrowing.ports.incoming;
|
||||
|
||||
public interface GiveBackBook {
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.wkrzywiec.hexagonal.library.borrowing.ports.outgoing;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.model.BookReservedEvent;
|
||||
|
||||
public interface BorrowingEventPublisher {
|
||||
public void publish(BookReservedEvent event);
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package io.wkrzywiec.hexagonal.library.email;
|
||||
|
||||
public class EmailContent {
|
||||
}
|
||||
@@ -1,10 +1,18 @@
|
||||
package io.wkrzywiec.hexagonal.library.email;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.email.model.EmailAddress;
|
||||
import io.wkrzywiec.hexagonal.library.email.model.ReservationConfirmEmail;
|
||||
|
||||
class EmailCreator {
|
||||
|
||||
// EmailContent prepareEmailContent() {
|
||||
// // email to
|
||||
// // email from
|
||||
// //
|
||||
// }
|
||||
static ReservationConfirmEmail reservationEmail(Long reservationId, String bookTitle, String emailTo){
|
||||
|
||||
EmailAddress from = new EmailAddress("tom@library.com");
|
||||
EmailAddress to = new EmailAddress(emailTo);
|
||||
|
||||
String subject = String.format("Library - book reservation confirmation (id - %d)", reservationId);
|
||||
String content = String.format("Dear reader,%n you have reserved a %s book which will be waiting for you in our library for next 2 days. Your reservation id is %d. %n Have a nice day, %n Library",
|
||||
bookTitle, reservationId);
|
||||
return new ReservationConfirmEmail(from, to, subject, content);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.wkrzywiec.hexagonal.library.email;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.email.model.ReservationConfirmEmail;
|
||||
import io.wkrzywiec.hexagonal.library.email.model.SendReservationConfirmationCommand;
|
||||
import io.wkrzywiec.hexagonal.library.email.ports.incoming.SendReservationConfirmation;
|
||||
import io.wkrzywiec.hexagonal.library.email.ports.outgoing.EmailSender;
|
||||
import io.wkrzywiec.hexagonal.library.email.ports.outgoing.EmailDatabase;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class EmailFacade implements SendReservationConfirmation {
|
||||
|
||||
private final EmailSender emailSender;
|
||||
private final EmailDatabase database;
|
||||
|
||||
@Override
|
||||
public void handle(SendReservationConfirmationCommand sendReservationConfirmation) {
|
||||
String bookTitle = database
|
||||
.getTitleByBookId(sendReservationConfirmation.getBookId())
|
||||
.orElseThrow(() -> new IllegalArgumentException("Can't get book title from database. Reason: there is no book with an id: " + sendReservationConfirmation.getBookId()));
|
||||
String userEmailAddress = database
|
||||
.getUserEmailAddress(sendReservationConfirmation.getUserId())
|
||||
.orElseThrow(() -> new IllegalArgumentException("Can't get email address from database. Reason: there is no user with an id: " + sendReservationConfirmation.getUserId()));
|
||||
|
||||
ReservationConfirmEmail reservationConfirmEmail = EmailCreator.reservationEmail(
|
||||
sendReservationConfirmation.getReservationId(),
|
||||
bookTitle,
|
||||
userEmailAddress
|
||||
);
|
||||
emailSender.sendReservationConfirmationEmail(reservationConfirmEmail);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package io.wkrzywiec.hexagonal.library.email;
|
||||
|
||||
|
||||
public interface EmailSender {
|
||||
// void sendReservationConfirmationEmail(ReservationDetails reservationDetails);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
package io.wkrzywiec.hexagonal.library.email;
|
||||
|
||||
//import io.wkrzywiec.hexagonal.library.borrowing.model.ReservationDetails;
|
||||
|
||||
public class SendGridEmailSender implements EmailSender {
|
||||
|
||||
// @Override
|
||||
// public void sendReservationConfirmationEmail(ReservationDetails reservationDetails) {
|
||||
// email address - to
|
||||
// repl9oservation id
|
||||
// book title`
|
||||
|
||||
// Email from = new Email("test@example.com");
|
||||
// String subject = "Sending with SendGrid is Fun";
|
||||
// Email to = new Email("test@example.com");
|
||||
// Content content = new Content("text/plain", "and easy to do anywhere, even with Java");
|
||||
// Mail mail = new Mail(from, subject, to, content);
|
||||
//
|
||||
// SendGrid sg = new SendGrid(System.getenv("SENDGRID_API_KEY"));
|
||||
// Request request = new Request();
|
||||
// try {
|
||||
// request.setMethod(Method.POST);
|
||||
// request.setEndpoint("mail/send");
|
||||
// request.setBody(mail.build());
|
||||
// Response response = sg.api(request);
|
||||
// System.out.println(response.getStatusCode());
|
||||
// System.out.println(response.getBody());
|
||||
// System.out.println(response.getHeaders());
|
||||
// } catch (IOException ex) {
|
||||
//
|
||||
// }
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.wkrzywiec.hexagonal.library.email.application;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.model.BookReservedEvent;
|
||||
import io.wkrzywiec.hexagonal.library.email.model.SendReservationConfirmationCommand;
|
||||
import io.wkrzywiec.hexagonal.library.email.ports.incoming.SendReservationConfirmation;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.context.event.EventListener;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class BookReservedEventHandler {
|
||||
|
||||
private final SendReservationConfirmation sendReservationConfirmation;
|
||||
|
||||
@EventListener
|
||||
public void handle(BookReservedEvent event) {
|
||||
sendReservationConfirmation.handle(
|
||||
new SendReservationConfirmationCommand(
|
||||
event.getReservationIdAsLong(),
|
||||
event.getUserIdAsLong(),
|
||||
event.getBookIdAsLong()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.wkrzywiec.hexagonal.library.email.infrastructure;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.email.ports.outgoing.EmailDatabase;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class EmailDatabaseAdapter implements EmailDatabase {
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Override
|
||||
public Optional<String> getTitleByBookId(Long bookId) {
|
||||
try {
|
||||
return Optional.ofNullable(jdbcTemplate.queryForObject(
|
||||
"SELECT title FROM book WHERE id = ?",
|
||||
String.class,
|
||||
bookId));
|
||||
} catch (DataAccessException ex){
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getUserEmailAddress(Long userId) {
|
||||
try {
|
||||
return Optional.ofNullable(jdbcTemplate.queryForObject(
|
||||
"SELECT email FROM user WHERE id = ?",
|
||||
String.class,
|
||||
userId));
|
||||
} catch (DataAccessException ex){
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package io.wkrzywiec.hexagonal.library.email.infrastructure;
|
||||
|
||||
import com.sendgrid.Method;
|
||||
import com.sendgrid.Request;
|
||||
import com.sendgrid.SendGrid;
|
||||
import com.sendgrid.helpers.mail.Mail;
|
||||
import com.sendgrid.helpers.mail.objects.Content;
|
||||
import com.sendgrid.helpers.mail.objects.Email;
|
||||
import io.wkrzywiec.hexagonal.library.email.model.ReservationConfirmEmail;
|
||||
import io.wkrzywiec.hexagonal.library.email.ports.outgoing.EmailSender;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SendGridEmailSender implements EmailSender {
|
||||
|
||||
@Override
|
||||
public void sendReservationConfirmationEmail(ReservationConfirmEmail reservationConfirmEmail) {
|
||||
Email from = new Email(reservationConfirmEmail.getFromEmailAddressAsString());
|
||||
Email to = new Email(reservationConfirmEmail.getToEmailAddressAsString());
|
||||
Content content = new Content("text/plain", reservationConfirmEmail.getContentAsString());
|
||||
Mail mail = new Mail(
|
||||
from,
|
||||
reservationConfirmEmail.getSubjectAsString(),
|
||||
to,
|
||||
content);
|
||||
|
||||
SendGrid sg = new SendGrid(System.getenv("SENDGRID_API_KEY"));
|
||||
Request request = new Request();
|
||||
try {
|
||||
request.setMethod(Method.POST);
|
||||
request.setEndpoint("mail/send");
|
||||
request.setBody(mail.build());
|
||||
sg.api(request);
|
||||
} catch (IOException ex) {
|
||||
System.out.print(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package io.wkrzywiec.hexagonal.library.email.model;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class EmailAddress {
|
||||
|
||||
private final String value;
|
||||
|
||||
public EmailAddress(String value) {
|
||||
Pattern pattern = Pattern.compile("^(.+)@(.+)$");
|
||||
Matcher matcher = pattern.matcher(value);
|
||||
if(matcher.matches()){
|
||||
this.value = value;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Provided value is not an email address");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getAsString() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.wkrzywiec.hexagonal.library.email.model;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class ReservationConfirmEmail {
|
||||
|
||||
private final EmailAddress from;
|
||||
private final EmailAddress to;
|
||||
private final String subject;
|
||||
private final String content;
|
||||
|
||||
public String getFromEmailAddressAsString(){
|
||||
return this.from.getAsString();
|
||||
}
|
||||
|
||||
public String getToEmailAddressAsString(){
|
||||
return this.to.getAsString();
|
||||
}
|
||||
|
||||
public String getSubjectAsString(){
|
||||
return this.subject;
|
||||
}
|
||||
|
||||
public String getContentAsString(){
|
||||
return this.content;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.wkrzywiec.hexagonal.library.email.model;
|
||||
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter
|
||||
public class SendReservationConfirmationCommand {
|
||||
|
||||
private final Long reservationId;
|
||||
private final Long userId;
|
||||
private final Long bookId;
|
||||
|
||||
public SendReservationConfirmationCommand(Long reservationId, Long userId, Long bookId) {
|
||||
this.reservationId = reservationId;
|
||||
this.userId = userId;
|
||||
this.bookId = bookId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package io.wkrzywiec.hexagonal.library.email.ports.incoming;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.email.model.SendReservationConfirmationCommand;
|
||||
|
||||
public interface SendReservationConfirmation {
|
||||
void handle(SendReservationConfirmationCommand reservationConfirmationCommand);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.wkrzywiec.hexagonal.library.email.ports.outgoing;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface EmailDatabase {
|
||||
Optional<String> getTitleByBookId(Long bookId);
|
||||
Optional<String> getUserEmailAddress(Long userId);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.wkrzywiec.hexagonal.library.email.ports.outgoing;
|
||||
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.email.model.ReservationConfirmEmail;
|
||||
|
||||
public interface EmailSender {
|
||||
void sendReservationConfirmationEmail(ReservationConfirmEmail reservationConfirmEmail);
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.wkrzywiec.hexagonal.library.user;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.user.model.AddUserCommand;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.EmailAddress;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.User;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.UserIdentifier;
|
||||
import io.wkrzywiec.hexagonal.library.user.ports.incoming.AddNewUser;
|
||||
import io.wkrzywiec.hexagonal.library.user.ports.outgoing.UserDatabase;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class UserFacade implements AddNewUser {
|
||||
|
||||
private final UserDatabase database;
|
||||
|
||||
@Override
|
||||
public UserIdentifier handle(AddUserCommand addUserCommand) {
|
||||
User user = new User(
|
||||
new EmailAddress(addUserCommand.getEmail()),
|
||||
addUserCommand.getFirstName(),
|
||||
addUserCommand.getLastName()
|
||||
);
|
||||
return database.save(user);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package io.wkrzywiec.hexagonal.library.user.application;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.user.model.AddUserCommand;
|
||||
import io.wkrzywiec.hexagonal.library.user.ports.incoming.AddNewUser;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/users")
|
||||
@RequiredArgsConstructor
|
||||
public class UserCommandController {
|
||||
|
||||
private final AddNewUser addNewUser;
|
||||
|
||||
@PostMapping("")
|
||||
public ResponseEntity<String> addNewUser(@RequestBody AddUserCommand addUserCommand){
|
||||
addNewUser.handle(addUserCommand);
|
||||
return new ResponseEntity<>("New user was added to library", HttpStatus.CREATED);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package io.wkrzywiec.hexagonal.library.user.infrastructure;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.user.model.User;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.UserIdentifier;
|
||||
import io.wkrzywiec.hexagonal.library.user.ports.outgoing.UserDatabase;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public class UserDatabaseAdapter implements UserDatabase {
|
||||
|
||||
private final UserRepository userRepository;
|
||||
|
||||
@Override
|
||||
public UserIdentifier save(User user) {
|
||||
User savedUser = userRepository.save(user);
|
||||
return new UserIdentifier(savedUser.getIdentifierAsLong());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
package io.wkrzywiec.hexagonal.library.user.infrastructure;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.user.model.User;
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
@Repository
|
||||
public interface UserRepository extends CrudRepository<User, Long> {
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package io.wkrzywiec.hexagonal.library.user.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@Builder
|
||||
public class AddUserCommand {
|
||||
|
||||
private String email;
|
||||
private String firstName;
|
||||
private String lastName;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package io.wkrzywiec.hexagonal.library.user.model;
|
||||
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embeddable;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@Embeddable
|
||||
@EqualsAndHashCode
|
||||
public class EmailAddress {
|
||||
|
||||
@Column(name="email")
|
||||
private String value;
|
||||
|
||||
public EmailAddress(String value) {
|
||||
Pattern pattern = Pattern.compile("^(.+)@(.+)$");
|
||||
Matcher matcher = pattern.matcher(value);
|
||||
if(matcher.matches()){
|
||||
this.value = value;
|
||||
} else {
|
||||
throw new IllegalArgumentException("Provided value is not an email address");
|
||||
}
|
||||
}
|
||||
|
||||
private EmailAddress(){}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package io.wkrzywiec.hexagonal.library.user.model;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import javax.persistence.Column;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.GenerationType;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.Table;
|
||||
|
||||
@Entity
|
||||
@Table(name="user")
|
||||
@EqualsAndHashCode
|
||||
public class User {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@EqualsAndHashCode.Exclude
|
||||
private Long id;
|
||||
|
||||
@Embedded
|
||||
private EmailAddress emailAddress;
|
||||
|
||||
@Column(name="first_name")
|
||||
private String firstName;
|
||||
|
||||
@Column(name="last_name")
|
||||
private String lastName;
|
||||
|
||||
public User(EmailAddress emailAddress, String firstName, String lastName) {
|
||||
this.emailAddress = emailAddress;
|
||||
this.firstName = firstName;
|
||||
this.lastName = lastName;
|
||||
}
|
||||
|
||||
public Long getIdentifierAsLong(){
|
||||
return id;
|
||||
}
|
||||
|
||||
private User(){}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package io.wkrzywiec.hexagonal.library.user.model;
|
||||
|
||||
|
||||
public class UserIdentifier {
|
||||
|
||||
private final Long id;
|
||||
|
||||
public UserIdentifier(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getAsLong(){
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.wkrzywiec.hexagonal.library.user.ports.incoming;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.user.model.AddUserCommand;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.UserIdentifier;
|
||||
|
||||
public interface AddNewUser {
|
||||
UserIdentifier handle(AddUserCommand addUserCommand);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.wkrzywiec.hexagonal.library.user.ports.outgoing;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.user.model.User;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.UserIdentifier;
|
||||
|
||||
public interface UserDatabase {
|
||||
UserIdentifier save(User user);
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import io.wkrzywiec.hexagonal.library.inventory.model.Isbn13;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class TestData {
|
||||
public class BookTestData {
|
||||
|
||||
public static String homoDeusBookGoogleId() {
|
||||
return "dWYyCwAAQBAJ";
|
||||
@@ -0,0 +1,8 @@
|
||||
package io.wkrzywiec.hexagonal.library;
|
||||
|
||||
public class UserTestData {
|
||||
|
||||
public static String johnDoeEmail(){
|
||||
return "john.doe@test.com";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.wkrzywiec.hexagonal.library.architecture;
|
||||
|
||||
import com.tngtech.archunit.core.importer.ImportOption;
|
||||
import com.tngtech.archunit.junit.AnalyzeClasses;
|
||||
import com.tngtech.archunit.junit.ArchTest;
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
import io.wkrzywiec.hexagonal.library.email.EmailFacade;
|
||||
|
||||
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClass;
|
||||
import static com.tngtech.archunit.library.Architectures.onionArchitecture;
|
||||
|
||||
@AnalyzeClasses(packages = {"io.wkrzywiec.hexagonal.library.email"},
|
||||
importOptions = { ImportOption.DoNotIncludeTests.class })
|
||||
public class EmailArchitectureTest {
|
||||
|
||||
@ArchTest
|
||||
public static final ArchRule hexagonalArchInEmailDomain = onionArchitecture()
|
||||
.domainModels("io.wkrzywiec.hexagonal.library.email.model..")
|
||||
.domainServices("io.wkrzywiec.hexagonal.library.email..")
|
||||
.applicationServices("io.wkrzywiec.hexagonal.library.email.application..")
|
||||
.adapter("infrastructure", "io.wkrzywiec.hexagonal.library.email.infrastructure..");
|
||||
|
||||
@ArchTest
|
||||
public static final ArchRule noSpringDependenciesInEmailFacade =
|
||||
noClass(EmailFacade.class)
|
||||
.should()
|
||||
.dependOnClassesThat()
|
||||
.resideInAPackage("org.springframework..");
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package io.wkrzywiec.hexagonal.library.architecture;
|
||||
|
||||
import com.tngtech.archunit.core.importer.ImportOption;
|
||||
import com.tngtech.archunit.junit.AnalyzeClasses;
|
||||
import com.tngtech.archunit.junit.ArchTest;
|
||||
import com.tngtech.archunit.lang.ArchRule;
|
||||
import io.wkrzywiec.hexagonal.library.user.UserFacade;
|
||||
|
||||
import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.noClass;
|
||||
import static com.tngtech.archunit.library.Architectures.onionArchitecture;
|
||||
|
||||
@AnalyzeClasses(packages = {"io.wkrzywiec.hexagonal.library.user"},
|
||||
importOptions = { ImportOption.DoNotIncludeTests.class })
|
||||
public class UserArchitectureTest {
|
||||
|
||||
@ArchTest
|
||||
public static final ArchRule hexagonalArchInUserDomain = onionArchitecture()
|
||||
.domainModels("io.wkrzywiec.hexagonal.library.user.model..")
|
||||
.domainServices("io.wkrzywiec.hexagonal.library.user..")
|
||||
.applicationServices("io.wkrzywiec.hexagonal.library.user.application..")
|
||||
.adapter("infrastructure", "io.wkrzywiec.hexagonal.library.user.infrastructure..");
|
||||
|
||||
@ArchTest
|
||||
public static final ArchRule noSpringDependenciesInUserFacade =
|
||||
noClass(UserFacade.class)
|
||||
.should()
|
||||
.dependOnClassesThat()
|
||||
.resideInAPackage("org.springframework..");
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.wkrzywiec.hexagonal.library.borrowing;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.model.BookReservedEvent;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.ports.outgoing.BorrowingEventPublisher;
|
||||
|
||||
public class BorrowingEventPublisherFake implements BorrowingEventPublisher {
|
||||
|
||||
@Override
|
||||
public void publish(BookReservedEvent event) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import io.wkrzywiec.hexagonal.library.borrowing.model.MakeBookAvailableCommand;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.model.exception.ActiveUserNotFoundException;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.model.exception.AvailableBookNotFoundExeption;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.model.exception.TooManyBooksAssignedToUserException;
|
||||
import io.wkrzywiec.hexagonal.library.borrowing.ports.outgoing.BorrowingEventPublisher;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -19,11 +20,13 @@ public class BorrowingFacadeTest {
|
||||
|
||||
private BorrowingFacade facade;
|
||||
private InMemoryBorrowingDatabase database;
|
||||
private BorrowingEventPublisher eventPublisher;
|
||||
|
||||
@BeforeEach
|
||||
public void init(){
|
||||
database = new InMemoryBorrowingDatabase();
|
||||
facade = new BorrowingFacade(database);
|
||||
eventPublisher = new BorrowingEventPublisherFake();
|
||||
facade = new BorrowingFacade(database, eventPublisher);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.wkrzywiec.hexagonal.library.borrowing.infrastructure;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.TestData;
|
||||
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.ReservationDetails;
|
||||
@@ -9,15 +10,17 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.jdbc.JdbcTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
|
||||
|
||||
@JdbcTest
|
||||
@SpringBootTest
|
||||
public class BorrowingDatabaseAdapterITCase {
|
||||
|
||||
@Autowired
|
||||
@@ -28,27 +31,18 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
@BeforeEach
|
||||
public void init(){
|
||||
database = new BorrowingDatabaseAdapter(jdbcTemplate);
|
||||
|
||||
jdbcTemplate.update(
|
||||
"INSERT INTO book (book_external_id, title) VALUES (?, ?)",
|
||||
TestData.homoDeusBookGoogleId(),
|
||||
TestData.homoDeusBookTitle());
|
||||
|
||||
jdbcTemplate.update(
|
||||
"INSERT INTO user (first_name, last_name, email) VALUES (?, ?, ?)",
|
||||
"John",
|
||||
"Doe",
|
||||
"john.doe@test.com");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Save book as available")
|
||||
@Sql("/book-and-user.sql")
|
||||
@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,
|
||||
TestData.homoDeusBookTitle());
|
||||
BookTestData.homoDeusBookTitle());
|
||||
|
||||
//when
|
||||
database.setBookAvailable(bookId);
|
||||
@@ -63,16 +57,14 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
|
||||
@Test
|
||||
@DisplayName("Get available book by id")
|
||||
@Sql({"/book-and-user.sql", "/available-book.sql"})
|
||||
@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,
|
||||
TestData.homoDeusBookTitle());
|
||||
|
||||
jdbcTemplate.update(
|
||||
"INSERT INTO available (book_id) VALUES (?)",
|
||||
bookId);
|
||||
BookTestData.homoDeusBookTitle());
|
||||
|
||||
//when
|
||||
Optional<AvailableBook> availableBookOptional = database.getAvailableBook(bookId);
|
||||
@@ -84,12 +76,14 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
|
||||
@Test
|
||||
@DisplayName("Get active user by id")
|
||||
@Sql("/book-and-user.sql")
|
||||
@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,
|
||||
"john.doe@test.com");
|
||||
UserTestData.johnDoeEmail());
|
||||
|
||||
//when
|
||||
Optional<ActiveUser> activeUserOptional = database.getActiveUser(activeUserId);
|
||||
@@ -101,17 +95,19 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
|
||||
@Test
|
||||
@DisplayName("Save reserved book")
|
||||
@Sql({"/book-and-user.sql", "/available-book.sql"})
|
||||
@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,
|
||||
TestData.homoDeusBookTitle());
|
||||
BookTestData.homoDeusBookTitle());
|
||||
|
||||
Long activeUserId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM user WHERE email = ?",
|
||||
Long.class,
|
||||
"john.doe@test.com");
|
||||
UserTestData.johnDoeEmail());
|
||||
|
||||
ReservedBook reservedBook = new ReservedBook(bookId, activeUserId);
|
||||
|
||||
@@ -119,7 +115,6 @@ public class BorrowingDatabaseAdapterITCase {
|
||||
ReservationDetails reservationDetails = database.save(reservedBook);
|
||||
|
||||
//then
|
||||
|
||||
assertEquals(bookId, reservationDetails.getReservedBook().getIdAsLong());
|
||||
assertEquals(activeUserId, reservationDetails.getReservedBook().getAssignedUserIdAsLong());
|
||||
assertTrue(reservationDetails.getReservationId().getIdAsLong() > 0);
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package io.wkrzywiec.hexagonal.library.email;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import io.wkrzywiec.hexagonal.library.UserTestData;
|
||||
import io.wkrzywiec.hexagonal.library.email.model.SendReservationConfirmationCommand;
|
||||
import io.wkrzywiec.hexagonal.library.email.ports.outgoing.EmailSender;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
public class EmailFacadeTest {
|
||||
|
||||
private EmailFacade facade;
|
||||
private EmailSender emailSender;
|
||||
private InMemoryEmailDatabase database;
|
||||
|
||||
|
||||
@BeforeEach
|
||||
public void init() {
|
||||
database = new InMemoryEmailDatabase();
|
||||
emailSender = new EmailSenderFake();
|
||||
facade = new EmailFacade(emailSender, database);
|
||||
|
||||
database.bookTitles.put(1L, BookTestData.homoDeusBookTitle());
|
||||
database.emailAddresses.put(1L, UserTestData.johnDoeEmail());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Prepare & send reservation confirmation email")
|
||||
public void shouldPrepareAndSendReservationConfirmation(){
|
||||
//given
|
||||
SendReservationConfirmationCommand sendReservationConfirmationCommand
|
||||
= new SendReservationConfirmationCommand(1L, 1L, 1L);
|
||||
|
||||
//when & then
|
||||
assertDoesNotThrow(() -> facade.handle(sendReservationConfirmationCommand));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package io.wkrzywiec.hexagonal.library.email;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.email.model.ReservationConfirmEmail;
|
||||
import io.wkrzywiec.hexagonal.library.email.ports.outgoing.EmailSender;
|
||||
|
||||
public class EmailSenderFake implements EmailSender {
|
||||
|
||||
@Override
|
||||
public void sendReservationConfirmationEmail(ReservationConfirmEmail reservationConfirmEmail) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package io.wkrzywiec.hexagonal.library.email;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.email.ports.outgoing.EmailDatabase;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class InMemoryEmailDatabase implements EmailDatabase {
|
||||
|
||||
ConcurrentHashMap<Long, String> bookTitles = new ConcurrentHashMap<>();
|
||||
ConcurrentHashMap<Long, String> emailAddresses = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public Optional<String> getTitleByBookId(Long bookId) {
|
||||
return Optional.of(bookTitles.get(bookId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<String> getUserEmailAddress(Long userId) {
|
||||
return Optional.of(emailAddresses.get(userId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package io.wkrzywiec.hexagonal.library.email.infrastructure;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import io.wkrzywiec.hexagonal.library.UserTestData;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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 org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
|
||||
|
||||
@SpringBootTest
|
||||
public class EmailDatabaseAdapterITCase {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
private EmailDatabaseAdapter emailDatabase;
|
||||
|
||||
@BeforeEach
|
||||
public void init(){
|
||||
emailDatabase = new EmailDatabaseAdapter(jdbcTemplate);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Get book title from db by its id")
|
||||
@Sql({"/book-and-user.sql"})
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenBookId_whenGetBookTitle_thenGetBookTitle() {
|
||||
//given
|
||||
Long bookId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM book WHERE title = ?",
|
||||
Long.class,
|
||||
BookTestData.homoDeusBookTitle());
|
||||
|
||||
//when
|
||||
Optional<String> bookTitle = emailDatabase.getTitleByBookId(bookId);
|
||||
|
||||
//then
|
||||
assertEquals(Optional.of(BookTestData.homoDeusBookTitle()), bookTitle);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Get empty result when book is not in db")
|
||||
@Sql({"/book-and-user.sql"})
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenWrongBookId_whenGetBookTitle_thenGetEmptyResult() {
|
||||
//given
|
||||
Long bookId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM book WHERE title = ?",
|
||||
Long.class,
|
||||
BookTestData.homoDeusBookTitle());
|
||||
|
||||
//when
|
||||
Optional<String> bookTitle = emailDatabase.getTitleByBookId(bookId + 1124);
|
||||
|
||||
//then
|
||||
assertEquals(Optional.empty(), bookTitle);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Get email from db by user id")
|
||||
@Sql({"/book-and-user.sql"})
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenUserId_whenGetEmail_thenGetEmailAddress() {
|
||||
//given
|
||||
Long userId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM user WHERE email = ?",
|
||||
Long.class,
|
||||
UserTestData.johnDoeEmail());
|
||||
|
||||
//when
|
||||
Optional<String> emailAddress = emailDatabase.getUserEmailAddress(userId);
|
||||
|
||||
//then
|
||||
assertEquals(Optional.of(UserTestData.johnDoeEmail()), emailAddress);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Get empty result when book is not in db")
|
||||
@Sql({"/book-and-user.sql"})
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenWrongUserId_whenGetEmail_thenGetEmptyResult() {
|
||||
//given
|
||||
Long userId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM user WHERE email = ?",
|
||||
Long.class,
|
||||
UserTestData.johnDoeEmail());
|
||||
|
||||
//when
|
||||
Optional<String> emailAddress = emailDatabase.getUserEmailAddress(userId + 1124);
|
||||
|
||||
//then
|
||||
assertEquals(Optional.empty(), emailAddress);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
package io.wkrzywiec.hexagonal.library.email.model;
|
||||
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
public class EmailAddressTest {
|
||||
|
||||
|
||||
@Test
|
||||
@DisplayName("Create correct EmailAddress")
|
||||
public void givenCorrectEmailString_whenCreateEmailAddress_thenIsCreated(){
|
||||
//given
|
||||
String emailString = "john.doe@test.com";
|
||||
|
||||
//when
|
||||
EmailAddress emailAddress = new EmailAddress(emailString);
|
||||
|
||||
//then
|
||||
assertEquals(emailString, emailAddress.getAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Throw IllegalArgument exception for incorrect email")
|
||||
public void givenInCorrectEmailString_whenCreateEmailAddress_thenThrowException(){
|
||||
//given
|
||||
String notAnEmailString = "not an email";
|
||||
String emailWithoutAt = "tom[at]test.com";
|
||||
String emailWithoutDomain = "tom@";
|
||||
|
||||
//when & then
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> new EmailAddress(notAnEmailString));
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> new EmailAddress(emailWithoutAt));
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> new EmailAddress(emailWithoutDomain));
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.wkrzywiec.hexagonal.library.inventory;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.TestData;
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import io.wkrzywiec.hexagonal.library.inventory.model.Book;
|
||||
import io.wkrzywiec.hexagonal.library.inventory.ports.outgoing.GetBookDetails;
|
||||
|
||||
@@ -14,8 +14,8 @@ public class GetBookDetailsFake implements GetBookDetails {
|
||||
public GetBookDetailsFake() {
|
||||
books = new HashMap<String, Book>();
|
||||
books.put(
|
||||
TestData.homoDeusBookGoogleId(),
|
||||
TestData.homoDeusBook());
|
||||
BookTestData.homoDeusBookGoogleId(),
|
||||
BookTestData.homoDeusBook());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.wkrzywiec.hexagonal.library.inventory;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.TestData;
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import io.wkrzywiec.hexagonal.library.inventory.model.AddNewBookCommand;
|
||||
import io.wkrzywiec.hexagonal.library.inventory.model.Book;
|
||||
import io.wkrzywiec.hexagonal.library.inventory.ports.outgoing.GetBookDetails;
|
||||
@@ -33,7 +33,7 @@ public class InventoryFacadeTest {
|
||||
//given
|
||||
AddNewBookCommand externalBookId = AddNewBookCommand
|
||||
.builder()
|
||||
.googleBookId(TestData.homoDeusBookGoogleId())
|
||||
.googleBookId(BookTestData.homoDeusBookGoogleId())
|
||||
.build();
|
||||
|
||||
//when
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package io.wkrzywiec.hexagonal.library.inventory.infrastructure;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.TestData;
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import io.wkrzywiec.hexagonal.library.inventory.model.Book;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
@@ -31,14 +31,14 @@ public class GoogleBooksAdapterITCase {
|
||||
@DisplayName("Get book details from Google Books")
|
||||
public void givenCorrectBookId_whenGetBookDetails_thenReturnBookDetailsDTO(){
|
||||
//given
|
||||
String homoDeusResponse = TestData.homoDeusGooleBooksResponse();
|
||||
Book homoDeusBook = TestData.homoDeusBook();
|
||||
String homoDeusResponse = BookTestData.homoDeusGooleBooksResponse();
|
||||
Book homoDeusBook = BookTestData.homoDeusBook();
|
||||
server.expect(requestTo(
|
||||
"https://www.googleapis.com/books/v1/volumes/" + TestData.homoDeusBookGoogleId()))
|
||||
"https://www.googleapis.com/books/v1/volumes/" + BookTestData.homoDeusBookGoogleId()))
|
||||
.andRespond(withSuccess(homoDeusResponse, MediaType.APPLICATION_JSON));
|
||||
|
||||
//when
|
||||
Book actualBook= googleBooks.handle(TestData.homoDeusBookGoogleId());
|
||||
Book actualBook= googleBooks.handle(BookTestData.homoDeusBookGoogleId());
|
||||
|
||||
//then
|
||||
assertEquals(homoDeusBook, actualBook);
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
package io.wkrzywiec.hexagonal.library.inventory.infrastructure;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.TestData;
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import io.wkrzywiec.hexagonal.library.inventory.model.Book;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
|
||||
|
||||
@DataJpaTest
|
||||
@SpringBootTest
|
||||
public class InventoryDatabaseAdapterITCase {
|
||||
|
||||
@Autowired
|
||||
@@ -29,9 +31,10 @@ public class InventoryDatabaseAdapterITCase {
|
||||
|
||||
@Test
|
||||
@DisplayName("Save new book in database")
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void givenBook_whenSaveIt_thenBookIsSaved() {
|
||||
//given
|
||||
Book homoDeusBook = TestData.homoDeusBook();
|
||||
Book homoDeusBook = BookTestData.homoDeusBook();
|
||||
|
||||
//when
|
||||
Book savedBook = database.save(homoDeusBook);
|
||||
|
||||
@@ -2,7 +2,7 @@ package io.wkrzywiec.hexagonal.library.query;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import io.wkrzywiec.hexagonal.library.TestData;
|
||||
import io.wkrzywiec.hexagonal.library.BookTestData;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@@ -32,7 +32,7 @@ public class GoogleBookSearchClientITCase {
|
||||
@DisplayName("Search for a book")
|
||||
public void whenSearchForBooks_thenGetListOfBooks(){
|
||||
//given
|
||||
String harryPotterSearchResponse = TestData.harryPotterSearchResponse();
|
||||
String harryPotterSearchResponse = BookTestData.harryPotterSearchResponse();
|
||||
server.expect(requestTo(
|
||||
"https://www.googleapis.com/books/v1/volumes?langRestrict=en&maxResults=40&printType=books&q=" + "harry%20potter"))
|
||||
.andRespond(withSuccess(harryPotterSearchResponse, MediaType.APPLICATION_JSON));
|
||||
@@ -46,7 +46,7 @@ public class GoogleBookSearchClientITCase {
|
||||
@DisplayName("Search for a book and get empty result")
|
||||
public void whenSearchForBooks_thenGetEmptyResult(){
|
||||
//given
|
||||
String noBooksResponse = TestData.noBooksSearchResponse();
|
||||
String noBooksResponse = BookTestData.noBooksSearchResponse();
|
||||
server.expect(requestTo(
|
||||
"https://www.googleapis.com/books/v1/volumes?langRestrict=en&maxResults=40&printType=books&q=" + "djfjbasdknl"))
|
||||
.andRespond(withSuccess(noBooksResponse, MediaType.APPLICATION_JSON));
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package io.wkrzywiec.hexagonal.library.user;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.user.model.User;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.UserIdentifier;
|
||||
import io.wkrzywiec.hexagonal.library.user.ports.outgoing.UserDatabase;
|
||||
import org.apache.commons.lang3.reflect.FieldUtils;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class InMemoryUserDatabase implements UserDatabase {
|
||||
|
||||
ConcurrentHashMap<Long, User> users = new ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public UserIdentifier save(User user) {
|
||||
Long id = users.size() + 1L;
|
||||
|
||||
try {
|
||||
FieldUtils.writeField(user, "id", id, true);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
users.put(id, user);
|
||||
return new UserIdentifier(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package io.wkrzywiec.hexagonal.library.user;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.UserTestData;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.AddUserCommand;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.EmailAddress;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.User;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.UserIdentifier;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class UserFacadeTest {
|
||||
|
||||
private InMemoryUserDatabase database;
|
||||
private UserFacade facade;
|
||||
|
||||
@BeforeEach
|
||||
public void init(){
|
||||
database = new InMemoryUserDatabase();
|
||||
facade = new UserFacade(database);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Add new user")
|
||||
public void shouldAddNewUser(){
|
||||
//given
|
||||
User expectedUser = new User(
|
||||
new EmailAddress(UserTestData.johnDoeEmail()),
|
||||
"John",
|
||||
"Doe"
|
||||
);
|
||||
|
||||
AddUserCommand addUserCommand = AddUserCommand.builder()
|
||||
.email(UserTestData.johnDoeEmail())
|
||||
.firstName("John")
|
||||
.lastName("Doe")
|
||||
.build();
|
||||
|
||||
//when
|
||||
UserIdentifier userIdentifier = facade.handle(addUserCommand);
|
||||
|
||||
//then
|
||||
assertTrue(userIdentifier.getAsLong() > 0);
|
||||
assertEquals(expectedUser, database.users.get(userIdentifier.getAsLong()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package io.wkrzywiec.hexagonal.library.user.infrastructure;
|
||||
|
||||
import io.wkrzywiec.hexagonal.library.UserTestData;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.EmailAddress;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.User;
|
||||
import io.wkrzywiec.hexagonal.library.user.model.UserIdentifier;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
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 org.springframework.test.context.jdbc.Sql;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.springframework.test.context.jdbc.Sql.ExecutionPhase.AFTER_TEST_METHOD;
|
||||
|
||||
@SpringBootTest
|
||||
public class UserDatabaseAdapterITCase {
|
||||
|
||||
@Autowired
|
||||
private JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
private UserRepository userRepository;
|
||||
|
||||
private UserDatabaseAdapter userDatabase;
|
||||
|
||||
@BeforeEach
|
||||
public void init(){
|
||||
userDatabase = new UserDatabaseAdapter(userRepository);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("Save new user in database")
|
||||
@Sql(scripts = "/clean-database.sql", executionPhase = AFTER_TEST_METHOD)
|
||||
public void shouldSaveDatabase(){
|
||||
//given
|
||||
User user = new User(
|
||||
new EmailAddress(UserTestData.johnDoeEmail()),
|
||||
"John",
|
||||
"Doe"
|
||||
);
|
||||
|
||||
//when
|
||||
UserIdentifier userIdentifier = userDatabase.save(user);
|
||||
|
||||
//then
|
||||
Long savedUserId = jdbcTemplate.queryForObject(
|
||||
"SELECT id FROM user WHERE email = ?",
|
||||
Long.class,
|
||||
UserTestData.johnDoeEmail());
|
||||
|
||||
assertEquals(userIdentifier.getAsLong(), savedUserId);
|
||||
}
|
||||
}
|
||||
7
src/test/resources/application.yml
Normal file
7
src/test/resources/application.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
spring:
|
||||
datasource:
|
||||
url: jdbc:h2:mem:testdb
|
||||
driverClassName: org.h2.Driver
|
||||
username: sa
|
||||
password: password
|
||||
jpa.database-platform: org.hibernate.dialect.H2Dialect
|
||||
3
src/test/resources/available-book.sql
Normal file
3
src/test/resources/available-book.sql
Normal file
@@ -0,0 +1,3 @@
|
||||
INSERT INTO public.available (book_id) VALUES
|
||||
SELECT id FROM public.book WHERE book.title = 'Homo Deus'
|
||||
;
|
||||
17
src/test/resources/book-and-user.sql
Normal file
17
src/test/resources/book-and-user.sql
Normal file
@@ -0,0 +1,17 @@
|
||||
INSERT INTO public.author (name) VALUES
|
||||
('Yuval Noah Harari')
|
||||
;
|
||||
|
||||
INSERT INTO public.book (book_external_id,isbn_10,isbn_13,title,publisher,published_date,description,page_count,image_link) VALUES
|
||||
('dWYyCwAAQBAJ','1473545374','9781473545373','Homo Deus','Random House','2016-09-08','<p><b>**THE MILLION COPY BESTSELLER**</b><br> <b></b><br><b> <i>Sapiens </i>showed us where we came from. In uncertain times, <i>Homo Deus</i> shows us where we’re going.</b></p><p> Yuval Noah Harari envisions a near future in which we face a new set of challenges. <i>Homo Deus</i> explores the projects, dreams and nightmares that will shape the twenty-first century and beyond – from overcoming death to creating artificial life.</p><p> It asks the fundamental questions: how can we protect this fragile world from our own destructive power? And what does our future hold?<br> <b></b><br><b> ''<i>Homo Deus</i> will shock you. It will entertain you. It will make you think in ways you had not thought before’ Daniel Kahneman, bestselling author of <i>Thinking, Fast and Slow</i></b></p>',528,'http://books.google.com/books/content?id=dWYyCwAAQBAJ&printsec=frontcover&img=1&zoom=1&edge=curl&imgtk=AFLRE73PkLs4TNB-W2uhDvXJkIB4-9G9AJ_L1iYTYLEXa3zi2kahdsN9-_0tL7WRWgujNpjMA5ZuJO7_ykFUlCWAyLzcQVcGkqUS-NOkUkEcJ_ZRrgq48URpcfBrJWQCwSWtHo5pEGkp&source=gbs_api')
|
||||
;
|
||||
|
||||
INSERT INTO public.book_author (book_id, author_id)
|
||||
SELECT b.id, a.id
|
||||
FROM public.book b, public.author a
|
||||
WHERE b.title = 'Homo deus' AND a.name = 'Yuval Noah Harari'
|
||||
;
|
||||
|
||||
INSERT INTO public.user (first_name, last_name, email) VALUES
|
||||
('John','Doe','john.doe@test.com')
|
||||
;
|
||||
6
src/test/resources/clean-database.sql
Normal file
6
src/test/resources/clean-database.sql
Normal file
@@ -0,0 +1,6 @@
|
||||
DELETE FROM public.reserved;
|
||||
DELETE FROM public.available;
|
||||
DELETE FROM public.book_author;
|
||||
DELETE FROM public.user;
|
||||
DELETE FROM public.book;
|
||||
DELETE FROM public.author;
|
||||
Reference in New Issue
Block a user