BAEL-4546: Reverting evaluation article code

This commit is contained in:
Swapan Pramanick 2021-10-30 02:01:47 +02:00
parent 1e9e55daf9
commit e29cfda933
19 changed files with 0 additions and 648 deletions

View File

@ -1,20 +0,0 @@
package com.baeldung.hexagonal;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration;
import org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
@SpringBootApplication(exclude={
CassandraAutoConfiguration.class,
MongoDataAutoConfiguration.class,
MongoAutoConfiguration.class
})
public class HexagonalSpringApplication {
public static void main(final String[] args) {
SpringApplication.run(HexagonalSpringApplication.class, args);
}
}

View File

@ -1,22 +0,0 @@
package com.baeldung.hexagonal.adapter;
import com.baeldung.hexagonal.domain.Booking;
import com.baeldung.hexagonal.port.BookingPersistencePort;
import com.baeldung.hexagonal.repository.BookingRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class BookingPersistenceAdapter implements BookingPersistencePort {
@Autowired
private BookingRepository bookingRepository;
public boolean persist(Booking booking) {
return bookingRepository.save(booking);
}
public boolean updateStatus(String bookingId, String status) {
return bookingRepository.updateStatus(bookingId, status);
}
}

View File

@ -1,32 +0,0 @@
package com.baeldung.hexagonal.adapter;
import com.baeldung.hexagonal.port.BookingServicePort;
import org.springframework.beans.factory.annotation.Autowired;
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.RestController;
import static com.baeldung.hexagonal.port.BookingServicePort.BookingRequest;
import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse;
import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse.SUCCESS;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.FAILED_DEPENDENCY;
@RestController
public class RestAPIEndpointAdapter {
private BookingServicePort bookingServicePort;
@Autowired
public RestAPIEndpointAdapter(BookingServicePort bookingServicePort) {
this.bookingServicePort = bookingServicePort;
}
@PostMapping(path = "/booking")
public ResponseEntity<BookingResponse> createBooking(@RequestBody BookingRequest request) {
BookingResponse response = bookingServicePort.book(request);
return new ResponseEntity<>(response,
response.getStatusCode() == SUCCESS ? CREATED : FAILED_DEPENDENCY);
}
}

View File

@ -1,30 +0,0 @@
package com.baeldung.hexagonal.adapter;
import com.baeldung.hexagonal.external.service.TheatreService;
import com.baeldung.hexagonal.port.TheatreServicePort;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import java.util.Optional;
import java.util.Set;
import static com.baeldung.hexagonal.external.service.TheatreService.Reservation;
import static org.springframework.http.HttpStatus.CREATED;
import static org.springframework.http.HttpStatus.NO_CONTENT;
@Component
public class TheatreServiceAdapter implements TheatreServicePort {
@Autowired
private TheatreService theatreService;
public Optional<String> reserveSeats(String movieShowId, Set<String> seats) {
ResponseEntity<Reservation> response = theatreService.postReservation(movieShowId, seats);
return response.getStatusCode() == CREATED ? Optional.of(response.getBody().getId()): Optional.empty();
}
public boolean releaseSeats(String resrevationId) {
return theatreService.deleteReservation(resrevationId).getStatusCode() == NO_CONTENT;
}
}

View File

@ -1,19 +0,0 @@
package com.baeldung.hexagonal.adapter;
import com.baeldung.hexagonal.external.service.CustomerWalletService;
import com.baeldung.hexagonal.port.WalletServicePort;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import static org.springframework.http.HttpStatus.CREATED;
@Component
public class WalletServiceAdapter implements WalletServicePort {
@Autowired
private CustomerWalletService customerWalletService;
public boolean debit(String customerId, Double amount) {
return customerWalletService.postDebit(customerId, amount) == CREATED;
}
}

View File

