From a78ecaeadb1ada1e9a4e4b576c06cc1508630877 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Fri, 24 May 2024 19:36:58 +0200 Subject: [PATCH 1/7] feat: UserController --- .../web/controller/UserController.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 src/main/java/eu/ztsh/wymiana/web/controller/UserController.java diff --git a/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java b/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java new file mode 100644 index 0000000..c9f7bdc --- /dev/null +++ b/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java @@ -0,0 +1,36 @@ +package eu.ztsh.wymiana.web.controller; + +import eu.ztsh.wymiana.model.User; +import eu.ztsh.wymiana.service.UserService; +import eu.ztsh.wymiana.web.model.UserCreateRequest; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequiredArgsConstructor +@Validated +@RestController +@RequestMapping("/api/user") +public class UserController { + + private final UserService userService; + + @GetMapping("{pesel}") + public ResponseEntity get(@PathVariable("pesel") String pesel) { + return ResponseEntity.of(userService.get(pesel)); + } + + @PostMapping + public ResponseEntity create(@Valid @RequestBody UserCreateRequest request) { + userService.create(request); + return ResponseEntity.status(204).build(); + } + +} From 2f7e7065486742e99370d90c32e299485a3e4f57 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Fri, 24 May 2024 19:37:20 +0200 Subject: [PATCH 2/7] fix: UserService hardening --- src/main/java/eu/ztsh/wymiana/service/UserService.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/eu/ztsh/wymiana/service/UserService.java b/src/main/java/eu/ztsh/wymiana/service/UserService.java index f9e7cde..0bb7945 100644 --- a/src/main/java/eu/ztsh/wymiana/service/UserService.java +++ b/src/main/java/eu/ztsh/wymiana/service/UserService.java @@ -7,6 +7,7 @@ import eu.ztsh.wymiana.util.UserMapper; import eu.ztsh.wymiana.validation.InstanceValidator; import eu.ztsh.wymiana.web.model.UserCreateRequest; import lombok.RequiredArgsConstructor; +import org.hibernate.validator.constraints.pl.PESEL; import org.springframework.stereotype.Service; import java.util.Optional; @@ -26,7 +27,7 @@ public class UserService { return UserMapper.entityToPojo(userRepository.save(UserMapper.requestToEntity(request))); } - public Optional get(String pesel) { + public Optional get(@PESEL String pesel) { return userRepository.findById(pesel).map(UserMapper::entityToPojo); } From 2981691ffc86a14634eb4f42ad0260c7bbffef2c Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Fri, 24 May 2024 19:37:56 +0200 Subject: [PATCH 3/7] test: Integration tests outline --- pom.xml | 36 +++++ .../eu/ztsh/wymiana/config/NbpProperties.java | 8 ++ .../config/RestClientConfiguration.java | 4 +- src/main/resources/application.yaml | 12 ++ .../web/ApplicationIntegrationTests.java | 131 ++++++++++++++++++ src/test/resources/application-it.yaml | 2 + 6 files changed, 191 insertions(+), 2 deletions(-) create mode 100644 src/main/java/eu/ztsh/wymiana/config/NbpProperties.java create mode 100644 src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java create mode 100644 src/test/resources/application-it.yaml diff --git a/pom.xml b/pom.xml index 3756469..257ebbb 100644 --- a/pom.xml +++ b/pom.xml @@ -51,6 +51,10 @@ org.springframework.boot spring-boot-starter-data-jpa + + org.springframework.boot + spring-boot-starter-actuator + @@ -69,6 +73,12 @@ org.springframework.boot spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-webflux + test org.junit.jupiter @@ -109,6 +119,32 @@ + + org.apache.maven.plugins + maven-surefire-plugin + + + default-test + + + **/*Tests.java + + + + + integration-tests + test + + test + + + + **/*Test.java + + + + + diff --git a/src/main/java/eu/ztsh/wymiana/config/NbpProperties.java b/src/main/java/eu/ztsh/wymiana/config/NbpProperties.java new file mode 100644 index 0000000..0ed6c42 --- /dev/null +++ b/src/main/java/eu/ztsh/wymiana/config/NbpProperties.java @@ -0,0 +1,8 @@ +package eu.ztsh.wymiana.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties("nbp") +public record NbpProperties(String baseurl) { + +} diff --git a/src/main/java/eu/ztsh/wymiana/config/RestClientConfiguration.java b/src/main/java/eu/ztsh/wymiana/config/RestClientConfiguration.java index 74ee47b..f264dbe 100644 --- a/src/main/java/eu/ztsh/wymiana/config/RestClientConfiguration.java +++ b/src/main/java/eu/ztsh/wymiana/config/RestClientConfiguration.java @@ -8,9 +8,9 @@ import org.springframework.web.client.RestClient; public class RestClientConfiguration { @Bean - public RestClient restClient() { + public RestClient restClient(NbpProperties nbpProperties) { return RestClient.builder() - .baseUrl("http://api.nbp.pl") + .baseUrl(nbpProperties.baseurl()) .defaultHeader("Accept", "application/json") .build(); } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index ae6306d..47b720c 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -2,6 +2,9 @@ hsqldb: name: db port: 9090 +nbp: + baseurl: "http://api.nbp.pl" + spring: datasource: username: sa @@ -11,3 +14,12 @@ spring: jpa: hibernate: ddl-auto: create + +management: + endpoints: + jmx: + exposure: + exclude: '*' + web: + exposure: + include: health diff --git a/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java b/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java new file mode 100644 index 0000000..8781c04 --- /dev/null +++ b/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java @@ -0,0 +1,131 @@ +package eu.ztsh.wymiana.web; + +import eu.ztsh.wymiana.WireMockExtension; +import org.junit.jupiter.api.ClassOrderer; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestClassOrder; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.reactive.server.WebTestClient; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Integration test suite. + * Contrary to the principle of test independence, tests are dependent on one another to create continuous suite. + */ +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@ActiveProfiles("it") +@ExtendWith(WireMockExtension.class) +@TestMethodOrder(MethodOrderer.DisplayName.class) +@TestClassOrder(ClassOrderer.DisplayName.class) +class ApplicationIntegrationTests { + + private final WebTestClient webTestClient; + + @Autowired + public ApplicationIntegrationTests(WebTestClient webTestClient) { + this.webTestClient = webTestClient; + } + + @Nested + @TestMethodOrder(MethodOrderer.DisplayName.class) + @DisplayName("01: Context") + class ContextTests { + + @Test + @DisplayName("01.1: Load context") + void contextLoads() { + assertThat(webTestClient).isNotNull(); + webTestClient.get().uri("/actuator/health").exchange().expectBody().json("{\"status\":\"UP\"}"); + } + + } + + @Nested + @TestMethodOrder(MethodOrderer.DisplayName.class) + @DisplayName("02: User") + class UserTests { + + @Test + @DisplayName("02.1: Create valid user") + void createUserTest() { + + } + + @Test + @DisplayName("02.2: Try to create invalid user") + void createInvalidUserTest() { + + } + + @Test + @DisplayName("02.3: Try to create duplicated user") + void createDuplicatedUserTest() { + + } + + @Test + @DisplayName("02.4: Get valid user") + void getValidUserTest() { + + } + + @Test + @DisplayName("02.5: Try to get non-existing user") + void getNonExistingUserTest() { + + } + + @Test + @DisplayName("02.6: Get user by incorrect PESEL") + void getIncorrectPeselUserTest() { + + } + + } + + @Nested + @TestMethodOrder(MethodOrderer.DisplayName.class) + @DisplayName("03: Exchange") + class ExchangeTests { + + @Test + @DisplayName("03.1: Try to perform invalid money exchange: no data") + void noNbpDataTest() { + + } + + @Test + @DisplayName("03.2: Perform valid money exchange") + void exchangeTest() { + + } + + @Test + @DisplayName("03.3: Try to perform invalid money exchange: not existing user") + void exchangeNotExistingUserTest() { + + } + + @Test + @DisplayName("03.4: Try to perform invalid money exchange: invalid PESEL") + void invalidPeselTest() { + + } + + @Test + @DisplayName("03.5: Try to perform invalid money exchange: insufficient funds") + void insufficientFundsTest() { + + } + + } + +} diff --git a/src/test/resources/application-it.yaml b/src/test/resources/application-it.yaml new file mode 100644 index 0000000..198e366 --- /dev/null +++ b/src/test/resources/application-it.yaml @@ -0,0 +1,2 @@ +nbp: + baseurl: "http://localhost:38080" From 720937bd6c1b98f64aa3d7b80dad7d26a37a9fee Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Fri, 24 May 2024 23:37:44 +0200 Subject: [PATCH 4/7] feat!: JDK21 -> pattern matching, integration tests implementation & small fixes --- .woodpecker/maven.yaml | 2 +- pom.xml | 2 +- .../web/controller/UserController.java | 18 ++- .../wymiana/web/model/UserCreateRequest.java | 3 +- .../java/eu/ztsh/wymiana/EntityCreator.java | 23 ++- .../data/repository/UserRepositoryTest.java | 2 +- .../wymiana/service/CurrencyServiceTest.java | 24 ++-- .../ztsh/wymiana/service/UserServiceTest.java | 6 +- .../eu/ztsh/wymiana/util/UserMapperTest.java | 20 +-- .../web/ApplicationIntegrationTests.java | 133 ++++++++++++++++-- 10 files changed, 184 insertions(+), 49 deletions(-) diff --git a/.woodpecker/maven.yaml b/.woodpecker/maven.yaml index 82461b2..6458761 100644 --- a/.woodpecker/maven.yaml +++ b/.woodpecker/maven.yaml @@ -1,5 +1,5 @@ variables: - &maven_image maven:3.9.6-eclipse-temurin-17-alpine + &maven_image maven:3.9.6-eclipse-temurin-21-alpine steps: - name: build diff --git a/pom.xml b/pom.xml index 257ebbb..9ae7829 100644 --- a/pom.xml +++ b/pom.xml @@ -22,7 +22,7 @@ ${source.encoding} - 17 + 21 ${java.version} ${java.version} diff --git a/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java b/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java index c9f7bdc..da83862 100644 --- a/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java +++ b/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java @@ -1,10 +1,13 @@ package eu.ztsh.wymiana.web.controller; +import eu.ztsh.wymiana.exception.UserAlreadyExistsException; import eu.ztsh.wymiana.model.User; import eu.ztsh.wymiana.service.UserService; +import eu.ztsh.wymiana.validation.ValidationFailedException; import eu.ztsh.wymiana.web.model.UserCreateRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.GetMapping; @@ -17,7 +20,7 @@ import org.springframework.web.bind.annotation.RestController; @RequiredArgsConstructor @Validated @RestController -@RequestMapping("/api/user") +@RequestMapping(path = "/api/user", produces = "application/json") public class UserController { private final UserService userService; @@ -28,8 +31,17 @@ public class UserController { } @PostMapping - public ResponseEntity create(@Valid @RequestBody UserCreateRequest request) { - userService.create(request); + public ResponseEntity create(@Valid @RequestBody UserCreateRequest request) { + try { + userService.create(request); + } catch (Exception e) { + var status = switch (e) { + case ValidationFailedException ignored -> HttpStatus.BAD_REQUEST; + case UserAlreadyExistsException ignored -> HttpStatus.CONFLICT; + default -> HttpStatus.INTERNAL_SERVER_ERROR; + }; + return ResponseEntity.status(status).body(e.getMessage()); + } return ResponseEntity.status(204).build(); } diff --git a/src/main/java/eu/ztsh/wymiana/web/model/UserCreateRequest.java b/src/main/java/eu/ztsh/wymiana/web/model/UserCreateRequest.java index 6c15a62..bd7b1b4 100644 --- a/src/main/java/eu/ztsh/wymiana/web/model/UserCreateRequest.java +++ b/src/main/java/eu/ztsh/wymiana/web/model/UserCreateRequest.java @@ -4,12 +4,13 @@ import eu.ztsh.wymiana.validation.Adult; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotNull; import lombok.Builder; +import org.hibernate.validator.constraints.pl.PESEL; @Builder public record UserCreateRequest( @NotNull String name, @NotNull String surname, - @Adult String pesel, + @PESEL @Adult String pesel, @Min(0) double pln) { } diff --git a/src/test/java/eu/ztsh/wymiana/EntityCreator.java b/src/test/java/eu/ztsh/wymiana/EntityCreator.java index c0bb186..5cb9498 100644 --- a/src/test/java/eu/ztsh/wymiana/EntityCreator.java +++ b/src/test/java/eu/ztsh/wymiana/EntityCreator.java @@ -2,13 +2,17 @@ package eu.ztsh.wymiana; import eu.ztsh.wymiana.data.entity.CurrencyEntity; import eu.ztsh.wymiana.data.entity.UserEntity; +import eu.ztsh.wymiana.model.Currency; import eu.ztsh.wymiana.model.Rate; import eu.ztsh.wymiana.model.Rates; +import eu.ztsh.wymiana.model.User; import eu.ztsh.wymiana.web.model.CurrencyExchangeRequest; import eu.ztsh.wymiana.web.model.UserCreateRequest; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Optional; public class EntityCreator { @@ -16,6 +20,8 @@ public class EntityCreator { public static class Constants { public static String PESEL = "00281018264"; + public static String INVALID_PESEL = PESEL.replace('6', '7'); + public static String ANOTHER_PESEL = "08280959342"; public static String NAME = "Janina"; public static String SURNAME = "Kowalska"; public static double PLN = 20.10; @@ -28,10 +34,25 @@ public class EntityCreator { } - public static UserEntityBuilder user() { + public static UserEntityBuilder userEntity() { return new UserEntityBuilder(); } + public static User user() { + return user(Constants.PLN, 0); + } + + public static User user(double pln, double usd) { + Map currencies = new HashMap<>(); + if (pln > 0) { + currencies.put("PLN", new Currency("PLN", pln)); + } + if (usd > 0) { + currencies.put("USD", new Currency("USD", pln)); + } + return new User(Constants.NAME, Constants.SURNAME, Constants.PESEL, currencies); + } + public static UserCreateRequest.UserCreateRequestBuilder userRequest() { return UserCreateRequest.builder().name(Constants.NAME) .surname(Constants.SURNAME) diff --git a/src/test/java/eu/ztsh/wymiana/data/repository/UserRepositoryTest.java b/src/test/java/eu/ztsh/wymiana/data/repository/UserRepositoryTest.java index 63cd478..20b7868 100644 --- a/src/test/java/eu/ztsh/wymiana/data/repository/UserRepositoryTest.java +++ b/src/test/java/eu/ztsh/wymiana/data/repository/UserRepositoryTest.java @@ -18,7 +18,7 @@ class UserRepositoryTest extends RepositoryBasedTest { @Transactional @DisplayName("Basic insert & get test") void basicTest() { - var entity = EntityCreator.user().build(); + var entity = EntityCreator.userEntity().build(); userRepository.save(entity); expect(entity); } diff --git a/src/test/java/eu/ztsh/wymiana/service/CurrencyServiceTest.java b/src/test/java/eu/ztsh/wymiana/service/CurrencyServiceTest.java index 95b1442..9603e00 100644 --- a/src/test/java/eu/ztsh/wymiana/service/CurrencyServiceTest.java +++ b/src/test/java/eu/ztsh/wymiana/service/CurrencyServiceTest.java @@ -37,7 +37,7 @@ class CurrencyServiceTest extends RepositoryBasedTest { @Transactional @Test void plnToUsdToSellSuccessTest() { - var entity = EntityCreator.user().build(); + var entity = EntityCreator.userEntity().build(); userRepository.save(entity); var result = currencyService.exchange(EntityCreator.exchangeRequest() .from(PLN_SYMBOL) @@ -46,14 +46,14 @@ class CurrencyServiceTest extends RepositoryBasedTest { .build()); assertThat(result.currencies()) .matches(map -> map.get(PLN_SYMBOL).amount() == 0 && map.get(USD_SYMBOL).amount() == USD_BUY); - var expected = EntityCreator.user().pln(0).usd(USD_BUY).build(); + var expected = EntityCreator.userEntity().pln(0).usd(USD_BUY).build(); expect(expected); } @Transactional @Test void plnToUsdToBuySuccessTest() { - var entity = EntityCreator.user().build(); + var entity = EntityCreator.userEntity().build(); userRepository.save(entity); var result = currencyService.exchange(EntityCreator.exchangeRequest() .from(PLN_SYMBOL) @@ -62,14 +62,14 @@ class CurrencyServiceTest extends RepositoryBasedTest { .build()); assertThat(result.currencies()) .matches(map -> map.get(PLN_SYMBOL).amount() == 0 && map.get(USD_SYMBOL).amount() == USD_BUY); - var expected = EntityCreator.user().pln(0).usd(USD_BUY).build(); + var expected = EntityCreator.userEntity().pln(0).usd(USD_BUY).build(); expect(expected); } @Transactional @Test void usdToPlnToSellSuccessTest() { - var entity = EntityCreator.user().pln(-1).usd(USD_SELL).build(); + var entity = EntityCreator.userEntity().pln(-1).usd(USD_SELL).build(); userRepository.save(entity); var result = currencyService.exchange(EntityCreator.exchangeRequest() .from(USD_SYMBOL) @@ -78,14 +78,14 @@ class CurrencyServiceTest extends RepositoryBasedTest { .build()); assertThat(result.currencies()) .matches(map -> map.get(PLN_SYMBOL).amount() == PLN && map.get(USD_SYMBOL).amount() == 0); - var expected = EntityCreator.user().pln(PLN).usd(0).build(); + var expected = EntityCreator.userEntity().pln(PLN).usd(0).build(); expect(expected); } @Transactional @Test void usdToPlnToBuySuccessTest() { - var entity = EntityCreator.user().pln(-1).usd(USD_SELL).build(); + var entity = EntityCreator.userEntity().pln(-1).usd(USD_SELL).build(); userRepository.save(entity); var result = currencyService.exchange(EntityCreator.exchangeRequest() .from(USD_SYMBOL) @@ -94,14 +94,14 @@ class CurrencyServiceTest extends RepositoryBasedTest { .build()); assertThat(result.currencies()) .matches(map -> map.get(PLN_SYMBOL).amount() == PLN && map.get(USD_SYMBOL).amount() == 0); - var expected = EntityCreator.user().pln(PLN).usd(0).build(); + var expected = EntityCreator.userEntity().pln(PLN).usd(0).build(); expect(expected); } @Transactional @Test void usdToPlnNoUsdCurrencyTest() { - var entity = EntityCreator.user().build(); + var entity = EntityCreator.userEntity().build(); userRepository.save(entity); var request = EntityCreator.exchangeRequest() .from(USD_SYMBOL) @@ -116,7 +116,7 @@ class CurrencyServiceTest extends RepositoryBasedTest { @Test void doubleExchangeTest() { var initialValue = 100; - var entity = EntityCreator.user().pln(initialValue).build(); + var entity = EntityCreator.userEntity().pln(initialValue).build(); userRepository.save(entity); var result1 = currencyService.exchange(EntityCreator.exchangeRequest() .from(PLN_SYMBOL) @@ -141,7 +141,7 @@ class CurrencyServiceTest extends RepositoryBasedTest { @Transactional @Test void insufficientFundsTest() { - var entity = EntityCreator.user().build(); + var entity = EntityCreator.userEntity().build(); userRepository.save(entity); var request = EntityCreator.exchangeRequest() .from(PLN_SYMBOL) @@ -203,7 +203,7 @@ class CurrencyServiceTest extends RepositoryBasedTest { } private static Stream invalidPeselTest() { - return Stream.of("INVALID", PESEL.replace('6', '7')); + return Stream.of("INVALID", INVALID_PESEL); } } diff --git a/src/test/java/eu/ztsh/wymiana/service/UserServiceTest.java b/src/test/java/eu/ztsh/wymiana/service/UserServiceTest.java index 8a0f9fc..1ab7690 100644 --- a/src/test/java/eu/ztsh/wymiana/service/UserServiceTest.java +++ b/src/test/java/eu/ztsh/wymiana/service/UserServiceTest.java @@ -31,7 +31,7 @@ class UserServiceTest extends RepositoryBasedTest { @DisplayName("Create new user") void createNewUserTest() { userService.create(EntityCreator.userRequest().build()); - var entity = EntityCreator.user().build(); + var entity = EntityCreator.userEntity().build(); expect(entity); } @@ -64,7 +64,7 @@ class UserServiceTest extends RepositoryBasedTest { @Test @DisplayName("Try to create too young user") void youngUserTest() { - var request = EntityCreator.userRequest().pesel("08280959342").build(); + var request = EntityCreator.userRequest().pesel("").build(); assertThatThrownBy(() -> userService.create(request)) .isInstanceOf(ValidationFailedException.class) .hasMessageContaining(Adult.MESSAGE); @@ -74,7 +74,7 @@ class UserServiceTest extends RepositoryBasedTest { @Transactional @DisplayName("Get existing user") void getExistingUserTest() { - var entity = EntityCreator.user().build(); + var entity = EntityCreator.userEntity().build(); userRepository.save(entity); var userOptional = userService.get(EntityCreator.Constants.PESEL); var expected = UserMapper.entityToPojo(entity); diff --git a/src/test/java/eu/ztsh/wymiana/util/UserMapperTest.java b/src/test/java/eu/ztsh/wymiana/util/UserMapperTest.java index dc1eeb8..11503aa 100644 --- a/src/test/java/eu/ztsh/wymiana/util/UserMapperTest.java +++ b/src/test/java/eu/ztsh/wymiana/util/UserMapperTest.java @@ -13,13 +13,8 @@ class UserMapperTest { @Test void entityToPojoTest() { - var entity = EntityCreator.user().build(); - var expected = new User( - EntityCreator.Constants.NAME, - EntityCreator.Constants.SURNAME, - EntityCreator.Constants.PESEL, - Map.of("PLN", new Currency("PLN", EntityCreator.Constants.PLN)) - ); + var entity = EntityCreator.userEntity().build(); + var expected = EntityCreator.user(); assertThat(UserMapper.entityToPojo(entity)) .usingRecursiveComparison() .isEqualTo(expected); @@ -27,13 +22,8 @@ class UserMapperTest { @Test void pojoToEntityTest() { - var entity = new User( - EntityCreator.Constants.NAME, - EntityCreator.Constants.SURNAME, - EntityCreator.Constants.PESEL, - Map.of("PLN", new Currency("PLN", EntityCreator.Constants.PLN)) - ); - var expected = EntityCreator.user().build(); + var entity = EntityCreator.user(); + var expected = EntityCreator.userEntity().build(); assertThat(UserMapper.pojoToEntity(entity)) .usingRecursiveComparison() .isEqualTo(expected); @@ -42,7 +32,7 @@ class UserMapperTest { @Test void requestToEntityTest() { var request = EntityCreator.userRequest().build(); - var expected = EntityCreator.user().build(); + var expected = EntityCreator.userEntity().build(); assertThat(UserMapper.requestToEntity(request)) .usingRecursiveComparison() .isEqualTo(expected); diff --git a/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java b/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java index 8781c04..38fdfae 100644 --- a/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java +++ b/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java @@ -1,6 +1,11 @@ package eu.ztsh.wymiana.web; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import eu.ztsh.wymiana.EntityCreator; import eu.ztsh.wymiana.WireMockExtension; +import eu.ztsh.wymiana.model.User; +import eu.ztsh.wymiana.util.UserMapper; import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.MethodOrderer; @@ -14,6 +19,12 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.reactive.server.WebTestClient; +import java.time.DayOfWeek; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAdjusters; + +import static eu.ztsh.wymiana.EntityCreator.Constants.*; import static org.assertj.core.api.Assertions.assertThat; /** @@ -28,10 +39,20 @@ import static org.assertj.core.api.Assertions.assertThat; class ApplicationIntegrationTests { private final WebTestClient webTestClient; + private final ObjectMapper objectMapper; @Autowired public ApplicationIntegrationTests(WebTestClient webTestClient) { this.webTestClient = webTestClient; + objectMapper = new ObjectMapper(); + } + + private String asJson(Object object) { + try { + return objectMapper.writeValueAsString(object); + } catch (JsonProcessingException e) { + throw new IllegalStateException(e); + } } @Nested @@ -53,40 +74,66 @@ class ApplicationIntegrationTests { @DisplayName("02: User") class UserTests { + private static final String endpoint = "/api/user"; + @Test @DisplayName("02.1: Create valid user") void createUserTest() { - + webTestClient.post() + .uri(endpoint) + .bodyValue(EntityCreator.userRequest().pln(100).build()) + .exchange() + .expectStatus().is2xxSuccessful(); } @Test @DisplayName("02.2: Try to create invalid user") void createInvalidUserTest() { - + webTestClient.post() + .uri(endpoint) + .bodyValue(EntityCreator.userRequest().pesel(INVALID_PESEL).build()) + .exchange() + .expectStatus() + .is4xxClientError(); } @Test @DisplayName("02.3: Try to create duplicated user") void createDuplicatedUserTest() { - + webTestClient.post() + .uri(endpoint) + .bodyValue(EntityCreator.userRequest().build()) + .exchange() + .expectStatus().is4xxClientError() + .expectBody(String.class).isEqualTo("User with PESEL %s already exists".formatted(PESEL)); } @Test @DisplayName("02.4: Get valid user") void getValidUserTest() { - + webTestClient.get() + .uri(endpoint.concat("/").concat(PESEL)) + .exchange() + .expectStatus().is2xxSuccessful() + .expectBody().json(asJson(UserMapper.entityToPojo(EntityCreator.userEntity().pln(100).build()))); } @Test @DisplayName("02.5: Try to get non-existing user") void getNonExistingUserTest() { - + webTestClient.get() + .uri(endpoint.concat("/").concat(ANOTHER_PESEL)) + .exchange() + .expectStatus().is4xxClientError(); } @Test @DisplayName("02.6: Get user by incorrect PESEL") void getIncorrectPeselUserTest() { - + webTestClient.get() + .uri(endpoint.concat("/").concat(INVALID_PESEL)) + .exchange() + .expectStatus().is4xxClientError(); } } @@ -96,34 +143,98 @@ class ApplicationIntegrationTests { @DisplayName("03: Exchange") class ExchangeTests { + private static final String URI_PATTERN = "/api/exchangerates/rates/c/usd/%s/"; + private final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + private static final String endpoint = "/api/exchange"; + @Test @DisplayName("03.1: Try to perform invalid money exchange: no data") void noNbpDataTest() { - + webTestClient.post() + .uri(endpoint) + .bodyValue(EntityCreator.exchangeRequest() + .from(PLN_SYMBOL) + .to(USD_SYMBOL) + .toSell(PLN) + .build()) + .exchange() + .expectStatus().is5xxServerError(); } @Test @DisplayName("03.2: Perform valid money exchange") - void exchangeTest() { - + void exchangeTest() throws JsonProcessingException { + var date = getTodayOrLastFriday(); + WireMockExtension.response( + URI_PATTERN.formatted(date), + 200, + new ObjectMapper().writeValueAsString(EntityCreator.rates(date)) + ); + webTestClient.post() + .uri(endpoint) + .bodyValue(EntityCreator.exchangeRequest() + .from(PLN_SYMBOL) + .to(USD_SYMBOL) + .toSell(PLN) + .build()) + .exchange() + .expectStatus().is2xxSuccessful() + .expectBody(User.class).isEqualTo(EntityCreator.user(100 - PLN, USD_BUY)); } @Test @DisplayName("03.3: Try to perform invalid money exchange: not existing user") void exchangeNotExistingUserTest() { - + webTestClient.post() + .uri(endpoint) + .bodyValue(EntityCreator.exchangeRequest() + .pesel(ANOTHER_PESEL) + .from(PLN_SYMBOL) + .to(USD_SYMBOL) + .toSell(PLN) + .build()) + .exchange() + .expectStatus().is4xxClientError() + .expectBody(String.class).isEqualTo("An exchange error has occurred"); } @Test @DisplayName("03.4: Try to perform invalid money exchange: invalid PESEL") void invalidPeselTest() { - + webTestClient.post() + .uri(endpoint) + .bodyValue(EntityCreator.exchangeRequest() + .pesel(INVALID_PESEL) + .from(PLN_SYMBOL) + .to(USD_SYMBOL) + .toSell(PLN) + .build()) + .exchange() + .expectStatus().is4xxClientError(); } @Test @DisplayName("03.5: Try to perform invalid money exchange: insufficient funds") void insufficientFundsTest() { + webTestClient.post() + .uri(endpoint) + .bodyValue(EntityCreator.exchangeRequest() + .pesel(ANOTHER_PESEL) + .from(USD_SYMBOL) + .to(PLN_SYMBOL) + .toBuy(PLN) + .build()) + .exchange() + .expectStatus().is4xxClientError(); + } + private String getTodayOrLastFriday() { + var today = LocalDate.now(); + if (today.getDayOfWeek() == DayOfWeek.SATURDAY + || today.getDayOfWeek() == DayOfWeek.SUNDAY) { + today = today.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY)); + } + return today.format(dtf); } } From 95ed5f6ae73c2a53fb301a7ce31b90170d8a72d3 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Sat, 25 May 2024 00:33:33 +0200 Subject: [PATCH 5/7] feat: ExchangeController & tests --- .../exception/UserNotFoundException.java | 11 +++++ .../ztsh/wymiana/service/CurrencyService.java | 3 +- .../eu/ztsh/wymiana/service/UserService.java | 2 + .../web/controller/ExchangeController.java | 42 +++++++++++++++++++ .../web/controller/UserController.java | 9 +++- .../java/eu/ztsh/wymiana/EntityCreator.java | 2 +- .../wymiana/service/CurrencyServiceTest.java | 3 +- .../web/ApplicationIntegrationTests.java | 34 +++++++-------- 8 files changed, 85 insertions(+), 21 deletions(-) create mode 100644 src/main/java/eu/ztsh/wymiana/exception/UserNotFoundException.java create mode 100644 src/main/java/eu/ztsh/wymiana/web/controller/ExchangeController.java diff --git a/src/main/java/eu/ztsh/wymiana/exception/UserNotFoundException.java b/src/main/java/eu/ztsh/wymiana/exception/UserNotFoundException.java new file mode 100644 index 0000000..7390eb3 --- /dev/null +++ b/src/main/java/eu/ztsh/wymiana/exception/UserNotFoundException.java @@ -0,0 +1,11 @@ +package eu.ztsh.wymiana.exception; + +import eu.ztsh.wymiana.web.model.CurrencyExchangeRequest; + +public class UserNotFoundException extends RuntimeException { + + public UserNotFoundException(CurrencyExchangeRequest entity) { + super("User with PESEL %s not found".formatted(entity.pesel())); + } + +} diff --git a/src/main/java/eu/ztsh/wymiana/service/CurrencyService.java b/src/main/java/eu/ztsh/wymiana/service/CurrencyService.java index b00f761..401626a 100644 --- a/src/main/java/eu/ztsh/wymiana/service/CurrencyService.java +++ b/src/main/java/eu/ztsh/wymiana/service/CurrencyService.java @@ -2,6 +2,7 @@ package eu.ztsh.wymiana.service; import eu.ztsh.wymiana.exception.ExchangeFailedException; import eu.ztsh.wymiana.exception.InsufficientFundsException; +import eu.ztsh.wymiana.exception.UserNotFoundException; import eu.ztsh.wymiana.model.Currency; import eu.ztsh.wymiana.model.User; import eu.ztsh.wymiana.validation.InstanceValidator; @@ -49,7 +50,7 @@ public class CurrencyService { user.currencies().putAll(exchanged); return userService.update(user); }) - .orElseThrow(ExchangeFailedException::new); + .orElseThrow(() -> new UserNotFoundException(request)); } private Currency create(String symbol) { diff --git a/src/main/java/eu/ztsh/wymiana/service/UserService.java b/src/main/java/eu/ztsh/wymiana/service/UserService.java index 0bb7945..857bc0f 100644 --- a/src/main/java/eu/ztsh/wymiana/service/UserService.java +++ b/src/main/java/eu/ztsh/wymiana/service/UserService.java @@ -9,9 +9,11 @@ import eu.ztsh.wymiana.web.model.UserCreateRequest; import lombok.RequiredArgsConstructor; import org.hibernate.validator.constraints.pl.PESEL; import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; import java.util.Optional; +@Validated @RequiredArgsConstructor @Service public class UserService { diff --git a/src/main/java/eu/ztsh/wymiana/web/controller/ExchangeController.java b/src/main/java/eu/ztsh/wymiana/web/controller/ExchangeController.java new file mode 100644 index 0000000..ae666ef --- /dev/null +++ b/src/main/java/eu/ztsh/wymiana/web/controller/ExchangeController.java @@ -0,0 +1,42 @@ +package eu.ztsh.wymiana.web.controller; + +import eu.ztsh.wymiana.exception.InsufficientFundsException; +import eu.ztsh.wymiana.exception.UserAlreadyExistsException; +import eu.ztsh.wymiana.exception.UserNotFoundException; +import eu.ztsh.wymiana.service.CurrencyService; +import eu.ztsh.wymiana.validation.ValidationFailedException; +import eu.ztsh.wymiana.web.model.CurrencyExchangeRequest; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +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; + +@RequiredArgsConstructor +@Validated +@RestController +@RequestMapping(path = "/api/exchange", produces = "application/json") +public class ExchangeController { + + private final CurrencyService currencyService; + + @PostMapping + public ResponseEntity exchange(@Valid @RequestBody CurrencyExchangeRequest request) { + try { + return ResponseEntity.status(200).body(currencyService.exchange(request)); + } catch (Exception e) { + var status = switch (e) { + case InsufficientFundsException ignored -> HttpStatus.BAD_REQUEST; + case UserNotFoundException ignored -> HttpStatus.NOT_FOUND; + default -> HttpStatus.INTERNAL_SERVER_ERROR; + }; + return ResponseEntity.status(status).body(e.getMessage()); + } + } + +} diff --git a/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java b/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java index da83862..319d93a 100644 --- a/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java +++ b/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java @@ -6,6 +6,7 @@ import eu.ztsh.wymiana.service.UserService; import eu.ztsh.wymiana.validation.ValidationFailedException; import eu.ztsh.wymiana.web.model.UserCreateRequest; import jakarta.validation.Valid; +import jakarta.validation.ValidationException; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; @@ -27,7 +28,13 @@ public class UserController { @GetMapping("{pesel}") public ResponseEntity get(@PathVariable("pesel") String pesel) { - return ResponseEntity.of(userService.get(pesel)); + try { + return userService.get(pesel) + .map(ResponseEntity::ok) + .orElse(ResponseEntity.notFound().build()); + } catch (ValidationException e) { + return ResponseEntity.badRequest().build(); + } } @PostMapping diff --git a/src/test/java/eu/ztsh/wymiana/EntityCreator.java b/src/test/java/eu/ztsh/wymiana/EntityCreator.java index 5cb9498..617cbfa 100644 --- a/src/test/java/eu/ztsh/wymiana/EntityCreator.java +++ b/src/test/java/eu/ztsh/wymiana/EntityCreator.java @@ -48,7 +48,7 @@ public class EntityCreator { currencies.put("PLN", new Currency("PLN", pln)); } if (usd > 0) { - currencies.put("USD", new Currency("USD", pln)); + currencies.put("USD", new Currency("USD", usd)); } return new User(Constants.NAME, Constants.SURNAME, Constants.PESEL, currencies); } diff --git a/src/test/java/eu/ztsh/wymiana/service/CurrencyServiceTest.java b/src/test/java/eu/ztsh/wymiana/service/CurrencyServiceTest.java index 9603e00..c44625e 100644 --- a/src/test/java/eu/ztsh/wymiana/service/CurrencyServiceTest.java +++ b/src/test/java/eu/ztsh/wymiana/service/CurrencyServiceTest.java @@ -5,6 +5,7 @@ import eu.ztsh.wymiana.RepositoryBasedTest; import eu.ztsh.wymiana.data.repository.UserRepository; import eu.ztsh.wymiana.exception.ExchangeFailedException; import eu.ztsh.wymiana.exception.InsufficientFundsException; +import eu.ztsh.wymiana.exception.UserNotFoundException; import eu.ztsh.wymiana.validation.InstanceValidator; import eu.ztsh.wymiana.validation.ValidationFailedException; import jakarta.transaction.Transactional; @@ -175,7 +176,7 @@ class CurrencyServiceTest extends RepositoryBasedTest { .toSell(USD_SELL) .build(); assertThatThrownBy(() -> currencyService.exchange(entity)) - .isInstanceOf(ExchangeFailedException.class); + .isInstanceOf(UserNotFoundException.class); } @Test diff --git a/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java b/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java index 38fdfae..816cc99 100644 --- a/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java +++ b/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java @@ -4,7 +4,6 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import eu.ztsh.wymiana.EntityCreator; import eu.ztsh.wymiana.WireMockExtension; -import eu.ztsh.wymiana.model.User; import eu.ztsh.wymiana.util.UserMapper; import org.junit.jupiter.api.ClassOrderer; import org.junit.jupiter.api.DisplayName; @@ -83,7 +82,7 @@ class ApplicationIntegrationTests { .uri(endpoint) .bodyValue(EntityCreator.userRequest().pln(100).build()) .exchange() - .expectStatus().is2xxSuccessful(); + .expectStatus().isNoContent(); } @Test @@ -94,7 +93,7 @@ class ApplicationIntegrationTests { .bodyValue(EntityCreator.userRequest().pesel(INVALID_PESEL).build()) .exchange() .expectStatus() - .is4xxClientError(); + .isBadRequest(); } @Test @@ -104,7 +103,7 @@ class ApplicationIntegrationTests { .uri(endpoint) .bodyValue(EntityCreator.userRequest().build()) .exchange() - .expectStatus().is4xxClientError() + .expectStatus().isEqualTo(409) .expectBody(String.class).isEqualTo("User with PESEL %s already exists".formatted(PESEL)); } @@ -114,7 +113,7 @@ class ApplicationIntegrationTests { webTestClient.get() .uri(endpoint.concat("/").concat(PESEL)) .exchange() - .expectStatus().is2xxSuccessful() + .expectStatus().isOk() .expectBody().json(asJson(UserMapper.entityToPojo(EntityCreator.userEntity().pln(100).build()))); } @@ -124,7 +123,7 @@ class ApplicationIntegrationTests { webTestClient.get() .uri(endpoint.concat("/").concat(ANOTHER_PESEL)) .exchange() - .expectStatus().is4xxClientError(); + .expectStatus().isNotFound(); } @Test @@ -133,7 +132,7 @@ class ApplicationIntegrationTests { webTestClient.get() .uri(endpoint.concat("/").concat(INVALID_PESEL)) .exchange() - .expectStatus().is4xxClientError(); + .expectStatus().isBadRequest(); } } @@ -158,18 +157,19 @@ class ApplicationIntegrationTests { .toSell(PLN) .build()) .exchange() - .expectStatus().is5xxServerError(); + .expectStatus().isEqualTo(500); } @Test @DisplayName("03.2: Perform valid money exchange") - void exchangeTest() throws JsonProcessingException { + void exchangeTest() { var date = getTodayOrLastFriday(); WireMockExtension.response( URI_PATTERN.formatted(date), 200, - new ObjectMapper().writeValueAsString(EntityCreator.rates(date)) + asJson(EntityCreator.rates(date)) ); + var expected = asJson(EntityCreator.user(100 - PLN, USD_BUY)); webTestClient.post() .uri(endpoint) .bodyValue(EntityCreator.exchangeRequest() @@ -178,8 +178,9 @@ class ApplicationIntegrationTests { .toSell(PLN) .build()) .exchange() - .expectStatus().is2xxSuccessful() - .expectBody(User.class).isEqualTo(EntityCreator.user(100 - PLN, USD_BUY)); + .expectStatus().isOk() + .expectBody().json(expected); + WireMockExtension.verifyGet(2, URI_PATTERN.formatted(date)); } @Test @@ -194,8 +195,8 @@ class ApplicationIntegrationTests { .toSell(PLN) .build()) .exchange() - .expectStatus().is4xxClientError() - .expectBody(String.class).isEqualTo("An exchange error has occurred"); + .expectStatus().isNotFound() + .expectBody(String.class).isEqualTo("User with PESEL %s not found".formatted(ANOTHER_PESEL)); } @Test @@ -210,7 +211,7 @@ class ApplicationIntegrationTests { .toSell(PLN) .build()) .exchange() - .expectStatus().is4xxClientError(); + .expectStatus().isBadRequest(); } @Test @@ -219,13 +220,12 @@ class ApplicationIntegrationTests { webTestClient.post() .uri(endpoint) .bodyValue(EntityCreator.exchangeRequest() - .pesel(ANOTHER_PESEL) .from(USD_SYMBOL) .to(PLN_SYMBOL) .toBuy(PLN) .build()) .exchange() - .expectStatus().is4xxClientError(); + .expectStatus().isBadRequest(); } private String getTodayOrLastFriday() { From d0e21e9eff4671575051ad7d9cc60fe0b28db695 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Sat, 25 May 2024 00:36:32 +0200 Subject: [PATCH 6/7] test: Dummy fix --- .../java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java b/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java index 816cc99..272c140 100644 --- a/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java +++ b/src/test/java/eu/ztsh/wymiana/web/ApplicationIntegrationTests.java @@ -180,7 +180,7 @@ class ApplicationIntegrationTests { .exchange() .expectStatus().isOk() .expectBody().json(expected); - WireMockExtension.verifyGet(2, URI_PATTERN.formatted(date)); + WireMockExtension.verifyGet(1, URI_PATTERN.formatted(date)); } @Test From ec0ee860eed779925c8af6ffdd234596de3267b3 Mon Sep 17 00:00:00 2001 From: Piotr Dec Date: Sat, 25 May 2024 00:47:12 +0200 Subject: [PATCH 7/7] fix: SonarLint fixes --- .../ztsh/wymiana/exception/UserNotFoundException.java | 6 +++++- .../wymiana/web/controller/ExchangeController.java | 3 --- .../ztsh/wymiana/web/controller/UserController.java | 11 ++++++++--- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/main/java/eu/ztsh/wymiana/exception/UserNotFoundException.java b/src/main/java/eu/ztsh/wymiana/exception/UserNotFoundException.java index 7390eb3..3cf38c2 100644 --- a/src/main/java/eu/ztsh/wymiana/exception/UserNotFoundException.java +++ b/src/main/java/eu/ztsh/wymiana/exception/UserNotFoundException.java @@ -5,7 +5,11 @@ import eu.ztsh.wymiana.web.model.CurrencyExchangeRequest; public class UserNotFoundException extends RuntimeException { public UserNotFoundException(CurrencyExchangeRequest entity) { - super("User with PESEL %s not found".formatted(entity.pesel())); + this(entity.pesel()); + } + + public UserNotFoundException(String pesel) { + super("User with PESEL %s not found".formatted(pesel)); } } diff --git a/src/main/java/eu/ztsh/wymiana/web/controller/ExchangeController.java b/src/main/java/eu/ztsh/wymiana/web/controller/ExchangeController.java index ae666ef..fb0ee13 100644 --- a/src/main/java/eu/ztsh/wymiana/web/controller/ExchangeController.java +++ b/src/main/java/eu/ztsh/wymiana/web/controller/ExchangeController.java @@ -1,14 +1,11 @@ package eu.ztsh.wymiana.web.controller; import eu.ztsh.wymiana.exception.InsufficientFundsException; -import eu.ztsh.wymiana.exception.UserAlreadyExistsException; import eu.ztsh.wymiana.exception.UserNotFoundException; import eu.ztsh.wymiana.service.CurrencyService; -import eu.ztsh.wymiana.validation.ValidationFailedException; import eu.ztsh.wymiana.web.model.CurrencyExchangeRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpEntity; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.annotation.Validated; diff --git a/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java b/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java index 319d93a..bca74ce 100644 --- a/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java +++ b/src/main/java/eu/ztsh/wymiana/web/controller/UserController.java @@ -1,6 +1,7 @@ package eu.ztsh.wymiana.web.controller; import eu.ztsh.wymiana.exception.UserAlreadyExistsException; +import eu.ztsh.wymiana.exception.UserNotFoundException; import eu.ztsh.wymiana.model.User; import eu.ztsh.wymiana.service.UserService; import eu.ztsh.wymiana.validation.ValidationFailedException; @@ -31,9 +32,13 @@ public class UserController { try { return userService.get(pesel) .map(ResponseEntity::ok) - .orElse(ResponseEntity.notFound().build()); - } catch (ValidationException e) { - return ResponseEntity.badRequest().build(); + .orElseThrow(() -> new UserNotFoundException(pesel)); + } catch (Exception e) { + return ResponseEntity.status(switch (e) { + case ValidationException ignore -> 400; + case UserNotFoundException ignore -> 404; + default -> 500; + }).build(); } }