diff --git a/src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/adapter/CartController.java b/src/main/java/com/rviewer/skeletons/application/adapter/CartController.java similarity index 86% rename from src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/adapter/CartController.java rename to src/main/java/com/rviewer/skeletons/application/adapter/CartController.java index 5f3a437..7d2f5be 100644 --- a/src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/adapter/CartController.java +++ b/src/main/java/com/rviewer/skeletons/application/adapter/CartController.java @@ -1,12 +1,12 @@ -package com.rviewer.skeletons.infrastructure.inbound.api.adapter; +package com.rviewer.skeletons.application.adapter; +import com.rviewer.skeletons.application.exception.CartAlreadyExistsException; +import com.rviewer.skeletons.application.exception.CartNotFoundException; import com.rviewer.skeletons.domain.model.Cart; import com.rviewer.skeletons.domain.model.Item; import com.rviewer.skeletons.domain.service.CartService; -import com.rviewer.skeletons.infrastructure.inbound.api.exception.CartAlreadyExistsException; -import com.rviewer.skeletons.infrastructure.inbound.api.exception.CartNotFoundException; -import com.rviewer.skeletons.infrastructure.inbound.api.request.CreateCartReq; -import com.rviewer.skeletons.infrastructure.inbound.api.request.UpdateCartReq; +import com.rviewer.skeletons.application.request.CreateCartReq; +import com.rviewer.skeletons.application.request.UpdateCartReq; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; diff --git a/src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/exception/CartAlreadyExistsException.java b/src/main/java/com/rviewer/skeletons/application/exception/CartAlreadyExistsException.java similarity index 84% rename from src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/exception/CartAlreadyExistsException.java rename to src/main/java/com/rviewer/skeletons/application/exception/CartAlreadyExistsException.java index 94b4def..dab25e1 100644 --- a/src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/exception/CartAlreadyExistsException.java +++ b/src/main/java/com/rviewer/skeletons/application/exception/CartAlreadyExistsException.java @@ -1,4 +1,4 @@ -package com.rviewer.skeletons.infrastructure.inbound.api.exception; +package com.rviewer.skeletons.application.exception; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; diff --git a/src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/exception/CartNotFoundException.java b/src/main/java/com/rviewer/skeletons/application/exception/CartNotFoundException.java similarity index 84% rename from src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/exception/CartNotFoundException.java rename to src/main/java/com/rviewer/skeletons/application/exception/CartNotFoundException.java index 5cb0609..bf5b3df 100644 --- a/src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/exception/CartNotFoundException.java +++ b/src/main/java/com/rviewer/skeletons/application/exception/CartNotFoundException.java @@ -1,4 +1,4 @@ -package com.rviewer.skeletons.infrastructure.inbound.api.exception; +package com.rviewer.skeletons.application.exception; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; diff --git a/src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/exception/ControllerExceptionHandler.java b/src/main/java/com/rviewer/skeletons/application/exception/ControllerExceptionHandler.java similarity index 95% rename from src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/exception/ControllerExceptionHandler.java rename to src/main/java/com/rviewer/skeletons/application/exception/ControllerExceptionHandler.java index 66a6fa8..08af670 100644 --- a/src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/exception/ControllerExceptionHandler.java +++ b/src/main/java/com/rviewer/skeletons/application/exception/ControllerExceptionHandler.java @@ -1,4 +1,4 @@ -package com.rviewer.skeletons.infrastructure.inbound.api.exception; +package com.rviewer.skeletons.application.exception; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/request/CreateCartReq.java b/src/main/java/com/rviewer/skeletons/application/request/CreateCartReq.java similarity index 77% rename from src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/request/CreateCartReq.java rename to src/main/java/com/rviewer/skeletons/application/request/CreateCartReq.java index c2ef5b7..1830318 100644 --- a/src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/request/CreateCartReq.java +++ b/src/main/java/com/rviewer/skeletons/application/request/CreateCartReq.java @@ -1,4 +1,4 @@ -package com.rviewer.skeletons.infrastructure.inbound.api.request; +package com.rviewer.skeletons.application.request; import com.rviewer.skeletons.domain.model.Item; import lombok.Getter; diff --git a/src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/request/UpdateCartReq.java b/src/main/java/com/rviewer/skeletons/application/request/UpdateCartReq.java similarity index 77% rename from src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/request/UpdateCartReq.java rename to src/main/java/com/rviewer/skeletons/application/request/UpdateCartReq.java index 4594c30..e208ee1 100644 --- a/src/main/java/com/rviewer/skeletons/infrastructure/inbound/api/request/UpdateCartReq.java +++ b/src/main/java/com/rviewer/skeletons/application/request/UpdateCartReq.java @@ -1,4 +1,4 @@ -package com.rviewer.skeletons.infrastructure.inbound.api.request; +package com.rviewer.skeletons.application.request; import com.rviewer.skeletons.domain.model.Item; import lombok.Getter; diff --git a/src/main/java/com/rviewer/skeletons/domain/exception/HashGenerationException.java b/src/main/java/com/rviewer/skeletons/domain/exception/HashGenerationException.java new file mode 100644 index 0000000..c04499f --- /dev/null +++ b/src/main/java/com/rviewer/skeletons/domain/exception/HashGenerationException.java @@ -0,0 +1,7 @@ +package com.rviewer.skeletons.domain.exception; + +public class HashGenerationException extends RuntimeException { + public HashGenerationException(Throwable cause) { + super("Unable generate hash: " + cause); + } +} diff --git a/src/main/java/com/rviewer/skeletons/domain/model/Block.java b/src/main/java/com/rviewer/skeletons/domain/model/Block.java new file mode 100644 index 0000000..95bda24 --- /dev/null +++ b/src/main/java/com/rviewer/skeletons/domain/model/Block.java @@ -0,0 +1,52 @@ +package com.rviewer.skeletons.domain.model; + +import com.rviewer.skeletons.domain.exception.HashGenerationException; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.With; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.time.LocalDateTime; +import java.util.Random; + +@Data +@AllArgsConstructor +@With +public class Block { + private LocalDateTime timestamp; + private String lasthash; + private String data; + private Integer nonce; + private String hash; + + public Block() { + this.timestamp = LocalDateTime.now(); + this.nonce = new Random().nextInt(); + this.hash = generateHashFromBlock(this); + } + + public static Block getGenesisBlock() { + return new Block().withLasthash(""); + } + + static String generateHashFromBlock(final Block block) throws HashGenerationException { + try { + final var digest = MessageDigest.getInstance("SHA-256"); + final byte[] hash = digest.digest(block.getHashData().getBytes(StandardCharsets.UTF_8)); + final var hexString = new StringBuilder(); + for (final byte elem : hash) { + final var hex = Integer.toHexString(0xff & elem); + if (hex.length() == 1) hexString.append('0'); + hexString.append(hex); + } + return hexString.toString(); + } catch (final Exception e) { + throw new HashGenerationException(e); + } + } + + private String getHashData() { + return timestamp + lasthash + data + nonce; + } +} diff --git a/src/main/java/com/rviewer/skeletons/domain/model/Blockchain.java b/src/main/java/com/rviewer/skeletons/domain/model/Blockchain.java new file mode 100644 index 0000000..54c254e --- /dev/null +++ b/src/main/java/com/rviewer/skeletons/domain/model/Blockchain.java @@ -0,0 +1,28 @@ +package com.rviewer.skeletons.domain.model; + +import org.springframework.stereotype.Component; + +import java.util.LinkedList; +import java.util.List; + +@Component +public class Blockchain { + private final LinkedList blockchainList = new LinkedList<>(); + + public LinkedList getBlockchainList() { + return blockchainList; + } + + public Block addBlock(Block block) { + blockchainList.add(block); + return blockchainList.getLast(); + } + + public Boolean replace(List blockchainList) { + return true; + } + + public Boolean isValid(List blockchainList) { + return true; + } +} diff --git a/src/main/java/com/rviewer/skeletons/domain/ports/secondary/DatabasePort.java b/src/main/java/com/rviewer/skeletons/domain/ports/secondary/CartDatabasePort.java similarity index 86% rename from src/main/java/com/rviewer/skeletons/domain/ports/secondary/DatabasePort.java rename to src/main/java/com/rviewer/skeletons/domain/ports/secondary/CartDatabasePort.java index 8b40a64..9cfb1d1 100644 --- a/src/main/java/com/rviewer/skeletons/domain/ports/secondary/DatabasePort.java +++ b/src/main/java/com/rviewer/skeletons/domain/ports/secondary/CartDatabasePort.java @@ -4,7 +4,7 @@ import com.rviewer.skeletons.domain.model.Cart; import java.util.Optional; -public interface DatabasePort { +public interface CartDatabasePort { Optional get(String id); void save(Cart cart); diff --git a/src/main/java/com/rviewer/skeletons/domain/service/CartService.java b/src/main/java/com/rviewer/skeletons/domain/service/CartService.java index 07ab3fd..7018ea9 100644 --- a/src/main/java/com/rviewer/skeletons/domain/service/CartService.java +++ b/src/main/java/com/rviewer/skeletons/domain/service/CartService.java @@ -1,8 +1,10 @@ package com.rviewer.skeletons.domain.service; +import com.rviewer.skeletons.domain.model.Block; +import com.rviewer.skeletons.domain.model.Blockchain; import com.rviewer.skeletons.domain.model.Cart; import com.rviewer.skeletons.domain.ports.primary.CartServicePort; -import com.rviewer.skeletons.domain.ports.secondary.DatabasePort; +import com.rviewer.skeletons.domain.ports.secondary.CartDatabasePort; import org.springframework.stereotype.Component; import javax.transaction.Transactional; @@ -11,24 +13,34 @@ import java.util.Optional; @Component @Transactional public class CartService implements CartServicePort { - private final DatabasePort databasePort; + private final CartDatabasePort cartDatabasePort; + private final Blockchain blockchain; - public CartService(DatabasePort databasePort) { - this.databasePort = databasePort; + public CartService(CartDatabasePort cartDatabasePort, Blockchain blockchain) { + this.cartDatabasePort = cartDatabasePort; + this.blockchain = blockchain; } @Override public Optional get(String id) { - return databasePort.get(id); + return cartDatabasePort.get(id); } @Override public void save(Cart cart) { - databasePort.save(cart); + cartDatabasePort.save(cart); + if (blockchain.getBlockchainList().isEmpty()) { + blockchain.addBlock(Block.getGenesisBlock().withData(cart.toString())); + } else { + blockchain.addBlock(new Block().withData(cart.toString()) + .withLasthash(blockchain.getBlockchainList().getLast().getHash())); + } } @Override public void delete(String id) { - databasePort.delete(id); + cartDatabasePort.delete(id); + blockchain.addBlock(new Block().withData("deleted cart with id:" + id) + .withLasthash(blockchain.getBlockchainList().getLast().getHash())); } } diff --git a/src/main/java/com/rviewer/skeletons/infrastructure/outbound/database/adapter/PostgresAdapter.java b/src/main/java/com/rviewer/skeletons/infrastructure/database/adapter/CartDatabaseAdapter.java similarity index 61% rename from src/main/java/com/rviewer/skeletons/infrastructure/outbound/database/adapter/PostgresAdapter.java rename to src/main/java/com/rviewer/skeletons/infrastructure/database/adapter/CartDatabaseAdapter.java index 1c30881..12773b3 100644 --- a/src/main/java/com/rviewer/skeletons/infrastructure/outbound/database/adapter/PostgresAdapter.java +++ b/src/main/java/com/rviewer/skeletons/infrastructure/database/adapter/CartDatabaseAdapter.java @@ -1,20 +1,20 @@ -package com.rviewer.skeletons.infrastructure.outbound.database.adapter; +package com.rviewer.skeletons.infrastructure.database.adapter; import com.rviewer.skeletons.domain.model.Cart; -import com.rviewer.skeletons.domain.ports.secondary.DatabasePort; -import com.rviewer.skeletons.infrastructure.outbound.database.repository.PostgresCartRepository; -import com.rviewer.skeletons.infrastructure.outbound.database.dto.PostgresCart; +import com.rviewer.skeletons.domain.ports.secondary.CartDatabasePort; +import com.rviewer.skeletons.infrastructure.database.dto.PostgresCart; +import com.rviewer.skeletons.infrastructure.database.repository.PostgresCartRepository; import org.springframework.stereotype.Component; import java.util.Optional; import java.util.UUID; @Component -public class PostgresAdapter implements DatabasePort { +public class CartDatabaseAdapter implements CartDatabasePort { private final PostgresCartRepository postgresCartRepository; - public PostgresAdapter(PostgresCartRepository postgresCartRepository) { + public CartDatabaseAdapter(PostgresCartRepository postgresCartRepository) { this.postgresCartRepository = postgresCartRepository; } diff --git a/src/main/java/com/rviewer/skeletons/infrastructure/outbound/database/dto/PostgresCart.java b/src/main/java/com/rviewer/skeletons/infrastructure/database/dto/PostgresCart.java similarity index 94% rename from src/main/java/com/rviewer/skeletons/infrastructure/outbound/database/dto/PostgresCart.java rename to src/main/java/com/rviewer/skeletons/infrastructure/database/dto/PostgresCart.java index b50905c..6c5af54 100644 --- a/src/main/java/com/rviewer/skeletons/infrastructure/outbound/database/dto/PostgresCart.java +++ b/src/main/java/com/rviewer/skeletons/infrastructure/database/dto/PostgresCart.java @@ -1,4 +1,4 @@ -package com.rviewer.skeletons.infrastructure.outbound.database.dto; +package com.rviewer.skeletons.infrastructure.database.dto; import com.rviewer.skeletons.domain.model.Cart; import lombok.AllArgsConstructor; diff --git a/src/main/java/com/rviewer/skeletons/infrastructure/outbound/database/dto/PostgresItem.java b/src/main/java/com/rviewer/skeletons/infrastructure/database/dto/PostgresItem.java similarity index 92% rename from src/main/java/com/rviewer/skeletons/infrastructure/outbound/database/dto/PostgresItem.java rename to src/main/java/com/rviewer/skeletons/infrastructure/database/dto/PostgresItem.java index 6d5ee2d..73ea065 100644 --- a/src/main/java/com/rviewer/skeletons/infrastructure/outbound/database/dto/PostgresItem.java +++ b/src/main/java/com/rviewer/skeletons/infrastructure/database/dto/PostgresItem.java @@ -1,4 +1,4 @@ -package com.rviewer.skeletons.infrastructure.outbound.database.dto; +package com.rviewer.skeletons.infrastructure.database.dto; import com.rviewer.skeletons.domain.model.Item; import lombok.AllArgsConstructor; diff --git a/src/main/java/com/rviewer/skeletons/infrastructure/outbound/database/repository/PostgresCartRepository.java b/src/main/java/com/rviewer/skeletons/infrastructure/database/repository/PostgresCartRepository.java similarity index 68% rename from src/main/java/com/rviewer/skeletons/infrastructure/outbound/database/repository/PostgresCartRepository.java rename to src/main/java/com/rviewer/skeletons/infrastructure/database/repository/PostgresCartRepository.java index b116eb2..55e714e 100644 --- a/src/main/java/com/rviewer/skeletons/infrastructure/outbound/database/repository/PostgresCartRepository.java +++ b/src/main/java/com/rviewer/skeletons/infrastructure/database/repository/PostgresCartRepository.java @@ -1,6 +1,6 @@ -package com.rviewer.skeletons.infrastructure.outbound.database.repository; +package com.rviewer.skeletons.infrastructure.database.repository; -import com.rviewer.skeletons.infrastructure.outbound.database.dto.PostgresCart; +import com.rviewer.skeletons.infrastructure.database.dto.PostgresCart; import org.springframework.data.repository.CrudRepository; import org.springframework.stereotype.Repository; diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e25079a..c2bbd6e 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -2,7 +2,7 @@ server.port=8080 application.title=Cartfidential application.version=1.0.0 -spring.datasource.initialization-mode=always +spring.sql.init.mode=always spring.datasource.username = rv_user spring.datasource.password = rv_password spring.datasource.driverClassName = org.postgresql.Driver diff --git a/src/test/java/com/rviewer/skeletons/infrastructure/inbound/api/adapter/CartControllerTest.java b/src/test/java/com/rviewer/skeletons/infrastructure/inbound/api/adapter/CartControllerTest.java index 93b8294..178d018 100644 --- a/src/test/java/com/rviewer/skeletons/infrastructure/inbound/api/adapter/CartControllerTest.java +++ b/src/test/java/com/rviewer/skeletons/infrastructure/inbound/api/adapter/CartControllerTest.java @@ -4,8 +4,8 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.rviewer.skeletons.domain.model.Cart; import com.rviewer.skeletons.domain.model.Item; import com.rviewer.skeletons.domain.service.CartService; -import com.rviewer.skeletons.infrastructure.inbound.api.exception.CartAlreadyExistsException; -import com.rviewer.skeletons.infrastructure.inbound.api.exception.CartNotFoundException; +import com.rviewer.skeletons.application.exception.CartAlreadyExistsException; +import com.rviewer.skeletons.application.exception.CartNotFoundException; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; @@ -21,7 +21,7 @@ import java.util.List; import java.util.Optional; import java.util.UUID; -import static com.rviewer.skeletons.infrastructure.inbound.api.exception.ControllerExceptionHandler.INVALID_BODY; +import static com.rviewer.skeletons.application.exception.ControllerExceptionHandler.INVALID_BODY; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.Mockito.when; diff --git a/src/test/java/com/rviewer/skeletons/infrastructure/outbound/adapter/PostgresAdapterTest.java b/src/test/java/com/rviewer/skeletons/infrastructure/outbound/adapter/CartDatabaseAdapterTest.java similarity index 78% rename from src/test/java/com/rviewer/skeletons/infrastructure/outbound/adapter/PostgresAdapterTest.java rename to src/test/java/com/rviewer/skeletons/infrastructure/outbound/adapter/CartDatabaseAdapterTest.java index 1325ca4..3c730cf 100644 --- a/src/test/java/com/rviewer/skeletons/infrastructure/outbound/adapter/PostgresAdapterTest.java +++ b/src/test/java/com/rviewer/skeletons/infrastructure/outbound/adapter/CartDatabaseAdapterTest.java @@ -2,9 +2,9 @@ package com.rviewer.skeletons.infrastructure.outbound.adapter; import com.rviewer.skeletons.domain.model.Cart; import com.rviewer.skeletons.domain.model.Item; -import com.rviewer.skeletons.infrastructure.outbound.database.adapter.PostgresAdapter; -import com.rviewer.skeletons.infrastructure.outbound.database.dto.PostgresCart; -import com.rviewer.skeletons.infrastructure.outbound.database.repository.PostgresCartRepository; +import com.rviewer.skeletons.infrastructure.database.adapter.CartDatabaseAdapter; +import com.rviewer.skeletons.infrastructure.database.dto.PostgresCart; +import com.rviewer.skeletons.infrastructure.database.repository.PostgresCartRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; @@ -23,16 +23,16 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @SpringBootTest -class PostgresAdapterTest { +class CartDatabaseAdapterTest { @Autowired - private PostgresAdapter postgresAdapter; + private CartDatabaseAdapter cartDatabaseAdapter; @Mock private PostgresCartRepository postgresCartRepository; @BeforeEach void init() { - postgresAdapter = new PostgresAdapter(postgresCartRepository); + cartDatabaseAdapter = new CartDatabaseAdapter(postgresCartRepository); } @Test @@ -43,7 +43,7 @@ class PostgresAdapterTest { when(postgresCartRepository.findById(id)).thenReturn(Optional.ofNullable(PostgresCart.fromDomain(expectedCart))); - Optional result = postgresAdapter.get(id.toString()); + Optional result = cartDatabaseAdapter.get(id.toString()); assertThat(Optional.of(result)).hasValue(Optional.of(expectedCart)); } @@ -53,7 +53,7 @@ class PostgresAdapterTest { Item item = new Item(UUID.randomUUID(), "item", 1, 100F); Cart newCart = new Cart(id, List.of(item)); - postgresAdapter.save(newCart); + cartDatabaseAdapter.save(newCart); ArgumentCaptor captor = ArgumentCaptor.forClass(PostgresCart.class); verify(postgresCartRepository).save(captor.capture()); @@ -68,7 +68,7 @@ class PostgresAdapterTest { when(postgresCartRepository.findById(id)).thenReturn(Optional.ofNullable(PostgresCart.fromDomain(cartToDelete))); - postgresAdapter.delete(cartToDelete.getId().toString()); + cartDatabaseAdapter.delete(cartToDelete.getId().toString()); verify(postgresCartRepository, times(1)).deleteById(cartToDelete.getId()); }