Преглед на файлове

normalize entities and collections names

Daniel Bohry преди 7 месеца
родител
ревизия
4d200c76f6
променени са 25 файла, в които са добавени 150 реда и са изтрити 151 реда
  1. 7 7
      src/main/java/com/danielbohry/stocks/api/portfolio/PortfolioHistoryController.java
  2. 2 2
      src/main/java/com/danielbohry/stocks/api/portfolio/PortfolioResponse.java
  3. 2 2
      src/main/java/com/danielbohry/stocks/api/portfolio/UpdatePortfolioRequest.java
  4. 10 10
      src/main/java/com/danielbohry/stocks/api/stock/StockController.java
  5. 3 3
      src/main/java/com/danielbohry/stocks/client/FinanceClient.java
  6. 3 3
      src/main/java/com/danielbohry/stocks/client/InferenceClient.java
  7. 6 6
      src/main/java/com/danielbohry/stocks/domain/Portfolio.java
  8. 4 6
      src/main/java/com/danielbohry/stocks/domain/PortfolioHistory.java
  9. 22 0
      src/main/java/com/danielbohry/stocks/domain/PortfolioStock.java
  10. 0 33
      src/main/java/com/danielbohry/stocks/domain/Quote.java
  11. 17 4
      src/main/java/com/danielbohry/stocks/domain/Stock.java
  12. 1 1
      src/main/java/com/danielbohry/stocks/domain/StockInfo.java
  13. 4 4
      src/main/java/com/danielbohry/stocks/repository/portfolio/PortfolioEntity.java
  14. 2 2
      src/main/java/com/danielbohry/stocks/repository/snapshot/PortfolioHistoryEntity.java
  15. 2 2
      src/main/java/com/danielbohry/stocks/repository/snapshot/SnapshotRepository.java
  16. 5 5
      src/main/java/com/danielbohry/stocks/repository/stock/QuoteRepository.java
  17. 1 1
      src/main/java/com/danielbohry/stocks/repository/stock/StockHistory.java
  18. 23 23
      src/main/java/com/danielbohry/stocks/repository/stock/StockRepository.java
  19. 8 9
      src/main/java/com/danielbohry/stocks/service/PortfolioService.java
  20. 7 7
      src/main/java/com/danielbohry/stocks/service/SnapshotService.java
  21. 3 3
      src/main/java/com/danielbohry/stocks/service/StockEncryptService.java
  22. 5 5
      src/main/java/com/danielbohry/stocks/service/StockInfoService.java
  23. 7 7
      src/main/java/com/danielbohry/stocks/service/StockService.java
  24. 2 2
      src/test/java/service/PortfolioServiceTest.java
  25. 4 4
      src/test/java/service/StockServiceTest.java

+ 7 - 7
src/main/java/com/danielbohry/stocks/api/portfolio/SnapshotController.java → src/main/java/com/danielbohry/stocks/api/portfolio/PortfolioHistoryController.java

@@ -1,6 +1,6 @@
 package com.danielbohry.stocks.api.portfolio;
 
-import com.danielbohry.stocks.domain.snapshot.PortfolioSnapshot;
+import com.danielbohry.stocks.domain.PortfolioHistory;
 import com.danielbohry.stocks.service.SnapshotService;
 import lombok.AllArgsConstructor;
 import org.springframework.http.HttpStatus;
@@ -13,18 +13,18 @@ import java.util.List;
 @RequestMapping("api/portfolios/{portfolioId}")
 @AllArgsConstructor
 @CrossOrigin