@ -1,87 +0,0 @@
package com.baeldung.hexagonal.domain;
import com.baeldung.hexagonal.port.BookingServicePort;
import java.util.Set;
import java.util.UUID;
public class Booking {
public static final String STATUS_INITIAL = "INITIAL";
public static final String STATUS_SUCCESS = "SUCCESS";
public static final String STATUS_FAILURE = "FAILED";
private String bookingId;
private String movieShowId;
private String customerId;
private Set<String> seats;
private Double amount;
private String status;
public Booking(
String bookingId, String movieShowId, String customerId, Set<String> seats, Double amount, String status) {
this.bookingId = bookingId;
this.movieShowId = movieShowId;
this.customerId = customerId;
this.seats = seats;
this.amount = amount;
this.status = status;
}
public Booking(BookingServicePort.BookingRequest request) {
this.bookingId = UUID.randomUUID().toString();
this.movieShowId = request.getMovieShowId();
this.customerId = request.getCustomerId();
this.seats = request.getSeats();
this.amount = request.getAmount();
this.status = STATUS_INITIAL;
}
public String getMovieShowId() {
return movieShowId;
}
public Set<String> getSeats() {
return seats;
}
public String getCustomerId() {
return customerId;
}
public String getBookingId() {
return bookingId;
}
public Double getAmount() {
return amount;
}
public String getStatus() {
return status;
}
public void setBookingId(String bookingId) {
this.bookingId = bookingId;
}
public void setMovieShowId(String movieShowId) {
this.movieShowId = movieShowId;
}
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
public void setSeats(Set<String> seats) {
this.seats = seats;
}
public void setAmount(Double amount) {
this.amount = amount;
}
public void setStatus(String status) {
this.status = status;
}
}

View File

@ -1,8 +0,0 @@
package com.baeldung.hexagonal.external.service;
import org.springframework.http.HttpStatus;
public interface CustomerWalletService {
HttpStatus postDebit(String customerId, Double amount);
}

View File

@ -1,11 +0,0 @@
package com.baeldung.hexagonal.external.service;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
@Service
public class MockCustomerWalletService implements CustomerWalletService {
public HttpStatus postDebit(String customerId, Double amount) {
return HttpStatus.CREATED;
}
}

View File

@ -1,21 +0,0 @@
package com.baeldung.hexagonal.external.service;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import java.util.Set;
import java.util.UUID;
@Service
public class MockTheatreService implements TheatreService {
public ResponseEntity<Reservation> postReservation(String movieShowId, Set<String> seats) {
return new ResponseEntity<>(
new Reservation(UUID.randomUUID().toString()), HttpStatus.CREATED);
}
public ResponseEntity<Void> deleteReservation(String reservationId) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
}

View File

