cache = new ConcurrentHashMap<>(1);
-
- public BigDecimal getSellRate(Symbol currency) {
- return getCurrency(currency).sell();
- }
-
- public BigDecimal getBuyRate(Symbol currency) {
- return getCurrency(currency).buy();
- }
-
- private synchronized RatesCache getCurrency(Symbol currency) {
- var today = getFetchDate();
- var cacheObject = cache.get(currency);
- if (cacheObject == null || cacheObject.date().isBefore(today)) {
- var fresh = fetchData(currency, dtf.format(today));
- var rate = fresh.getRates().getFirst();
- cacheObject = new RatesCache(
- LocalDate.parse(rate.getEffectiveDate(), dtf),
- rate.getBid(),
- rate.getAsk()
- );
- cache.put(Symbol.valueOf(fresh.getCode().toUpperCase()), cacheObject);
- }
- return cacheObject;
- }
-
- /**
- * Calculates date for data fetch.
- *
- * Usually this would be today, but as rates are set only Mon-Fri, during weekends it is needed to fetch last friday rates.
- *
- * @return date for data fetch
- */
- @VisibleForTesting
- LocalDate getFetchDate() {
- var today = LocalDate.now(clock);
- return isWeekend(today) ? today.with(TemporalAdjusters.previous(DayOfWeek.FRIDAY)) : today;
- }
-
- @VisibleForTesting
- Rates fetchData(Symbol code, String date) {
- return restClient.get().uri(URI_PATTERN, code.name().toLowerCase(), date)
- .retrieve()
- .onStatus(HttpStatusCode::is4xxClientError, ((request, response) -> {
- throw new NoDataException(code, date);
- }))
- .body(Rates.class);
- }
-
- private static boolean isWeekend(LocalDate today) {
- return today.getDayOfWeek() == DayOfWeek.SATURDAY
- || today.getDayOfWeek() == DayOfWeek.SUNDAY;
- }
-
- private record RatesCache(LocalDate date, BigDecimal buy, BigDecimal sell) {
-
- }
-
-}
diff --git a/src/main/java/eu/ztsh/wymiana/service/UserService.java b/src/main/java/eu/ztsh/wymiana/service/UserService.java
index 2205d19..01cba7c 100644
--- a/src/main/java/eu/ztsh/wymiana/service/UserService.java
+++ b/src/main/java/eu/ztsh/wymiana/service/UserService.java
@@ -1,45 +1,31 @@
package eu.ztsh.wymiana.service;
-import eu.ztsh.wymiana.config.CurrencyProperties;
import eu.ztsh.wymiana.data.repository.UserRepository;
import eu.ztsh.wymiana.exception.UserAlreadyExistsException;
import eu.ztsh.wymiana.model.User;
-import eu.ztsh.wymiana.model.UserCreateRequestConfiguredWrapper;
import eu.ztsh.wymiana.util.UserMapper;
-import eu.ztsh.wymiana.validation.InstanceValidator;
import eu.ztsh.wymiana.web.model.UserCreateRequest;
+import jakarta.validation.Valid;
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 {
private final UserRepository userRepository;
- private final InstanceValidator validator;
- private final CurrencyProperties currencyProperties;
- public User create(UserCreateRequest request) {
- validator.validate(request);
+ public User create(@Valid UserCreateRequest request) {
if (userRepository.findById(request.pesel()).isPresent()) {
throw new UserAlreadyExistsException(request);
}
- return UserMapper.entityToPojo(userRepository.save(UserMapper.requestToEntity(
- UserCreateRequestConfiguredWrapper.wrap(request).withInitial(currencyProperties.initial()).build()
- )));
+ return UserMapper.entityToPojo(userRepository.save(UserMapper.requestToEntity(request)));
}
- public Optional get(@PESEL String pesel) {
+ public Optional get(String pesel) {
return userRepository.findById(pesel).map(UserMapper::entityToPojo);
}
- public User update(User user) {
- return UserMapper.entityToPojo(userRepository.save(UserMapper.pojoToEntity(user)));
- }
-
}
diff --git a/src/main/java/eu/ztsh/wymiana/util/CurrencyMapper.java b/src/main/java/eu/ztsh/wymiana/util/CurrencyMapper.java
index 697e956..7542187 100644
--- a/src/main/java/eu/ztsh/wymiana/util/CurrencyMapper.java
+++ b/src/main/java/eu/ztsh/wymiana/util/CurrencyMapper.java
@@ -2,7 +2,6 @@ package eu.ztsh.wymiana.util;
import eu.ztsh.wymiana.data.entity.CurrencyEntity;
import eu.ztsh.wymiana.model.Currency;
-import eu.ztsh.wymiana.model.Symbol;
import java.util.List;
import java.util.Map;
@@ -14,19 +13,11 @@ public class CurrencyMapper {
return new Currency(entity.getSymbol(), entity.getAmount());
}
- public static CurrencyEntity pojoToEntity(Currency pojo, String pesel) {
- return new CurrencyEntity(pesel, pojo.symbol(), pojo.amount());
- }
-
- public static Map entitiesToPojoMap(List values) {
+ public static Map entitiesToPojoMap(List values) {
return values.stream().map(CurrencyMapper::entityToPojo)
.collect(Collectors.toMap(Currency::symbol, pojo -> pojo));
}
- public static List pojoMapToEntities(Map currencies, String pesel) {
- return currencies.values().stream().map(entry -> pojoToEntity(entry, pesel)).toList();
- }
-
private CurrencyMapper() {
}
diff --git a/src/main/java/eu/ztsh/wymiana/util/UserMapper.java b/src/main/java/eu/ztsh/wymiana/util/UserMapper.java
index 31c2a20..b4aff44 100644
--- a/src/main/java/eu/ztsh/wymiana/util/UserMapper.java
+++ b/src/main/java/eu/ztsh/wymiana/util/UserMapper.java
@@ -3,8 +3,7 @@ package eu.ztsh.wymiana.util;
import eu.ztsh.wymiana.data.entity.CurrencyEntity;
import eu.ztsh.wymiana.data.entity.UserEntity;
import eu.ztsh.wymiana.model.User;
-import eu.ztsh.wymiana.model.UserCreateRequestConfiguredWrapper;
-import eu.ztsh.wymiana.web.model.UserResponse;
+import eu.ztsh.wymiana.web.model.UserCreateRequest;
import java.util.List;
@@ -15,18 +14,9 @@ public class UserMapper {
CurrencyMapper.entitiesToPojoMap(entity.getCurrencies()));
}
- public static UserEntity pojoToEntity(User pojo) {
- return new UserEntity(pojo.pesel(), pojo.name(), pojo.surname(),
- CurrencyMapper.pojoMapToEntities(pojo.currencies(), pojo.pesel()));
- }
-
- public static UserResponse pojoToResponse(User pojo) {
- return new UserResponse(pojo.name(), pojo.surname(), pojo.pesel(), pojo.currencies().values().stream().toList());
- }
-
- public static UserEntity requestToEntity(UserCreateRequestConfiguredWrapper request) {
+ public static UserEntity requestToEntity(UserCreateRequest request) {
return new UserEntity(request.pesel(), request.name(), request.surname(),
- List.of(new CurrencyEntity(request.pesel(), request.initialSymbol(), request.initial())));
+ List.of(new CurrencyEntity(request.pesel(), "PLN", request.pln())));
}
private UserMapper() {
diff --git a/src/main/java/eu/ztsh/wymiana/validation/Adult.java b/src/main/java/eu/ztsh/wymiana/validation/Adult.java
index 0d7448f..affba90 100644
--- a/src/main/java/eu/ztsh/wymiana/validation/Adult.java
+++ b/src/main/java/eu/ztsh/wymiana/validation/Adult.java
@@ -16,12 +16,9 @@ import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Constraint(validatedBy = AdultValidator.class)
@Documented
public @interface Adult {
- String message() default MESSAGE;
+ String message() default "{jakarta.validation.constraints.Adult.message}";
Class>[] groups() default { };
Class extends Payload>[] payload() default { };
-
- String MESSAGE = "The person has not reached the age of 18";
-
}
diff --git a/src/main/java/eu/ztsh/wymiana/validation/InstanceValidator.java b/src/main/java/eu/ztsh/wymiana/validation/InstanceValidator.java
deleted file mode 100644
index 664f5c4..0000000
--- a/src/main/java/eu/ztsh/wymiana/validation/InstanceValidator.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package eu.ztsh.wymiana.validation;
-
-import jakarta.validation.Validator;
-
-public class InstanceValidator {
-
- private final Validator validator;
-
- public InstanceValidator(Validator validator) {
- this.validator = validator;
- }
-
- public void validate(T t, Class>... classes) {
- var violations = validator.validate(t, classes);
- if (!violations.isEmpty()) {
- throw new ValidationFailedException(violations);
- }
- }
-
-}
diff --git a/src/main/java/eu/ztsh/wymiana/validation/InstanceValidatorFactory.java b/src/main/java/eu/ztsh/wymiana/validation/InstanceValidatorFactory.java
deleted file mode 100644
index 69289ab..0000000
--- a/src/main/java/eu/ztsh/wymiana/validation/InstanceValidatorFactory.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package eu.ztsh.wymiana.validation;
-
-import jakarta.validation.Validation;
-import jakarta.validation.ValidatorFactory;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-
-@Configuration
-public class InstanceValidatorFactory {
-
- @Bean
- public InstanceValidator instanceValidator() {
- try (ValidatorFactory factory = Validation.buildDefaultValidatorFactory()) {
- return new InstanceValidator(factory.getValidator());
- }
- }
-
-}
diff --git a/src/main/java/eu/ztsh/wymiana/validation/ValidExchangeRequest.java b/src/main/java/eu/ztsh/wymiana/validation/ValidExchangeRequest.java
deleted file mode 100644
index 608bc3c..0000000
--- a/src/main/java/eu/ztsh/wymiana/validation/ValidExchangeRequest.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package eu.ztsh.wymiana.validation;
-
-import jakarta.validation.Constraint;
-import jakarta.validation.Payload;
-
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
-import java.lang.annotation.Target;
-
-import static java.lang.annotation.ElementType.TYPE_USE;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
-
-
-@Retention(RUNTIME)
-@Target({ TYPE_USE })
-@Documented
-@Constraint(validatedBy = {ValidExchangeRequestValidator.class })
-public @interface ValidExchangeRequest {
-
- String message() default "Exchange request is not valid";
- Class>[] groups() default { };
- Class extends Payload>[] payload() default { };
-
-}
diff --git a/src/main/java/eu/ztsh/wymiana/validation/ValidExchangeRequestValidator.java b/src/main/java/eu/ztsh/wymiana/validation/ValidExchangeRequestValidator.java
deleted file mode 100644
index 9a01a0c..0000000
--- a/src/main/java/eu/ztsh/wymiana/validation/ValidExchangeRequestValidator.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package eu.ztsh.wymiana.validation;
-
-import eu.ztsh.wymiana.web.model.CurrencyExchangeRequest;
-import jakarta.validation.ConstraintValidator;
-import jakarta.validation.ConstraintValidatorContext;
-
-public class ValidExchangeRequestValidator implements
- ConstraintValidator {
-
- @Override
- public boolean isValid(CurrencyExchangeRequest request,
- ConstraintValidatorContext constraintValidatorContext) {
- if (request == null) {
- return false;
- }
-
- // Apart from @NotNull annotation we need to check if request.from() != null to avoid NPE in equals
- return (request.from() != null && !request.from().equals(request.to()))
- && !((request.toBuy() == null && request.toSell() == null)
- || (request.toBuy() != null && request.toSell() != null))
- && ((request.toBuy() != null && request.toBuy().signum() >= 0)
- || (request.toSell() != null && request.toSell().signum() >= 0));
- }
-
-}
diff --git a/src/main/java/eu/ztsh/wymiana/validation/ValidationFailedException.java b/src/main/java/eu/ztsh/wymiana/validation/ValidationFailedException.java
deleted file mode 100644
index 7ed17fa..0000000
--- a/src/main/java/eu/ztsh/wymiana/validation/ValidationFailedException.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package eu.ztsh.wymiana.validation;
-
-import jakarta.validation.ConstraintViolation;
-import lombok.Getter;
-
-import java.util.Set;
-import java.util.stream.Collectors;
-
-@Getter
-public class ValidationFailedException extends RuntimeException {
-
- private final transient Set> violations;
-
- public ValidationFailedException(Set> violations) {
- super("Validation failed: %s".formatted(violations.stream()
- .map(ConstraintViolation::getMessage).collect(Collectors.joining(System.lineSeparator()))));
- this.violations = violations;
- }
-
-}
diff --git a/src/main/java/eu/ztsh/wymiana/web/controller/ExchangeController.java b/src/main/java/eu/ztsh/wymiana/web/controller/ExchangeController.java
deleted file mode 100644
index 39c635d..0000000
--- a/src/main/java/eu/ztsh/wymiana/web/controller/ExchangeController.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package eu.ztsh.wymiana.web.controller;
-
-import eu.ztsh.wymiana.exception.InsufficientFundsException;
-import eu.ztsh.wymiana.exception.UserNotFoundException;
-import eu.ztsh.wymiana.service.CurrencyService;
-import eu.ztsh.wymiana.web.model.CurrencyExchangeRequest;
-import eu.ztsh.wymiana.web.model.UserResponse;
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.media.Content;
-import io.swagger.v3.oas.annotations.media.Schema;
-import io.swagger.v3.oas.annotations.responses.ApiResponse;
-import io.swagger.v3.oas.annotations.responses.ApiResponses;
-import jakarta.validation.Valid;
-import lombok.RequiredArgsConstructor;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-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;
-
- @Operation(summary = "Perform exchange")
- @ApiResponses(value = {
- @ApiResponse(responseCode = "200",
- description = "Exchange performed successfully",
- content = @Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
- schema = @Schema(implementation = UserResponse.class))),
- @ApiResponse(responseCode = "400",
- description = "Insufficient funds",
- content = @Content(mediaType = MediaType.TEXT_PLAIN_VALUE)),
- @ApiResponse(responseCode = "404",
- description = "User not found",
- content = @Content(mediaType = MediaType.TEXT_PLAIN_VALUE)),
- @ApiResponse(responseCode = "500",
- description = "Another error has occurred",
- content = @Content(mediaType = MediaType.TEXT_PLAIN_VALUE))
- })
- @PostMapping
- public ResponseEntity