-public class SnapshotController {
+public class PortfolioHistoryController {
 
     private final SnapshotService service;
 
-    @GetMapping("snapshots")
-    public ResponseEntity<?> getSnapshots(@PathVariable String portfolioId) {
-        List<PortfolioSnapshot> response = service.getSnapshots(portfolioId);
+    @GetMapping("history")
+    public ResponseEntity<?> getPortfolioHistory(@PathVariable String portfolioId) {
+        List<PortfolioHistory> response = service.getSnapshots(portfolioId);
         return ResponseEntity.ok(response);
     }
 
-    @PostMapping("snapshots")
-    public ResponseEntity<Void> createSnapshot(@PathVariable String portfolioId) {
+    @PostMapping("history")
+    public ResponseEntity<Void> savePortfolioHistory(@PathVariable String portfolioId) {
         service.createSnapshot(portfolioId);
         return ResponseEntity.status(HttpStatus.CREATED).build();
     }

+ 2 - 2
src/main/java/com/danielbohry/stocks/api/portfolio/PortfolioResponse.java

@@ -1,12 +1,12 @@
 package com.danielbohry.stocks.api.portfolio;
 
 import com.danielbohry.stocks.domain.Portfolio;
-import com.danielbohry.stocks.domain.Stock;
+import com.danielbohry.stocks.domain.PortfolioStock;
 
 import java.math.BigDecimal;
 import java.util.List;
 
-public record PortfolioResponse(String id, List<Stock> stocks, BigDecimal totalValue, Integer totalAssets) {
+public record PortfolioResponse(String id, List<PortfolioStock> stocks, BigDecimal totalValue, Integer totalAssets) {
 
     public static PortfolioResponse to(Portfolio portfolio) {
         return new PortfolioResponse(

+ 2 - 2
src/main/java/com/danielbohry/stocks/api/portfolio/UpdatePortfolioRequest.java

@@ -1,8 +1,8 @@
 package com.danielbohry.stocks.api.portfolio;
 
-import com.danielbohry.stocks.domain.Stock;
+import com.danielbohry.stocks.domain.PortfolioStock;
 
 import java.util.List;
 
-public record UpdatePortfolioRequest(List<Stock> stocks) {
+public record UpdatePortfolioRequest(List<PortfolioStock> stocks) {
 }

+ 10 - 10
src/main/java/com/danielbohry/stocks/api/stock/StockController.java

@@ -1,7 +1,7 @@
 package com.danielbohry.stocks.api.stock;
 
 import com.danielbohry.stocks.context.UserContextHolder;
-import com.danielbohry.stocks.domain.Quote;
+import com.danielbohry.stocks.domain.Stock;
 import com.danielbohry.stocks.domain.StockInfo;
 import com.danielbohry.stocks.service.StockInfoService;
 import com.danielbohry.stocks.service.StockService;
@@ -19,7 +19,7 @@ import java.util.List;
 import java.util.Objects;
 
 import static java.nio.charset.StandardCharsets.UTF_8;
-import static java.time.LocalDateTime.now;
+import static java.time.Instant.now;
 
 @Slf4j
 @RestController
@@ -32,8 +32,8 @@ public class StockController {
     private final StockInfoService infoService;
 
     @GetMapping
-    public ResponseEntity<List<Quote>> find(@RequestParam(value = "q", required = false) String query) {
-        List<Quote> response = (query == null || query.isBlank())
+    public ResponseEntity<List<Stock>> find(@RequestParam(value = "q", required = false) String query) {
+        List<Stock> response = (query == null || query.isBlank())
             ? service.getAll()
             : service.get(query);
 
@@ -41,8 +41,8 @@ public class StockController {
     }
 
     @GetMapping("{code}")
-    public ResponseEntity<Quote> getByCode(@PathVariable String code) {
-        Quote response = service.getByCode(code.toUpperCase());
+    public ResponseEntity<Stock> getByCode(@PathVariable String code) {
+        Stock response = service.getByCode(code.toUpperCase());
         return ResponseEntity.ok(response);
     }
 
@@ -60,12 +60,12 @@ public class StockController {
         }
 
         try (BufferedReader reader = new BufferedReader(new InputStreamReader(file.getInputStream(), UTF_8))) {
-            List<Quote> quotes = reader.lines()
+            List<Stock> quotes = reader.lines()
                 .map(r -> convert(r, currency))
                 .filter(Objects::nonNull)
                 .toList();
 
-            List<Quote> response = service.update(quotes);
+            List<Stock> response = service.update(quotes);
 
             return ResponseEntity.ok(new StockUploadCSVResponse(response.size() + " entries updated."));
         } catch (Exception e) {
@@ -84,7 +84,7 @@ public class StockController {
         return ResponseEntity.ok().build();
     }
 
-    private Quote convert(String input, String currency) {
+    private Stock convert(String input, String currency) {
         String[] value = input.split(",");
         log.info("Importing [{}]", (Object) value);
 
@@ -111,7 +111,7 @@ public class StockController {
             }
         }
 
-        return new Quote(value[0], value[1], currency, price, marketCap, now());
+        return new Stock(value[0], value[1], currency, price, marketCap, now());
     }
 
 }

+ 3 - 3
src/main/java/com/danielbohry/stocks/client/FinanceClient.java

@@ -1,6 +1,6 @@
 package com.danielbohry.stocks.client;
 
-import com.danielbohry.stocks.domain.Quote;
+import com.danielbohry.stocks.domain.Stock;
 import com.danielbohry.stocks.domain.StockInfo;
 import com.danielbohry.stocks.repository.stock.StockRepository.StockMetadataResponse;
 import lombok.RequiredArgsConstructor;
@@ -24,11 +24,11 @@ public class FinanceClient {
     @Value("${clients.stock.key}")
     private String key;
 
-    public List<StockInfo> fetchStockInfo(List<Quote> quotes) {
+    public List<StockInfo> fetchStockInfo(List<Stock> quotes) {
         List<StockInfo> response = new ArrayList<>();
         try {
             String[] tickers = quotes.stream()
-                .map(Quote::getCode)
+                .map(Stock::getCode)
                 .filter(ObjectUtils::isNotEmpty)
                 .toArray(String[]::new);
 

+ 3 - 3
src/main/java/com/danielbohry/stocks/client/InferenceClient.java

@@ -1,6 +1,6 @@
 package com.danielbohry.stocks.client;
 
-import com.danielbohry.stocks.domain.Quote;
+import com.danielbohry.stocks.domain.Stock;
 import com.danielbohry.stocks.domain.StockInfo;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import lombok.AllArgsConstructor;
@@ -54,7 +54,7 @@ public class InferenceClient {
 
     private static final ObjectMapper mapper = new ObjectMapper();
 
-    public StockInfo infer(Quote quote) {
+    public StockInfo infer(Stock quote) {
         String inference = infer(PROMPT + quote.getCode());
 
         return buildStockInfo(quote, inference);
@@ -85,7 +85,7 @@ public class InferenceClient {
         }
     }
 
-    private StockInfo buildStockInfo(Quote stock, String inference) {
+    private StockInfo buildStockInfo(Stock stock, String inference) {
         try {
             String cleanInference = inference.replaceAll("(?i)^[^{]*\\{", "{").replaceAll("[^}]*$", "}").trim();
             Inference parsed = mapper.readValue(cleanInference, Inference.class);

+ 6 - 6
src/main/java/com/danielbohry/stocks/domain/Portfolio.java

@@ -5,7 +5,7 @@ import lombok.Builder;
 import lombok.Data;
 
 import java.math.BigDecimal;
-import java.time.LocalDateTime;
+import java.time.Instant;
 import java.util.List;
 
 @Data
@@ -13,16 +13,16 @@ import java.util.List;
 public class Portfolio {
 
     private String id;
-    private List<Stock> stocks;
+    private List<PortfolioStock> stocks;
     private BigDecimal totalValue;
     private Integer totalAssets;
     private String user;
-    private LocalDateTime createdAt;
-    private LocalDateTime updatedAt;
+    private Instant createdAt;
+    private Instant updatedAt;
 
     public BigDecimal getTotalValue() {
         return this.stocks.stream()
-            .map(Stock::getTotal)
+            .map(PortfolioStock::getTotal)
             .reduce(BigDecimal.ZERO, BigDecimal::add);
     }
 
@@ -36,7 +36,7 @@ public class Portfolio {
         return Portfolio.builder()
             .id(entity.getId())
             .stocks(entity.getStocks().stream()
-                .map(stock -> new Stock(stock.getCode(), null, stock.getQuantity(), BigDecimal.ZERO, BigDecimal.ZERO))
+                .map(stock -> new PortfolioStock(stock.getCode(), null, stock.getQuantity(), BigDecimal.ZERO, BigDecimal.ZERO))
                 .toList())
             .user(entity.getUser())
             .createdAt(entity.getCreatedAt())

+ 4 - 6
src/main/java/com/danielbohry/stocks/domain/snapshot/PortfolioSnapshot.java → src/main/java/com/danielbohry/stocks/domain/PortfolioHistory.java

@@ -1,10 +1,8 @@
-package com.danielbohry.stocks.domain.snapshot;
+package com.danielbohry.stocks.domain;
 
-import com.danielbohry.stocks.domain.Stock;
 import lombok.AllArgsConstructor;
 import lombok.Builder;
 import lombok.Data;
-import lombok.NoArgsConstructor;
 
 import java.math.BigDecimal;
 import java.time.Instant;
@@ -13,17 +11,17 @@ import java.util.List;
 @Data
 @Builder
 @AllArgsConstructor
-public class PortfolioSnapshot {
+public class PortfolioHistory {
 
     private String portfolioId;
     private BigDecimal totalValue;
     private Integer totalAssets;
-    private List<Stock> stocks;
+    private List<PortfolioStock> stocks;
     private Instant createdAt;
 
     public BigDecimal getTotalValue() {
         return stocks.stream()
-            .map(Stock::getTotal)
+            .map(PortfolioStock::getTotal)
             .reduce(BigDecimal.ZERO, BigDecimal::add);
     }
 

+ 22 - 0
src/main/java/com/danielbohry/stocks/domain/PortfolioStock.java

@@ -0,0 +1,22 @@
+package com.danielbohry.stocks.domain;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.math.BigDecimal;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class PortfolioStock {
+
+    private String code;
+    private String name;
+    private int quantity;
+    private BigDecimal price;
+    private BigDecimal total;
+
+}

+ 0 - 33
src/main/java/com/danielbohry/stocks/domain/Quote.java

@@ -1,33 +0,0 @@
-package com.danielbohry.stocks.domain;
-
-import lombok.AllArgsConstructor;
-import lombok.Data;
-import lombok.NoArgsConstructor;
-import org.springframework.data.annotation.Id;
-
-import java.math.BigDecimal;
-import java.time.LocalDateTime;
-
-@Data
-@AllArgsConstructor
-@NoArgsConstructor
-public class Quote {
-
-    @Id
-    private String id;
-    private String code;
-    private String name;
-    private String currency;
-    private BigDecimal price;
-    private BigDecimal marketCap;
-    private LocalDateTime updatedAt;
-
-    public Quote(String code, String name, String currency, BigDecimal price, BigDecimal marketCap, LocalDateTime updatedAt) {
-        this.code = code;
-        this.name = name;
-        this.currency = currency;
-        this.price = price;
-        this.marketCap = marketCap;
-        this.updatedAt = updatedAt;
-    }
-}

+ 17 - 4
src/main/java/com/danielbohry/stocks/domain/Stock.java

@@ -1,22 +1,35 @@
 package com.danielbohry.stocks.domain;
 
 import lombok.AllArgsConstructor;
-import lombok.Builder;
 import lombok.Data;
 import lombok.NoArgsConstructor;
+import org.springframework.data.annotation.Id;
+import org.springframework.data.mongodb.core.mapping.Document;
 
 import java.math.BigDecimal;
+import java.time.Instant;
 
 @Data
-@Builder
 @AllArgsConstructor
 @NoArgsConstructor
+@Document("stocks")
 public class Stock {
 
+    @Id
+    private String id;
     private String code;
     private String name;
-    private int quantity;
+    private String currency;
     private BigDecimal price;
-    private BigDecimal total;
+    private BigDecimal marketCap;
+    private Instant updatedAt;
 
+    public Stock(String code, String name, String currency, BigDecimal price, BigDecimal marketCap, Instant updatedAt) {
+        this.code = code;
+        this.name = name;
+        this.currency = currency;
+        this.price = price;
+        this.marketCap = marketCap;
+        this.updatedAt = updatedAt;
+    }
 }

+ 1 - 1
src/main/java/com/danielbohry/stocks/domain/StockInfo.java

@@ -10,7 +10,7 @@ import java.time.Instant;
 
 @Data
 @Builder
-@Document("stock-metadata")
+@Document("stock_metadata")
 public class StockInfo {
 
     @Id

+ 4 - 4
src/main/java/com/danielbohry/stocks/repository/portfolio/PortfolioEntity.java

@@ -7,7 +7,7 @@ import lombok.NoArgsConstructor;
 import org.springframework.data.annotation.Transient;
 import org.springframework.data.mongodb.core.mapping.Document;
 
-import java.time.LocalDateTime;
+import java.time.Instant;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -15,14 +15,14 @@ import java.util.List;
 @AllArgsConstructor
 @NoArgsConstructor
 @Builder
-@Document("portfolio")
+@Document("portfolios")
 public class PortfolioEntity {
 
     private String id;
     private String encryptedStocks;
     private String user;
-    private LocalDateTime createdAt;
-    private LocalDateTime updatedAt;
+    private Instant createdAt;
+    private Instant updatedAt;
 
     @Transient
     private List<PortfolioStock> stocks = new ArrayList<>();

+ 2 - 2
src/main/java/com/danielbohry/stocks/repository/snapshot/PortfolioSnapshotEntity.java → src/main/java/com/danielbohry/stocks/repository/snapshot/PortfolioHistoryEntity.java

@@ -8,8 +8,8 @@ import java.time.Instant;
 
 @Data
 @Builder
-@Document("snapshot")
-public class PortfolioSnapshotEntity {
+@Document("portfolio_history")
+public class PortfolioHistoryEntity {
 
     private String portfolioId;
     private String encryptedStocks;

+ 2 - 2
src/main/java/com/danielbohry/stocks/repository/snapshot/SnapshotRepository.java

@@ -6,8 +6,8 @@ import org.springframework.stereotype.Repository;
 import java.util.List;
 
 @Repository
-public interface SnapshotRepository extends MongoRepository<PortfolioSnapshotEntity, String> {
+public interface SnapshotRepository extends MongoRepository<PortfolioHistoryEntity, String> {
 
-    List<PortfolioSnapshotEntity> findAllByPortfolioId(String portfolioId);
+    List<PortfolioHistoryEntity> findAllByPortfolioId(String portfolioId);
 
 }

+ 5 - 5
src/main/java/com/danielbohry/stocks/repository/stock/QuoteRepository.java

@@ -1,6 +1,6 @@
 package com.danielbohry.stocks.repository.stock;
 
-import com.danielbohry.stocks.domain.Quote;
+import com.danielbohry.stocks.domain.Stock;
 import org.springframework.data.mongodb.repository.MongoRepository;
 import org.springframework.data.mongodb.repository.Query;
 import org.springframework.stereotype.Repository;
@@ -9,13 +9,13 @@ import java.util.List;
 import java.util.Optional;
 
 @Repository
-public interface QuoteRepository extends MongoRepository<Quote, String> {
+public interface QuoteRepository extends MongoRepository<Stock, String> {
 
     @Query("{ $or: [ { 'name': { $regex: ?0, $options: 'i' } }, { 'code': { $regex: ?0, $options: 'i' } } ] }")
-    List<Quote> findByNameOrCode(String query);
+    List<Stock> findByNameOrCode(String query);
 
-    Optional<Quote> findByCode(String code);
+    Optional<Stock> findByCode(String code);
 
-    List<Quote> findByCodeIn(List<String> codes);
+    List<Stock> findByCodeIn(List<String> codes);
 
 }

+ 1 - 1
src/main/java/com/danielbohry/stocks/repository/stock/StockHistory.java

@@ -11,7 +11,7 @@ import java.time.Instant;
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
-@Document("stock-history")
+@Document("stock_history")
 public class StockHistory {
 
     private String code;

+ 23 - 23
src/main/java/com/danielbohry/stocks/repository/stock/StockRepository.java

@@ -1,7 +1,7 @@
 package com.danielbohry.stocks.repository.stock;
 
 import com.danielbohry.stocks.client.StockClient;
-import com.danielbohry.stocks.domain.Quote;
+import com.danielbohry.stocks.domain.Stock;
 import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import feign.FeignException;
@@ -13,14 +13,14 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Repository;
 
 import java.math.BigDecimal;
-import java.time.Instant;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.function.Function;
 import java.util.stream.Collectors;
 
-import static java.time.LocalDateTime.now;
+import static java.time.Instant.now;
+import static java.time.temporal.ChronoUnit.DAYS;
 import static java.util.stream.Collectors.toList;
 
 @Slf4j
@@ -37,49 +37,49 @@ public class StockRepository {
         this.key = key;
     }
 
-    public List<Quote> findAll() {
+    public List<Stock> findAll() {
         return repository.findAll();
     }
 
-    public List<Quote> findLike(String query) {
+    public List<Stock> findLike(String query) {
         return repository.findByNameOrCode(query);
     }
 
-    public Quote findByCode(String code) {
-        Optional<Quote> quote = repository.findByCode(code)
+    public Stock findByCode(String code) {
+        Optional<Stock> quote = repository.findByCode(code)
             .or(() -> repository.findByNameOrCode(code).stream().findFirst());
         return quote.orElseGet(() -> repository.save(getStockQuote(code)));
     }
 
-    public List<Quote> update(List<Quote> quotes) {
-        log.info("Updating [{}] quotes [{}]", quotes.size(), Instant.now());
+    public List<Stock> update(List<Stock> quotes) {
+        log.info("Updating [{}] quotes [{}]", quotes.size(), now());
         List<String> codes = quotes.stream()
-            .map(Quote::getCode)
+            .map(Stock::getCode)
             .collect(toList());
-        Map<String, Quote> existingQuotesMap = repository.findByCodeIn(codes)
+        Map<String, Stock> existingQuotesMap = repository.findByCodeIn(codes)
             .stream()
-            .collect(Collectors.toMap(Quote::getCode, Function.identity()));
+            .collect(Collectors.toMap(Stock::getCode, Function.identity()));
 
-        log.info("Found [{}] quotes by codes [{}]", existingQuotesMap.size(), Instant.now());
+        log.info("Found [{}] quotes by codes [{}]", existingQuotesMap.size(), now());
 
-        List<Quote> updatedQuotes = quotes.stream()
+        List<Stock> updatedQuotes = quotes.stream()
             .peek(quote -> {
-                Quote existingQuote = existingQuotesMap.get(quote.getCode());
+                Stock existingQuote = existingQuotesMap.get(quote.getCode());
                 if (existingQuote != null) {
                     quote.setId(existingQuote.getId());
                 }
             })
             .toList();
 
-        log.info("Updating [{}] quotes [{}]", updatedQuotes.size(), Instant.now());
-        List<Quote> result = repository.saveAll(updatedQuotes);
-        log.info("Update complete for [{}] quotes [{}]", quotes.size(), Instant.now());
+        log.info("Updating [{}] quotes [{}]", updatedQuotes.size(), now());
+        List<Stock> result = repository.saveAll(updatedQuotes);
+        log.info("Update complete for [{}] quotes [{}]", quotes.size(), now());
 
         return result;
     }
 
     public boolean isValid(String code) {
-        Quote quote = repository.findByCode(code).stream().findFirst().orElse(null);
+        Stock quote = repository.findByCode(code).stream().findFirst().orElse(null);
 
         if (quote != null) return true;
 
@@ -92,8 +92,8 @@ public class StockRepository {
         }
     }
 
-    public Quote getStockQuote(String code) {
-        Quote quote = repository.findByCode(code).stream().findFirst().orElse(new Quote(code, null, null, null, null, now()));
+    public Stock getStockQuote(String code) {
+        Stock quote = repository.findByCode(code).stream().findFirst().orElse(new Stock(code, null, null, null, null, now()));
         quote.setPrice(getLastPrice(quote));
 
         if (quote.getName() == null || quote.getPrice() == null) {
@@ -110,11 +110,11 @@ public class StockRepository {
         return client.getStockInfo(code, key);
     }
 
-    private BigDecimal getLastPrice(Quote quote) {
+    private BigDecimal getLastPrice(Stock 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))) {
+        } else if (quote.getUpdatedAt().isBefore(now().minus(5, DAYS))) {
             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 {

+ 8 - 9
src/main/java/com/danielbohry/stocks/service/PortfolioService.java

@@ -2,13 +2,12 @@ package com.danielbohry.stocks.service;
 
 import com.danielbohry.stocks.context.UserContextHolder;
 import com.danielbohry.stocks.domain.Portfolio;
-import com.danielbohry.stocks.domain.Quote;
 import com.danielbohry.stocks.domain.Stock;
+import com.danielbohry.stocks.domain.PortfolioStock;
 import com.danielbohry.stocks.exception.BadRequestException;
 import com.danielbohry.stocks.exception.NotFoundException;
 import com.danielbohry.stocks.exception.UnauthorizedException;
 import com.danielbohry.stocks.repository.portfolio.PortfolioEntity;
-import com.danielbohry.stocks.repository.portfolio.PortfolioEntity.PortfolioStock;
 import com.danielbohry.stocks.repository.portfolio.PortfolioRepository;
 import com.danielbohry.stocks.service.ExchangeService.ExchangeRateResponse;
 import lombok.AllArgsConstructor;
@@ -24,7 +23,7 @@ import java.util.Objects;
 import java.util.UUID;
 
 import static com.danielbohry.stocks.domain.Portfolio.convert;
-import static java.time.LocalDateTime.now;
+import static java.time.Instant.now;
 import static java.util.Collections.emptyList;
 
 @Slf4j
@@ -62,9 +61,9 @@ public class PortfolioService {
         BigDecimal targetRate = rates.getOrDefault(currency, BigDecimal.ONE);
 
         log.info("Getting portfolio [{}] and converting to [{}]", id, currency);
-        List<Stock> updatedStocks = portfolio.getStocks().stream()
+        List<PortfolioStock> updatedStocks = portfolio.getStocks().stream()
             .peek(stock -> {
-                Quote quote = stockService.getStockQuote(stock.getCode());
+                Stock quote = stockService.getStockQuote(stock.getCode());
                 stock.setName(quote.getName());
 
                 BigDecimal quoteRate = rates.getOrDefault(quote.getCurrency(), BigDecimal.ONE);
@@ -96,7 +95,7 @@ public class PortfolioService {
         return convert(repository.save(toSave));
     }
 
-    public Portfolio update(String id, List<Stock> stocks) {
+    public Portfolio update(String id, List<PortfolioStock> stocks) {
         log.info("Updating portfolio [{}]", id);
         PortfolioEntity toUpdate = repository.findById(id).orElseThrow(() -> new NotFoundException("Failed to update portfolio with id: " + id));
 
@@ -108,13 +107,13 @@ public class PortfolioService {
 
         toUpdate.setUpdatedAt(now());
         toUpdate.setEncryptedStocks(encryptService.encryptStocks(stocks.stream()
-            .map(stock -> new PortfolioStock(stock.getCode(), stock.getQuantity()))
+            .map(stock -> new PortfolioEntity.PortfolioStock(stock.getCode(), stock.getQuantity()))
             .toList()));
         toUpdate.setStocks(null);
 
         PortfolioEntity updated = repository.save(toUpdate);
 
-        List<PortfolioStock> decryptStocks = encryptService.decryptStocks(updated.getEncryptedStocks());
+        List<PortfolioEntity.PortfolioStock> decryptStocks = encryptService.decryptStocks(updated.getEncryptedStocks());
         updated.setStocks(decryptStocks);
 
         return convert(updated);
@@ -134,7 +133,7 @@ public class PortfolioService {
         });
     }
 
-    private void validate(List<Stock> stocks) {
+    private void validate(List<PortfolioStock> stocks) {
         boolean anyInvalid = stocks.stream()
             .anyMatch(stock -> !stockService.isValid(stock.getCode()));
 

+ 7 - 7
src/main/java/com/danielbohry/stocks/service/SnapshotService.java

@@ -1,8 +1,8 @@
 package com.danielbohry.stocks.service;
 
 import com.danielbohry.stocks.domain.Portfolio;
-import com.danielbohry.stocks.domain.snapshot.PortfolioSnapshot;
-import com.danielbohry.stocks.repository.snapshot.PortfolioSnapshotEntity;
+import com.danielbohry.stocks.domain.PortfolioHistory;
+import com.danielbohry.stocks.repository.snapshot.PortfolioHistoryEntity;
 import com.danielbohry.stocks.repository.snapshot.SnapshotRepository;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -36,9 +36,9 @@ public class SnapshotService {
         saveSnapshots(portfolios);
     }
 
-    public List<PortfolioSnapshot> getSnapshots(String portfolioId) {
+    public List<PortfolioHistory> getSnapshots(String portfolioId) {
         return repository.findAllByPortfolioId(portfolioId).stream()
-            .map(entity -> PortfolioSnapshot.builder()
+            .map(entity -> PortfolioHistory.builder()
                 .portfolioId(entity.getPortfolioId())
                 .stocks(encryptService.decryptStocks(entity.getEncryptedStocks()))
                 .createdAt(entity.getCreatedAt())
@@ -54,12 +54,12 @@ public class SnapshotService {
     public void saveSnapshots(List<Portfolio> portfolios) {
         if (portfolios.isEmpty()) return;
 
-        List<PortfolioSnapshotEntity> snapshots = portfolios.stream()
+        List<PortfolioHistoryEntity> snapshots = portfolios.stream()
             .filter(portfolio -> !portfolio.getStocks().isEmpty())
-            .map(portfolio -> new PortfolioSnapshot(portfolio.getId(), portfolio.getTotalValue(), portfolio.getTotalAssets(), portfolio.getStocks(), now()))
+            .map(portfolio -> new PortfolioHistory(portfolio.getId(), portfolio.getTotalValue(), portfolio.getTotalAssets(), portfolio.getStocks(), now()))
             .map(portfolio -> {
                 String encryptedStocks = encryptService.encryptStocks(portfolio.getStocks());
-                return PortfolioSnapshotEntity.builder()
+                return PortfolioHistoryEntity.builder()
                     .portfolioId(portfolio.getPortfolioId())
                     .encryptedStocks(encryptedStocks)
                     .createdAt(now())

+ 3 - 3
src/main/java/com/danielbohry/stocks/service/StockEncryptService.java

@@ -1,6 +1,6 @@
 package com.danielbohry.stocks.service;
 
-import com.danielbohry.stocks.domain.Stock;
+import com.danielbohry.stocks.domain.PortfolioStock;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import lombok.AllArgsConstructor;
@@ -16,7 +16,7 @@ public class StockEncryptService {
 
     private static final ObjectMapper MAPPER = new ObjectMapper();
 
-    public String encryptStocks(List<Stock> stocks) {
+    public String encryptStocks(List<PortfolioStock> stocks) {
         try {
             String toEncrypt = MAPPER.writeValueAsString(stocks);
             return service.encrypt(toEncrypt);
@@ -25,7 +25,7 @@ public class StockEncryptService {
         }
     }
 
-    public List<Stock> decryptStocks(String encrypted) {
+    public List<PortfolioStock> decryptStocks(String encrypted) {
         try {
             String data = service.decrypt(encrypted);
             return MAPPER.readValue(data, new TypeReference<>() {

+ 5 - 5
src/main/java/com/danielbohry/stocks/service/StockInfoService.java

@@ -1,7 +1,7 @@
 package com.danielbohry.stocks.service;
 
 import com.danielbohry.stocks.client.FinanceClient;
-import com.danielbohry.stocks.domain.Quote;
+import com.danielbohry.stocks.domain.Stock;
 import com.danielbohry.stocks.domain.StockInfo;
 import com.danielbohry.stocks.exception.NotFoundException;
 import com.danielbohry.stocks.repository.stock.StockInfoRepository;
@@ -34,17 +34,17 @@ public class StockInfoService {
 
     @Async("stockInfoExecutor")
     public void generate() {
-        List<Quote> stocks = stockRepository.findAll();
+        List<Stock> stocks = stockRepository.findAll();
 
         Set<String> existingIds = infoRepository.findAllById(
                 stocks.stream()
-                    .map(Quote::getCode)
+                    .map(Stock::getCode)
                     .collect(toSet())
             ).stream()
             .map(StockInfo::getCode)
             .collect(toSet());
 
-        List<Quote> request = stocks.stream()
+        List<Stock> request = stocks.stream()
             .filter(stock -> !existingIds.contains(stock.getCode()))
             .filter(stock -> !stock.getCode().contains(":"))
             .toList();
@@ -52,7 +52,7 @@ public class StockInfoService {
         int batchSize = 20;
         for (int i = 0; i < request.size(); i += batchSize) {
             int end = Math.min(i + batchSize, request.size());
-            List<Quote> batch = request.subList(i, end);
+            List<Stock> batch = request.subList(i, end);
             List<StockInfo> stockInfos = client.fetchStockInfo(batch);
             infoRepository.saveAll(stockInfos);
         }

+ 7 - 7
src/main/java/com/danielbohry/stocks/service/StockService.java

@@ -1,6 +1,6 @@
 package com.danielbohry.stocks.service;
 
-import com.danielbohry.stocks.domain.Quote;
+import com.danielbohry.stocks.domain.Stock;
 import com.danielbohry.stocks.repository.stock.StockRepository;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -21,15 +21,15 @@ public class StockService {
     private StockRepository repository;
 
     @Cacheable("allStockQuotes")
-    public List<Quote> getAll() {
+    public List<Stock> getAll() {
         return repository.findAll()
             .stream()
-            .sorted(Comparator.comparing(Quote::getName))
+            .sorted(Comparator.comparing(Stock::getName))
             .toList();
     }
 
     @Cacheable(value = "stockQuotesQuery", key = "#query")
-    public List<Quote> get(String query) {
+    public List<Stock> get(String query) {
         if (Objects.equals(query, "")) {
             return emptyList();
         }
@@ -39,12 +39,12 @@ public class StockService {
     }
 
     @Cacheable(value = "stockQuotes", key = "#code")
-    public Quote getByCode(String code) {
+    public Stock getByCode(String code) {
         log.debug("Getting stock by code [{}]", code);
         return repository.findByCode(code);
     }
 
-    public List<Quote> update(List<Quote> quotes) {
+    public List<Stock> update(List<Stock> quotes) {
         return repository.update(quotes);
     }
 
@@ -52,7 +52,7 @@ public class StockService {
         return repository.isValid(code);
     }
 
-    public Quote getStockQuote(String code) {
+    public Stock getStockQuote(String code) {
         return repository.getStockQuote(code);
     }
 

+ 2 - 2
src/test/java/service/PortfolioServiceTest.java

@@ -2,7 +2,7 @@ package service;
 
 import com.danielbohry.stocks.App;
 import com.danielbohry.stocks.domain.Portfolio;
-import com.danielbohry.stocks.domain.Stock;
+import com.danielbohry.stocks.domain.PortfolioStock;
 import com.danielbohry.stocks.repository.portfolio.PortfolioRepository;
 import com.danielbohry.stocks.repository.stock.StockRepository;
 import com.danielbohry.stocks.service.ExchangeService;
@@ -82,7 +82,7 @@ public class PortfolioServiceTest {
         when(stockRepository.isValid(any())).thenReturn(true);
 
         Portfolio portfolio = portfolioService.create();
-        Stock newStock = Stock.builder()
+        PortfolioStock newStock = PortfolioStock.builder()
             .code("code")
             .quantity(3)
             .build();

+ 4 - 4
src/test/java/service/StockServiceTest.java

@@ -1,7 +1,7 @@
 package service;
 
 import com.danielbohry.stocks.App;
-import com.danielbohry.stocks.domain.Quote;
+import com.danielbohry.stocks.domain.Stock;
 import com.danielbohry.stocks.repository.stock.StockRepository;
 import com.danielbohry.stocks.service.StockService;
 import org.junit.jupiter.api.BeforeEach;
@@ -34,10 +34,10 @@ public class StockServiceTest {
     public void shouldGetStockByCode() {
         //given
         String code = "AAPL";
-        Quote expected = new Quote(code, "Apple Inc.", "USD", null, null, null);
+        Stock expected = new Stock(code, "Apple Inc.", "USD", null, null, null);
 
         //when
-        Quote result = service.getByCode(code);
+        Stock result = service.getByCode(code);
 
         //then
         assertEquals(expected.getCode(), result.getCode());
@@ -50,7 +50,7 @@ public class StockServiceTest {
         String name = "West";
 
         //when
-        List<Quote> result = service.get(name);
+        List<Stock> result = service.get(name);
 
         //then
         assertEquals(6, result.size());