@ -1,24 +0,0 @@
package com.baeldung.hexagonal.external.service;
import org.springframework.http.ResponseEntity;
import java.util.Set;
public interface TheatreService {
ResponseEntity<Reservation> postReservation(String movieShowId, Set<String> seats);
ResponseEntity<Void> deleteReservation(String reservationId);
class Reservation {
private final String id;
public Reservation(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
}

View File

@ -1,8 +0,0 @@
package com.baeldung.hexagonal.port;
import com.baeldung.hexagonal.domain.Booking;
public interface BookingPersistencePort {
boolean persist(Booking booking);
public boolean updateStatus(String bookingId, String status);
}

View File

@ -1,73 +0,0 @@
package com.baeldung.hexagonal.port;
import java.util.Set;
public interface BookingServicePort {
BookingResponse book(BookingRequest request);
class BookingRequest {
private String movieShowId;
private String customerId;
private Set<String> seats;
private Double amount;
public String getMovieShowId() {
return movieShowId;
}
public void setMovieShowId(String movieShowId) {
this.movieShowId = movieShowId;
}
public String getCustomerId() {
return customerId;
}
public void setCustomerId(String customerId) {
this.customerId = customerId;
}
public Set<String> getSeats() {
return seats;
}
public void setSeats(Set<String> seats) {
this.seats = seats;
}
public Double getAmount() {
return amount;
}
public void setAmount(Double amount) {
this.amount = amount;
}
}
class BookingResponse {
public static final int SUCCESS = 0;
public static final int SEAT_NOT_AVAILABLE = 1;
public static final int PAYMENT_FAILED = 2;
public static final int UNKNOWN_ERROR = 3;
private final int statusCode;
private String bookingId;
public BookingResponse(String bookingId, int statusCode) {
this.bookingId = bookingId;
this.statusCode = statusCode;
}
public BookingResponse(int statusCode) {
this.statusCode = statusCode;
}
public int getStatusCode() {
return statusCode;
}
public String getBookingId() {
return bookingId;
}
}
}

View File

@ -1,9 +0,0 @@
package com.baeldung.hexagonal.port;
import java.util.Optional;
import java.util.Set;
public interface TheatreServicePort {
Optional<String> reserveSeats(String movieShowId, Set<String> seats);
boolean releaseSeats(String resrevationId);
}

View File

@ -1,5 +0,0 @@
package com.baeldung.hexagonal.port;
public interface WalletServicePort {
boolean debit(String customerId, Double amount);
}

View File

@ -1,9 +0,0 @@
package com.baeldung.hexagonal.repository;
import com.baeldung.hexagonal.domain.Booking;
public interface BookingRepository {
boolean save(Booking booking);
boolean updateStatus(String bookingId, String status);
}

View File

@ -1,16 +0,0 @@
package com.baeldung.hexagonal.repository;
import com.baeldung.hexagonal.domain.Booking;
import org.springframework.stereotype.Repository;
@Repository
public class MockBookingRepository implements BookingRepository {
public boolean save(Booking booking) {
return true;
}
@Override
public boolean updateStatus(String bookingId, String status) {
return true;
}
}

View File

@ -1,57 +0,0 @@
package com.baeldung.hexagonal.usecase;
import com.baeldung.hexagonal.domain.Booking;
import com.baeldung.hexagonal.port.BookingPersistencePort;
import com.baeldung.hexagonal.port.BookingServicePort;
import com.baeldung.hexagonal.port.TheatreServicePort;
import com.baeldung.hexagonal.port.WalletServicePort;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Optional;
import static com.baeldung.hexagonal.domain.Booking.STATUS_FAILURE;
import static com.baeldung.hexagonal.domain.Booking.STATUS_SUCCESS;
import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse.*;
@Component
public class BookTicketUseCase implements BookingServicePort {
private BookingPersistencePort bookingPersistencePort;
private TheatreServicePort theatreServicePort;
private WalletServicePort walletServicePort;
@Autowired
public BookTicketUseCase(BookingPersistencePort bookingPersistencePort, TheatreServicePort theatreServicePort, WalletServicePort walletServicePort) {
this.bookingPersistencePort = bookingPersistencePort;
this.theatreServicePort = theatreServicePort;
this.walletServicePort = walletServicePort;
}
public BookingResponse book(BookingRequest request) {
Booking booking = new Booking(request);
if (!bookingPersistencePort.persist(booking)) {
return new BookingResponse(UNKNOWN_ERROR);
}
String bookingId = booking.getBookingId();
Optional<String> reservationIdOptional = theatreServicePort.reserveSeats(booking.getMovieShowId(), booking.getSeats());
if (!reservationIdOptional.isPresent()) {
bookingPersistencePort.updateStatus(bookingId, STATUS_FAILURE);
return new BookingResponse(bookingId, SEAT_NOT_AVAILABLE);
}
if (!walletServicePort.debit(booking.getCustomerId(), booking.getAmount())) {
reservationIdOptional.ifPresent(reservationId -> theatreServicePort.releaseSeats(reservationId));
bookingPersistencePort.updateStatus(bookingId, STATUS_FAILURE);
return new BookingResponse(bookingId, PAYMENT_FAILED);
}
bookingPersistencePort.updateStatus(bookingId, STATUS_SUCCESS);
return new BookingResponse(bookingId, SUCCESS);
}
}

View File

@ -1,87 +0,0 @@
package com.baeldung.hexagonal.adapter;
import com.baeldung.hexagonal.port.BookingServicePort;
import com.baeldung.hexagonal.port.BookingServicePort.BookingRequest;
import com.baeldung.hexagonal.port.BookingServicePort.BookingResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import java.util.Arrays;
import java.util.HashSet;
import static com.baeldung.hexagonal.port.BookingServicePort.BookingResponse.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
class RestAPIEndpointAdapterUnitTest {
private BookingServicePort bookingServicePort;
private RestAPIEndpointAdapter restAPIEndpointAdapter;
@BeforeEach
void setUp() {
bookingServicePort = mock(BookingServicePort.class);
restAPIEndpointAdapter = new RestAPIEndpointAdapter(bookingServicePort);
}
private BookingServicePort.BookingRequest getBookingRequest() {
BookingServicePort.BookingRequest request = new BookingServicePort.BookingRequest();
request.setMovieShowId("movie-show-id");
request.setCustomerId("customer-id");
request.setSeats(new HashSet<>(Arrays.asList("A1", "A2")));
request.setAmount(100.00);
return request;
}
@Test
void whenBookingServicePortReturnsUnknownError_thenReturnInternalServerError() {
BookingServicePort.BookingRequest request = getBookingRequest();
when(bookingServicePort.book(any(BookingRequest.class))).thenReturn(new BookingResponse(UNKNOWN_ERROR));
ResponseEntity<BookingResponse> response = restAPIEndpointAdapter.createBooking(request);
verify(bookingServicePort).book(any(BookingRequest.class));
assertNotNull(response);
assertEquals(HttpStatus.FAILED_DEPENDENCY, response.getStatusCode());
}
@Test
void whenBookingServicePortReturnsSeatUnavailable_thenReturnPreconditionFailed() {
BookingServicePort.BookingRequest request = getBookingRequest();
when(bookingServicePort.book(any(BookingRequest.class))).thenReturn(new BookingResponse(SEAT_NOT_AVAILABLE));
ResponseEntity<BookingResponse> response = restAPIEndpointAdapter.createBooking(request);
verify(bookingServicePort).book(any(BookingRequest.class));
assertNotNull(response);
assertEquals(HttpStatus.FAILED_DEPENDENCY, response.getStatusCode());
}
@Test
void whenBookingServicePortReturnsPaymentFailed_thenReturnPreconditionFailed() {
BookingServicePort.BookingRequest request = getBookingRequest();
when(bookingServicePort.book(any(BookingRequest.class))).thenReturn(new BookingResponse(PAYMENT_FAILED));
ResponseEntity<BookingResponse> response = restAPIEndpointAdapter.createBooking(request);
verify(bookingServicePort).book(any(BookingRequest.class));
assertNotNull(response);
assertEquals(HttpStatus.FAILED_DEPENDENCY, response.getStatusCode());
}
@Test
void whenBookingServicePortReturnsSuccess_thenReturnCreated() {
BookingServicePort.BookingRequest request = getBookingRequest();
when(bookingServicePort.book(any(BookingRequest.class))).thenReturn(new BookingResponse(SUCCESS));
ResponseEntity<BookingResponse> response = restAPIEndpointAdapter.createBooking(request);
verify(bookingServicePort).book(any(BookingRequest.class));
assertNotNull(response);
assertEquals(HttpStatus.CREATED, response.getStatusCode());
}
}

View File

@ -1,110 +0,0 @@
package com.baeldung.hexagonal.usecase;
import com.baeldung.hexagonal.domain.Booking;
import com.baeldung.hexagonal.port.BookingPersistencePort;
import com.baeldung.hexagonal.port.BookingServicePort;
import com.baeldung.hexagonal.port.TheatreServicePort;
import com.baeldung.hexagonal.port.WalletServicePort;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
class BookTicketUseCaseUnitTest {
private BookingPersistencePort bookingPersistencePort;
private TheatreServicePort theatreServicePort;
private WalletServicePort walletServicePort;
private BookTicketUseCase bookTicketUseCase;
@BeforeEach
void setUp() {
bookingPersistencePort = mock(BookingPersistencePort.class);
theatreServicePort = mock(TheatreServicePort.class);
walletServicePort = mock(WalletServicePort.class);
bookTicketUseCase = new BookTicketUseCase(bookingPersistencePort, theatreServicePort, walletServicePort);
}
private BookingServicePort.BookingRequest getBookingRequest() {
BookingServicePort.BookingRequest request = new BookingServicePort.BookingRequest();
request.setMovieShowId("movie-show-id");
request.setCustomerId("customer-id");
request.setSeats(new HashSet<>(Arrays.asList("A1", "A2")));
request.setAmount(100.00);
return request;
}
@Test
void whenErrorInInitialPersistence_thenReturnUnknownError() {
BookingServicePort.BookingRequest request = getBookingRequest();
Booking booking = new Booking(request);
when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(false);
BookingServicePort.BookingResponse response = bookTicketUseCase.book(request);
verify(bookingPersistencePort, times(1)).persist(any(Booking.class));
assertNotNull(response);
assertEquals(BookingServicePort.BookingResponse.UNKNOWN_ERROR, response.getStatusCode());
}
@Test
void whenErrorInReserveSeats_thenReturnSeatNotAvailable() {
BookingServicePort.BookingRequest request = getBookingRequest();
Booking booking = new Booking(request);
when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(true);
when(theatreServicePort.reserveSeats(booking.getMovieShowId(), booking.getSeats()))
.thenReturn(Optional.empty());
BookingServicePort.BookingResponse response = bookTicketUseCase.book(request);
verify(bookingPersistencePort).persist(any(Booking.class));
verify(bookingPersistencePort).updateStatus(any(String.class), any(String.class));
verify(theatreServicePort).reserveSeats(any(String.class), any(HashSet.class));
assertNotNull(response);
assertEquals(BookingServicePort.BookingResponse.SEAT_NOT_AVAILABLE, response.getStatusCode());
}
@Test
void whenErrorInWalletDebit_thenReturnPaymentFailed() {
BookingServicePort.BookingRequest request = getBookingRequest();
Booking booking = new Booking(request);
when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(true);
when(theatreServicePort.reserveSeats(booking.getMovieShowId(), booking.getSeats()))
.thenReturn(Optional.of("reservation-id"));
when(walletServicePort.debit(booking.getCustomerId(), booking.getAmount()))
.thenReturn(false);
BookingServicePort.BookingResponse response = bookTicketUseCase.book(request);
verify(bookingPersistencePort).persist(any(Booking.class));
verify(bookingPersistencePort).updateStatus(any(String.class), any(String.class));
verify(theatreServicePort).reserveSeats(any(String.class), any(HashSet.class));
verify(walletServicePort).debit(any(String.class), any(Double.class));
verify(theatreServicePort).releaseSeats(any(String.class));
assertNotNull(response);
assertEquals(BookingServicePort.BookingResponse.PAYMENT_FAILED, response.getStatusCode());
}
@Test
void whenNoErrorInAnyPorts_thenReturnSuccess() {
BookingServicePort.BookingRequest request = getBookingRequest();
Booking booking = new Booking(request);
when(bookingPersistencePort.persist(any(Booking.class))).thenReturn(true);
when(theatreServicePort.reserveSeats(booking.getMovieShowId(), booking.getSeats()))
.thenReturn(Optional.of("reservation-id"));
when(walletServicePort.debit(booking.getCustomerId(), booking.getAmount()))
.thenReturn(true);
BookingServicePort.BookingResponse response = bookTicketUseCase.book(request);
verify(bookingPersistencePort).persist(any(Booking.class));
verify(bookingPersistencePort).updateStatus(any(String.class), any(String.class));
verify(theatreServicePort).reserveSeats( any(String.class), any(HashSet.class));
verify(walletServicePort).debit(any(String.class), any(Double.class));
assertNotNull(response);
assertEquals(BookingServicePort.BookingResponse.SUCCESS, response.getStatusCode());
}
}