package com.danielbohry.stocks.repository; import com.danielbohry.stocks.client.StockClient; import com.danielbohry.stocks.domain.Quote; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import feign.FeignException; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Repository; import java.math.BigDecimal; import java.time.Instant; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; import static java.time.LocalDateTime.now; import static java.util.stream.Collectors.toList; @Slf4j @Repository public class StockRepository { private final QuoteRepository repository; private final StockClient client; private final String key; public StockRepository(QuoteRepository repository, StockClient client, @Value("${clients.stock.key}") String key) { this.repository = repository; this.client = client; this.key = key; } public List findAll() { return repository.findAll(); } public List findLike(String query) { return repository.findByNameOrCode(query); } public Quote findByCode(String code) { Optional quote = repository.findByCode(code); return quote.orElseGet(() -> repository.save(getStockQuote(code))); } public List update(List quotes) { log.info("Updating [{}] quotes [{}]", quotes.size(), Instant.now()); List codes = quotes.stream() .map(Quote::getCode) .collect(toList()); Map existingQuotesMap = repository.findByCodeIn(codes) .stream() .collect(Collectors.toMap(Quote::getCode, Function.identity())); log.info("Found [{}] quotes by codes [{}]", existingQuotesMap.size(), Instant.now()); List updatedQuotes = quotes.stream() .map(quote -> { Quote existingQuote = existingQuotesMap.get(quote.getCode()); if (existingQuote != null) { quote.setId(existingQuote.getId()); } return quote; }) .collect(toList()); log.info("Updating [{}] quotes [{}]", updatedQuotes.size(), Instant.now()); List result = repository.saveAll(updatedQuotes); log.info("Update complete for [{}] quotes [{}]", quotes.size(), Instant.now()); return result; } public boolean isValid(String code) { Quote quote = repository.findByCode(code).stream().findFirst().orElse(null); if (quote != null && "BRL".equals(quote.getCurrency())) return true; try { log.info("Current stock's name is null. Requesting latest information..."); client.getStockInfo(code, key); return true; } catch (FeignException.NotFound e) { return false; } } public Quote getStockQuote(String code) { Quote quote = repository.findByCode(code).stream().findFirst().orElse(new Quote(code, null, null, null, now())); quote.setPrice(getLastPrice(quote)); if (quote.getName() == null || quote.getPrice() == null) { StockInfoResponse info = updateStockInformation(quote.getCode()); quote.setName(info.getName()); repository.save(quote); } return quote; } private StockInfoResponse updateStockInformation(String code) { log.info("Current stock's name is null. Requesting latest information..."); return client.getStockInfo(code, key); } private BigDecimal getLastPrice(Quote quote) { if (quote.getPrice() == null && "USD".equals(quote.getCurrency())) { log.info("Current quote for [{}] is null. Requesting latest quote...", quote); return new BigDecimal(client.getStockQuote(quote.getCode(), key).get(0).getLastPrice()); } else if (quote.getUpdatedAt().isBefore(now().minusDays(5))) { log.info("Current quote for [{}] is older than 1 day. Requesting latest quote...", quote); return new BigDecimal(client.getStockQuote(quote.getCode(), key).get(0).getLastPrice()); } else { return quote.getPrice(); } } @Data @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) public static class StockQuoteResponse { @JsonProperty("adjClose") private String lastPrice; @JsonProperty("adjOpen") private String openPrice; } @Data @AllArgsConstructor @NoArgsConstructor @JsonIgnoreProperties(ignoreUnknown = true) public static class StockInfoResponse { @JsonProperty("ticker") private String code; @JsonProperty("name") private String name; @JsonProperty("exchangeCode") private String exchange; } }