From 2ae84f67724792db5f98bbab17ae58e9f4af35c8 Mon Sep 17 00:00:00 2001 From: Pol Pinol Castuera Date: Sat, 31 May 2025 10:23:22 +0200 Subject: [PATCH] Apply linter --- build.gradle | 5 +- .../java/io/autoinvestor/CoreApplication.java | 6 +- .../application/AssetAlreadyExists.java | 6 +- .../application/AssetNotFoundException.java | 6 +- .../GetAllAssetsCommandHandler.java | 23 ++- .../application/GetAssetCommandHandler.java | 24 +-- .../GetAssetPriceCommandHandler.java | 28 +-- .../RegisterAssetCommandHandler.java | 37 ++-- .../io/autoinvestor/domain/AggregateRoot.java | 24 +-- .../java/io/autoinvestor/domain/Asset.java | 173 +++++++++--------- .../java/io/autoinvestor/domain/AssetId.java | 18 +- .../domain/AssetPriceFetcher.java | 2 +- .../autoinvestor/domain/AssetRepository.java | 8 +- .../domain/AssetWasRegisteredEvent.java | 16 +- .../AssetWasRegisteredEventPayload.java | 10 +- .../io/autoinvestor/domain/CompanyName.java | 28 +-- .../java/io/autoinvestor/domain/Event.java | 86 ++++----- .../java/io/autoinvestor/domain/EventId.java | 12 +- .../io/autoinvestor/domain/EventPayload.java | 2 +- .../autoinvestor/domain/EventPublisher.java | 2 +- src/main/java/io/autoinvestor/domain/Id.java | 40 ++-- src/main/java/io/autoinvestor/domain/Mic.java | 28 +-- .../java/io/autoinvestor/domain/Ticker.java | 28 +-- .../exceptions/BadRequestException.java | 6 +- .../exceptions/DuplicatedException.java | 6 +- .../exceptions/InternalErrorException.java | 6 +- .../event_publishers/EventMessageMapper.java | 45 ++--- .../GcpPubSubEventPublisher.java | 46 ++--- .../InMemoryEventPublisher.java | 39 ++-- .../fetchers/PriceFetchFailedException.java | 6 +- .../fetchers/PriceNotAvailableException.java | 6 +- .../fetchers/YFinanceAssetPriceFetcher.java | 172 ++++++++--------- .../repositories/AssetDocument.java | 12 +- .../repositories/AssetMapper.java | 36 ++-- .../repositories/InMemoryAssetRepository.java | 52 +++--- .../repositories/MongoAssetRepository.java | 72 ++++---- .../java/io/autoinvestor/ui/AssetDTO.java | 8 +- .../io/autoinvestor/ui/ErrorResponse.java | 6 +- .../autoinvestor/ui/ErrorResponseBuilder.java | 38 ++-- .../ui/GetAllAssetsController.java | 26 +-- .../autoinvestor/ui/GetAssetController.java | 23 +-- .../ui/GetAssetPriceController.java | 29 +-- .../ui/GlobalExceptionHandler.java | 85 ++++----- .../InvalidRegisterAssetInputException.java | 6 +- .../ui/RegisterAssetController.java | 28 +-- .../io/autoinvestor/ui/RegisterAssetDTO.java | 40 ++-- 46 files changed, 713 insertions(+), 692 deletions(-) diff --git a/build.gradle b/build.gradle index 4d4d488..7f742d7 100644 --- a/build.gradle +++ b/build.gradle @@ -46,9 +46,10 @@ bootBuildImage { spotless { java { - googleJavaFormat('1.22.0') + googleJavaFormat('1.22.0').aosp() removeUnusedImports() trimTrailingWhitespace() + leadingTabsToSpaces() endWithNewline() importOrder '', 'java', 'javax', 'org', 'com' target 'src/**/*.java' @@ -56,7 +57,7 @@ spotless { format 'misc', { target '*.gradle', '*.md', '.gitignore' - indentWithSpaces() + leadingTabsToSpaces() trimTrailingWhitespace() endWithNewline() } diff --git a/src/main/java/io/autoinvestor/CoreApplication.java b/src/main/java/io/autoinvestor/CoreApplication.java index bce7555..75664d9 100644 --- a/src/main/java/io/autoinvestor/CoreApplication.java +++ b/src/main/java/io/autoinvestor/CoreApplication.java @@ -6,7 +6,7 @@ @SpringBootApplication public class CoreApplication { - public static void main(String[] args) { - SpringApplication.run(CoreApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(CoreApplication.class, args); + } } diff --git a/src/main/java/io/autoinvestor/application/AssetAlreadyExists.java b/src/main/java/io/autoinvestor/application/AssetAlreadyExists.java index fbbab3b..efc59a8 100644 --- a/src/main/java/io/autoinvestor/application/AssetAlreadyExists.java +++ b/src/main/java/io/autoinvestor/application/AssetAlreadyExists.java @@ -3,7 +3,7 @@ import io.autoinvestor.exceptions.DuplicatedException; public class AssetAlreadyExists extends DuplicatedException { - public AssetAlreadyExists(String message) { - super(message); - } + public AssetAlreadyExists(String message) { + super(message); + } } diff --git a/src/main/java/io/autoinvestor/application/AssetNotFoundException.java b/src/main/java/io/autoinvestor/application/AssetNotFoundException.java index 5a1ad4d..2f52f50 100644 --- a/src/main/java/io/autoinvestor/application/AssetNotFoundException.java +++ b/src/main/java/io/autoinvestor/application/AssetNotFoundException.java @@ -3,7 +3,7 @@ import io.autoinvestor.exceptions.BadRequestException; public class AssetNotFoundException extends BadRequestException { - public AssetNotFoundException(String message) { - super(message); - } + public AssetNotFoundException(String message) { + super(message); + } } diff --git a/src/main/java/io/autoinvestor/application/GetAllAssetsCommandHandler.java b/src/main/java/io/autoinvestor/application/GetAllAssetsCommandHandler.java index bdc467e..8fdca9f 100644 --- a/src/main/java/io/autoinvestor/application/GetAllAssetsCommandHandler.java +++ b/src/main/java/io/autoinvestor/application/GetAllAssetsCommandHandler.java @@ -11,16 +11,19 @@ @Service public class GetAllAssetsCommandHandler { - private final AssetRepository repository; + private final AssetRepository repository; - public GetAllAssetsCommandHandler(AssetRepository repository) { - this.repository = repository; - } + public GetAllAssetsCommandHandler(AssetRepository repository) { + this.repository = repository; + } - public List handle() { - List assets = this.repository.findAll(); - return assets.stream() - .map(asset -> new GetAssetResponse(asset.id(), asset.mic(), asset.ticker(), asset.name())) - .collect(Collectors.toList()); - } + public List handle() { + List assets = this.repository.findAll(); + return assets.stream() + .map( + asset -> + new GetAssetResponse( + asset.id(), asset.mic(), asset.ticker(), asset.name())) + .collect(Collectors.toList()); + } } diff --git a/src/main/java/io/autoinvestor/application/GetAssetCommandHandler.java b/src/main/java/io/autoinvestor/application/GetAssetCommandHandler.java index 0f2e228..0728d6d 100644 --- a/src/main/java/io/autoinvestor/application/GetAssetCommandHandler.java +++ b/src/main/java/io/autoinvestor/application/GetAssetCommandHandler.java @@ -11,20 +11,20 @@ @Service public class GetAssetCommandHandler { - private final AssetRepository repository; + private final AssetRepository repository; - public GetAssetCommandHandler(AssetRepository repository) { - this.repository = repository; - } + public GetAssetCommandHandler(AssetRepository repository) { + this.repository = repository; + } - public GetAssetResponse handle(GetAssetCommand command) { - Optional asset = repository.findById(AssetId.of(command.assetId())); + public GetAssetResponse handle(GetAssetCommand command) { + Optional asset = repository.findById(AssetId.of(command.assetId())); - if (asset.isEmpty()) { - throw new AssetNotFoundException("Asset not found with ID: " + command.assetId()); - } + if (asset.isEmpty()) { + throw new AssetNotFoundException("Asset not found with ID: " + command.assetId()); + } - return new GetAssetResponse( - asset.get().id(), asset.get().mic(), asset.get().ticker(), asset.get().name()); - } + return new GetAssetResponse( + asset.get().id(), asset.get().mic(), asset.get().ticker(), asset.get().name()); + } } diff --git a/src/main/java/io/autoinvestor/application/GetAssetPriceCommandHandler.java b/src/main/java/io/autoinvestor/application/GetAssetPriceCommandHandler.java index dd0bf5c..923e993 100644 --- a/src/main/java/io/autoinvestor/application/GetAssetPriceCommandHandler.java +++ b/src/main/java/io/autoinvestor/application/GetAssetPriceCommandHandler.java @@ -12,22 +12,22 @@ @Service public class GetAssetPriceCommandHandler { - private final AssetPriceFetcher fetcher; - private final AssetRepository repository; + private final AssetPriceFetcher fetcher; + private final AssetRepository repository; - public GetAssetPriceCommandHandler(AssetRepository repository, AssetPriceFetcher fetcher) { - this.repository = repository; - this.fetcher = fetcher; - } + public GetAssetPriceCommandHandler(AssetRepository repository, AssetPriceFetcher fetcher) { + this.repository = repository; + this.fetcher = fetcher; + } - public GetAssetPriceResponse handle(GetAssetPriceCommand command) { - Optional asset = repository.findById(AssetId.of(command.assetId())); + public GetAssetPriceResponse handle(GetAssetPriceCommand command) { + Optional asset = repository.findById(AssetId.of(command.assetId())); - if (asset.isEmpty()) { - throw new AssetNotFoundException("Asset not found with ID: " + command.assetId()); - } + if (asset.isEmpty()) { + throw new AssetNotFoundException("Asset not found with ID: " + command.assetId()); + } - float price = fetcher.priceOn(asset.get(), command.date()); - return new GetAssetPriceResponse(price, command.date()); - } + float price = fetcher.priceOn(asset.get(), command.date()); + return new GetAssetPriceResponse(price, command.date()); + } } diff --git a/src/main/java/io/autoinvestor/application/RegisterAssetCommandHandler.java b/src/main/java/io/autoinvestor/application/RegisterAssetCommandHandler.java index 07a09e0..0461660 100644 --- a/src/main/java/io/autoinvestor/application/RegisterAssetCommandHandler.java +++ b/src/main/java/io/autoinvestor/application/RegisterAssetCommandHandler.java @@ -9,25 +9,28 @@ @Service public class RegisterAssetCommandHandler { - private final AssetRepository repository; - private final EventPublisher eventPublisher; - - public RegisterAssetCommandHandler(AssetRepository repository, EventPublisher eventPublisher) { - this.repository = repository; - this.eventPublisher = eventPublisher; - } - - public RegisterAssetResponse handle(RegisterAssetCommand command) { - if (this.repository.exists(command.mic(), command.ticker())) { - throw new AssetAlreadyExists( - "Duplicated asset for this mic: " + command.mic() + " and ticker: " + command.ticker()); + private final AssetRepository repository; + private final EventPublisher eventPublisher; + + public RegisterAssetCommandHandler(AssetRepository repository, EventPublisher eventPublisher) { + this.repository = repository; + this.eventPublisher = eventPublisher; } - Asset asset = Asset.create(command.mic(), command.ticker(), command.name()); + public RegisterAssetResponse handle(RegisterAssetCommand command) { + if (this.repository.exists(command.mic(), command.ticker())) { + throw new AssetAlreadyExists( + "Duplicated asset for this mic: " + + command.mic() + + " and ticker: " + + command.ticker()); + } + + Asset asset = Asset.create(command.mic(), command.ticker(), command.name()); - this.repository.save(asset); - this.eventPublisher.publish(asset.releaseEvents()); + this.repository.save(asset); + this.eventPublisher.publish(asset.releaseEvents()); - return new RegisterAssetResponse(asset.id(), asset.mic(), asset.ticker(), asset.name()); - } + return new RegisterAssetResponse(asset.id(), asset.mic(), asset.ticker(), asset.name()); + } } diff --git a/src/main/java/io/autoinvestor/domain/AggregateRoot.java b/src/main/java/io/autoinvestor/domain/AggregateRoot.java index ba5830f..49ff196 100644 --- a/src/main/java/io/autoinvestor/domain/AggregateRoot.java +++ b/src/main/java/io/autoinvestor/domain/AggregateRoot.java @@ -4,19 +4,19 @@ import java.util.List; public class AggregateRoot { - private final List> events; + private final List> events; - public AggregateRoot() { - this.events = new ArrayList<>(); - } + public AggregateRoot() { + this.events = new ArrayList<>(); + } - protected void recordEvent(Event event) { - this.events.add(event); - } + protected void recordEvent(Event event) { + this.events.add(event); + } - public List> releaseEvents() { - List> events = new ArrayList<>(this.events); - this.events.clear(); - return events; - } + public List> releaseEvents() { + List> events = new ArrayList<>(this.events); + this.events.clear(); + return events; + } } diff --git a/src/main/java/io/autoinvestor/domain/Asset.java b/src/main/java/io/autoinvestor/domain/Asset.java index d07e1e1..3cc67e8 100644 --- a/src/main/java/io/autoinvestor/domain/Asset.java +++ b/src/main/java/io/autoinvestor/domain/Asset.java @@ -3,88 +3,93 @@ import java.util.Date; public class Asset extends AggregateRoot { - private final AssetId id; - private final Mic mic; - private final Ticker ticker; - private final CompanyName name; - private final Date createdAt; - private final Date updatedAt; - - private Asset( - AssetId id, Mic mic, Ticker ticker, CompanyName name, Date createdAt, Date updatedAt) { - this.id = id; - this.mic = mic; - this.ticker = ticker; - this.name = name; - this.createdAt = createdAt; - this.updatedAt = updatedAt; - } - - private Asset(Mic mic, Ticker ticker, CompanyName name, Date createdAt, Date updatedAt) { - this.id = AssetId.generate(); - this.mic = mic; - this.ticker = ticker; - this.name = name; - this.createdAt = createdAt; - this.updatedAt = updatedAt; - - this.recordEvent(AssetWasRegisteredEvent.from(this)); - } - - public static Asset create(String mic, String ticker, String name) { - return new Asset( - Mic.from(mic), Ticker.from(ticker), CompanyName.from(name), new Date(), new Date()); - } - - public static Asset from( - String assetId, String mic, String ticker, String name, Date createdAt, Date updatedAt) { - return new Asset( - AssetId.of(assetId), - Mic.from(mic), - Ticker.from(ticker), - CompanyName.from(name), - createdAt, - updatedAt); - } - - public String mic() { - return mic.value(); - } - - public String ticker() { - return ticker.value(); - } - - public String name() { - return name.value(); - } - - public String id() { - return id.value(); - } - - public AssetId getId() { - return id; - } - - public Date getCreatedAt() { - return createdAt; - } - - public Date getUpdatedAt() { - return updatedAt; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null || getClass() != obj.getClass()) return false; - Asset that = (Asset) obj; - return mic.equals(that.mic) && ticker.equals(that.ticker); - } - - @Override - public String toString() { - return mic + ":" + ticker; - } + private final AssetId id; + private final Mic mic; + private final Ticker ticker; + private final CompanyName name; + private final Date createdAt; + private final Date updatedAt; + + private Asset( + AssetId id, Mic mic, Ticker ticker, CompanyName name, Date createdAt, Date updatedAt) { + this.id = id; + this.mic = mic; + this.ticker = ticker; + this.name = name; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + } + + private Asset(Mic mic, Ticker ticker, CompanyName name, Date createdAt, Date updatedAt) { + this.id = AssetId.generate(); + this.mic = mic; + this.ticker = ticker; + this.name = name; + this.createdAt = createdAt; + this.updatedAt = updatedAt; + + this.recordEvent(AssetWasRegisteredEvent.from(this)); + } + + public static Asset create(String mic, String ticker, String name) { + return new Asset( + Mic.from(mic), Ticker.from(ticker), CompanyName.from(name), new Date(), new Date()); + } + + public static Asset from( + String assetId, + String mic, + String ticker, + String name, + Date createdAt, + Date updatedAt) { + return new Asset( + AssetId.of(assetId), + Mic.from(mic), + Ticker.from(ticker), + CompanyName.from(name), + createdAt, + updatedAt); + } + + public String mic() { + return mic.value(); + } + + public String ticker() { + return ticker.value(); + } + + public String name() { + return name.value(); + } + + public String id() { + return id.value(); + } + + public AssetId getId() { + return id; + } + + public Date getCreatedAt() { + return createdAt; + } + + public Date getUpdatedAt() { + return updatedAt; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null || getClass() != obj.getClass()) return false; + Asset that = (Asset) obj; + return mic.equals(that.mic) && ticker.equals(that.ticker); + } + + @Override + public String toString() { + return mic + ":" + ticker; + } } diff --git a/src/main/java/io/autoinvestor/domain/AssetId.java b/src/main/java/io/autoinvestor/domain/AssetId.java index 2026644..c2fbaed 100644 --- a/src/main/java/io/autoinvestor/domain/AssetId.java +++ b/src/main/java/io/autoinvestor/domain/AssetId.java @@ -1,15 +1,15 @@ package io.autoinvestor.domain; public class AssetId extends Id { - AssetId(String id) { - super(id); - } + AssetId(String id) { + super(id); + } - public static AssetId generate() { - return new AssetId(generateId()); - } + public static AssetId generate() { + return new AssetId(generateId()); + } - public static AssetId of(String id) { - return new AssetId(id); - } + public static AssetId of(String id) { + return new AssetId(id); + } } diff --git a/src/main/java/io/autoinvestor/domain/AssetPriceFetcher.java b/src/main/java/io/autoinvestor/domain/AssetPriceFetcher.java index e1f3280..b94de53 100644 --- a/src/main/java/io/autoinvestor/domain/AssetPriceFetcher.java +++ b/src/main/java/io/autoinvestor/domain/AssetPriceFetcher.java @@ -3,5 +3,5 @@ import java.util.Date; public interface AssetPriceFetcher { - float priceOn(Asset asset, Date date); + float priceOn(Asset asset, Date date); } diff --git a/src/main/java/io/autoinvestor/domain/AssetRepository.java b/src/main/java/io/autoinvestor/domain/AssetRepository.java index f1a2f97..32925fd 100644 --- a/src/main/java/io/autoinvestor/domain/AssetRepository.java +++ b/src/main/java/io/autoinvestor/domain/AssetRepository.java @@ -4,11 +4,11 @@ import java.util.Optional; public interface AssetRepository { - void save(Asset asset); + void save(Asset asset); - boolean exists(String mic, String ticker); + boolean exists(String mic, String ticker); - Optional findById(AssetId assetId); + Optional findById(AssetId assetId); - List findAll(); + List findAll(); } diff --git a/src/main/java/io/autoinvestor/domain/AssetWasRegisteredEvent.java b/src/main/java/io/autoinvestor/domain/AssetWasRegisteredEvent.java index 65c9359..fd0fd92 100644 --- a/src/main/java/io/autoinvestor/domain/AssetWasRegisteredEvent.java +++ b/src/main/java/io/autoinvestor/domain/AssetWasRegisteredEvent.java @@ -2,13 +2,13 @@ public class AssetWasRegisteredEvent extends Event { - private AssetWasRegisteredEvent(Id aggregateId, AssetWasRegisteredEventPayload payload) { - super(aggregateId, "ASSET_CREATED", payload); - } + private AssetWasRegisteredEvent(Id aggregateId, AssetWasRegisteredEventPayload payload) { + super(aggregateId, "ASSET_CREATED", payload); + } - public static AssetWasRegisteredEvent from(Asset asset) { - AssetWasRegisteredEventPayload payload = - new AssetWasRegisteredEventPayload(asset.mic(), asset.ticker(), asset.name()); - return new AssetWasRegisteredEvent(asset.getId(), payload); - } + public static AssetWasRegisteredEvent from(Asset asset) { + AssetWasRegisteredEventPayload payload = + new AssetWasRegisteredEventPayload(asset.mic(), asset.ticker(), asset.name()); + return new AssetWasRegisteredEvent(asset.getId(), payload); + } } diff --git a/src/main/java/io/autoinvestor/domain/AssetWasRegisteredEventPayload.java b/src/main/java/io/autoinvestor/domain/AssetWasRegisteredEventPayload.java index cc64780..c318ae4 100644 --- a/src/main/java/io/autoinvestor/domain/AssetWasRegisteredEventPayload.java +++ b/src/main/java/io/autoinvestor/domain/AssetWasRegisteredEventPayload.java @@ -3,9 +3,9 @@ import java.util.Map; public record AssetWasRegisteredEventPayload(String mic, String ticker, String name) - implements EventPayload { - @Override - public Map asMap() { - return Map.of("mic", mic, "ticker", ticker, "name", name); - } + implements EventPayload { + @Override + public Map asMap() { + return Map.of("mic", mic, "ticker", ticker, "name", name); + } } diff --git a/src/main/java/io/autoinvestor/domain/CompanyName.java b/src/main/java/io/autoinvestor/domain/CompanyName.java index 6c94833..6c6be54 100644 --- a/src/main/java/io/autoinvestor/domain/CompanyName.java +++ b/src/main/java/io/autoinvestor/domain/CompanyName.java @@ -1,22 +1,22 @@ package io.autoinvestor.domain; public class CompanyName { - private final String value; + private final String value; - private CompanyName(String value) { - this.value = value; - } + private CompanyName(String value) { + this.value = value; + } - public static CompanyName from(String value) { - return new CompanyName(value); - } + public static CompanyName from(String value) { + return new CompanyName(value); + } - public String value() { - return value; - } + public String value() { + return value; + } - @Override - public String toString() { - return value; - } + @Override + public String toString() { + return value; + } } diff --git a/src/main/java/io/autoinvestor/domain/Event.java b/src/main/java/io/autoinvestor/domain/Event.java index 7b8d335..a5e455f 100644 --- a/src/main/java/io/autoinvestor/domain/Event.java +++ b/src/main/java/io/autoinvestor/domain/Event.java @@ -3,47 +3,47 @@ import java.util.Date; public abstract class Event

{ - private final EventId id; - private final Id aggregateId; - private final String type; - private final P payload; - private final Date occurredAt; - private final int version; - - protected Event(Id aggregateId, String type, P payload) { - this(aggregateId, type, payload, 1); - } - - protected Event(Id aggregateId, String type, P payload, int version) { - this.id = EventId.generate(); - this.aggregateId = aggregateId; - this.type = type; - this.payload = payload; - this.occurredAt = new Date(); - this.version = version; - } - - public EventId getId() { - return id; - } - - public Id getAggregateId() { - return aggregateId; - } - - public String getType() { - return type; - } - - public P getPayload() { - return payload; - } - - public Date getOccurredAt() { - return occurredAt; - } - - public int getVersion() { - return version; - } + private final EventId id; + private final Id aggregateId; + private final String type; + private final P payload; + private final Date occurredAt; + private final int version; + + protected Event(Id aggregateId, String type, P payload) { + this(aggregateId, type, payload, 1); + } + + protected Event(Id aggregateId, String type, P payload, int version) { + this.id = EventId.generate(); + this.aggregateId = aggregateId; + this.type = type; + this.payload = payload; + this.occurredAt = new Date(); + this.version = version; + } + + public EventId getId() { + return id; + } + + public Id getAggregateId() { + return aggregateId; + } + + public String getType() { + return type; + } + + public P getPayload() { + return payload; + } + + public Date getOccurredAt() { + return occurredAt; + } + + public int getVersion() { + return version; + } } diff --git a/src/main/java/io/autoinvestor/domain/EventId.java b/src/main/java/io/autoinvestor/domain/EventId.java index 1829578..8601083 100644 --- a/src/main/java/io/autoinvestor/domain/EventId.java +++ b/src/main/java/io/autoinvestor/domain/EventId.java @@ -1,11 +1,11 @@ package io.autoinvestor.domain; public class EventId extends Id { - EventId(String id) { - super(id); - } + EventId(String id) { + super(id); + } - public static EventId generate() { - return new EventId(generateId()); - } + public static EventId generate() { + return new EventId(generateId()); + } } diff --git a/src/main/java/io/autoinvestor/domain/EventPayload.java b/src/main/java/io/autoinvestor/domain/EventPayload.java index b39ed73..48b01d4 100644 --- a/src/main/java/io/autoinvestor/domain/EventPayload.java +++ b/src/main/java/io/autoinvestor/domain/EventPayload.java @@ -3,5 +3,5 @@ import java.util.Map; public interface EventPayload { - Map asMap(); + Map asMap(); } diff --git a/src/main/java/io/autoinvestor/domain/EventPublisher.java b/src/main/java/io/autoinvestor/domain/EventPublisher.java index 717fb9f..799870e 100644 --- a/src/main/java/io/autoinvestor/domain/EventPublisher.java +++ b/src/main/java/io/autoinvestor/domain/EventPublisher.java @@ -3,5 +3,5 @@ import java.util.List; public interface EventPublisher { - void publish(List> events); + void publish(List> events); } diff --git a/src/main/java/io/autoinvestor/domain/Id.java b/src/main/java/io/autoinvestor/domain/Id.java index 55b11a3..9746ec9 100644 --- a/src/main/java/io/autoinvestor/domain/Id.java +++ b/src/main/java/io/autoinvestor/domain/Id.java @@ -4,29 +4,29 @@ import java.util.UUID; public abstract class Id { - private final String id; + private final String id; - public Id(String id) { - this.id = id; - } + public Id(String id) { + this.id = id; + } - public String value() { - return id; - } + public String value() { + return id; + } - protected static String generateId() { - return UUID.randomUUID().toString(); - } + protected static String generateId() { + return UUID.randomUUID().toString(); + } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof Id that)) return false; - return Objects.equals(id, that.id); - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Id that)) return false; + return Objects.equals(id, that.id); + } - @Override - public int hashCode() { - return Objects.hash(id); - } + @Override + public int hashCode() { + return Objects.hash(id); + } } diff --git a/src/main/java/io/autoinvestor/domain/Mic.java b/src/main/java/io/autoinvestor/domain/Mic.java index 04b7f6a..9d12953 100644 --- a/src/main/java/io/autoinvestor/domain/Mic.java +++ b/src/main/java/io/autoinvestor/domain/Mic.java @@ -1,22 +1,22 @@ package io.autoinvestor.domain; public class Mic { - private final String value; + private final String value; - private Mic(String value) { - this.value = value; - } + private Mic(String value) { + this.value = value; + } - public static Mic from(String mic) { - return new Mic(mic); - } + public static Mic from(String mic) { + return new Mic(mic); + } - public String value() { - return value; - } + public String value() { + return value; + } - @Override - public String toString() { - return "Mic{" + "mic='" + value + '\'' + '}'; - } + @Override + public String toString() { + return "Mic{" + "mic='" + value + '\'' + '}'; + } } diff --git a/src/main/java/io/autoinvestor/domain/Ticker.java b/src/main/java/io/autoinvestor/domain/Ticker.java index 5bb9a0c..0ea9c06 100644 --- a/src/main/java/io/autoinvestor/domain/Ticker.java +++ b/src/main/java/io/autoinvestor/domain/Ticker.java @@ -1,22 +1,22 @@ package io.autoinvestor.domain; public class Ticker { - private final String value; + private final String value; - private Ticker(String value) { - this.value = value; - } + private Ticker(String value) { + this.value = value; + } - public static Ticker from(String ticker) { - return new Ticker(ticker); - } + public static Ticker from(String ticker) { + return new Ticker(ticker); + } - public String value() { - return value; - } + public String value() { + return value; + } - @Override - public String toString() { - return "Ticker{" + "ticker='" + value + '\'' + '}'; - } + @Override + public String toString() { + return "Ticker{" + "ticker='" + value + '\'' + '}'; + } } diff --git a/src/main/java/io/autoinvestor/exceptions/BadRequestException.java b/src/main/java/io/autoinvestor/exceptions/BadRequestException.java index 65f763c..6e67872 100644 --- a/src/main/java/io/autoinvestor/exceptions/BadRequestException.java +++ b/src/main/java/io/autoinvestor/exceptions/BadRequestException.java @@ -1,7 +1,7 @@ package io.autoinvestor.exceptions; public class BadRequestException extends RuntimeException { - public BadRequestException(String message) { - super(message); - } + public BadRequestException(String message) { + super(message); + } } diff --git a/src/main/java/io/autoinvestor/exceptions/DuplicatedException.java b/src/main/java/io/autoinvestor/exceptions/DuplicatedException.java index 8329bec..1ddfe2b 100644 --- a/src/main/java/io/autoinvestor/exceptions/DuplicatedException.java +++ b/src/main/java/io/autoinvestor/exceptions/DuplicatedException.java @@ -1,7 +1,7 @@ package io.autoinvestor.exceptions; public class DuplicatedException extends RuntimeException { - public DuplicatedException(String message) { - super(message); - } + public DuplicatedException(String message) { + super(message); + } } diff --git a/src/main/java/io/autoinvestor/exceptions/InternalErrorException.java b/src/main/java/io/autoinvestor/exceptions/InternalErrorException.java index f8ea854..3129d4b 100644 --- a/src/main/java/io/autoinvestor/exceptions/InternalErrorException.java +++ b/src/main/java/io/autoinvestor/exceptions/InternalErrorException.java @@ -1,7 +1,7 @@ package io.autoinvestor.exceptions; public class InternalErrorException extends RuntimeException { - public InternalErrorException(String message) { - super(message); - } + public InternalErrorException(String message) { + super(message); + } } diff --git a/src/main/java/io/autoinvestor/infrastructure/event_publishers/EventMessageMapper.java b/src/main/java/io/autoinvestor/infrastructure/event_publishers/EventMessageMapper.java index c7f94f3..6f28de2 100644 --- a/src/main/java/io/autoinvestor/infrastructure/event_publishers/EventMessageMapper.java +++ b/src/main/java/io/autoinvestor/infrastructure/event_publishers/EventMessageMapper.java @@ -14,27 +14,28 @@ final class EventMessageMapper { - private final ObjectMapper objectMapper; - - EventMessageMapper(ObjectMapper objectMapper) { - this.objectMapper = objectMapper; - } - - PubsubMessage toMessage(Event event) { - try { - Map envelope = new HashMap<>(); - envelope.put("payload", event.getPayload().asMap()); - envelope.put("eventId", event.getId().toString()); - envelope.put("type", event.getType()); - envelope.put("aggregateId", event.getAggregateId().value()); - envelope.put("occurredAt", Instant.ofEpochMilli(event.getOccurredAt().getTime()).toString()); - envelope.put("version", event.getVersion()); - - String json = objectMapper.writeValueAsString(envelope); - return PubsubMessage.newBuilder().setData(ByteString.copyFromUtf8(json)).build(); - - } catch (JsonProcessingException ex) { - throw new InternalErrorException("Failed to serialise domain event"); + private final ObjectMapper objectMapper; + + EventMessageMapper(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + PubsubMessage toMessage(Event event) { + try { + Map envelope = new HashMap<>(); + envelope.put("payload", event.getPayload().asMap()); + envelope.put("eventId", event.getId().toString()); + envelope.put("type", event.getType()); + envelope.put("aggregateId", event.getAggregateId().value()); + envelope.put( + "occurredAt", Instant.ofEpochMilli(event.getOccurredAt().getTime()).toString()); + envelope.put("version", event.getVersion()); + + String json = objectMapper.writeValueAsString(envelope); + return PubsubMessage.newBuilder().setData(ByteString.copyFromUtf8(json)).build(); + + } catch (JsonProcessingException ex) { + throw new InternalErrorException("Failed to serialise domain event"); + } } - } } diff --git a/src/main/java/io/autoinvestor/infrastructure/event_publishers/GcpPubSubEventPublisher.java b/src/main/java/io/autoinvestor/infrastructure/event_publishers/GcpPubSubEventPublisher.java index ed924a9..55b17cd 100644 --- a/src/main/java/io/autoinvestor/infrastructure/event_publishers/GcpPubSubEventPublisher.java +++ b/src/main/java/io/autoinvestor/infrastructure/event_publishers/GcpPubSubEventPublisher.java @@ -19,27 +19,27 @@ @Profile("prod") public class GcpPubSubEventPublisher implements EventPublisher { - private final Publisher publisher; - private final EventMessageMapper mapper; - - public GcpPubSubEventPublisher( - @Value("${GCP_PROJECT}") String projectId, - @Value("${PUBSUB_TOPIC}") String topic, - ObjectMapper objectMapper) - throws Exception { - this.mapper = new EventMessageMapper(objectMapper); - ProjectTopicName topicName = ProjectTopicName.of(projectId, topic); - this.publisher = Publisher.newBuilder(topicName).build(); - } - - @Override - public void publish(List> events) { - events.stream().map(mapper::toMessage).forEach(publisher::publish); - } - - @PreDestroy - public void shutdown() throws Exception { - publisher.shutdown(); - publisher.awaitTermination(1, TimeUnit.MINUTES); - } + private final Publisher publisher; + private final EventMessageMapper mapper; + + public GcpPubSubEventPublisher( + @Value("${GCP_PROJECT}") String projectId, + @Value("${PUBSUB_TOPIC}") String topic, + ObjectMapper objectMapper) + throws Exception { + this.mapper = new EventMessageMapper(objectMapper); + ProjectTopicName topicName = ProjectTopicName.of(projectId, topic); + this.publisher = Publisher.newBuilder(topicName).build(); + } + + @Override + public void publish(List> events) { + events.stream().map(mapper::toMessage).forEach(publisher::publish); + } + + @PreDestroy + public void shutdown() throws Exception { + publisher.shutdown(); + publisher.awaitTermination(1, TimeUnit.MINUTES); + } } diff --git a/src/main/java/io/autoinvestor/infrastructure/event_publishers/InMemoryEventPublisher.java b/src/main/java/io/autoinvestor/infrastructure/event_publishers/InMemoryEventPublisher.java index 907cf38..569bb50 100644 --- a/src/main/java/io/autoinvestor/infrastructure/event_publishers/InMemoryEventPublisher.java +++ b/src/main/java/io/autoinvestor/infrastructure/event_publishers/InMemoryEventPublisher.java @@ -14,23 +14,24 @@ @Profile("local") public class InMemoryEventPublisher implements EventPublisher { - private final ApplicationEventPublisher eventPublisher; - private final List> publishedEvents = new ArrayList<>(); - - public InMemoryEventPublisher(ApplicationEventPublisher eventPublisher) { - this.eventPublisher = eventPublisher; - } - - @Override - public void publish(List> events) { - this.publishedEvents.addAll(events); - events.forEach(this.eventPublisher::publishEvent); - } - - public boolean hasPublishedEvent(String type, String aggregateId) { - return publishedEvents.stream() - .anyMatch( - event -> - event.getType().equals(type) && event.getAggregateId().value().equals(aggregateId)); - } + private final ApplicationEventPublisher eventPublisher; + private final List> publishedEvents = new ArrayList<>(); + + public InMemoryEventPublisher(ApplicationEventPublisher eventPublisher) { + this.eventPublisher = eventPublisher; + } + + @Override + public void publish(List> events) { + this.publishedEvents.addAll(events); + events.forEach(this.eventPublisher::publishEvent); + } + + public boolean hasPublishedEvent(String type, String aggregateId) { + return publishedEvents.stream() + .anyMatch( + event -> + event.getType().equals(type) + && event.getAggregateId().value().equals(aggregateId)); + } } diff --git a/src/main/java/io/autoinvestor/infrastructure/fetchers/PriceFetchFailedException.java b/src/main/java/io/autoinvestor/infrastructure/fetchers/PriceFetchFailedException.java index 1bf5b57..3acd3ed 100644 --- a/src/main/java/io/autoinvestor/infrastructure/fetchers/PriceFetchFailedException.java +++ b/src/main/java/io/autoinvestor/infrastructure/fetchers/PriceFetchFailedException.java @@ -1,7 +1,7 @@ package io.autoinvestor.infrastructure.fetchers; public class PriceFetchFailedException extends RuntimeException { - public PriceFetchFailedException(String message) { - super(message); - } + public PriceFetchFailedException(String message) { + super(message); + } } diff --git a/src/main/java/io/autoinvestor/infrastructure/fetchers/PriceNotAvailableException.java b/src/main/java/io/autoinvestor/infrastructure/fetchers/PriceNotAvailableException.java index 2210f0b..ca82c0e 100644 --- a/src/main/java/io/autoinvestor/infrastructure/fetchers/PriceNotAvailableException.java +++ b/src/main/java/io/autoinvestor/infrastructure/fetchers/PriceNotAvailableException.java @@ -1,7 +1,7 @@ package io.autoinvestor.infrastructure.fetchers; public class PriceNotAvailableException extends RuntimeException { - public PriceNotAvailableException(String message) { - super(message); - } + public PriceNotAvailableException(String message) { + super(message); + } } diff --git a/src/main/java/io/autoinvestor/infrastructure/fetchers/YFinanceAssetPriceFetcher.java b/src/main/java/io/autoinvestor/infrastructure/fetchers/YFinanceAssetPriceFetcher.java index e410ae4..f29c909 100644 --- a/src/main/java/io/autoinvestor/infrastructure/fetchers/YFinanceAssetPriceFetcher.java +++ b/src/main/java/io/autoinvestor/infrastructure/fetchers/YFinanceAssetPriceFetcher.java @@ -24,92 +24,92 @@ @Component public class YFinanceAssetPriceFetcher implements AssetPriceFetcher { - private static final Logger logger = LoggerFactory.getLogger(YFinanceAssetPriceFetcher.class); - - /** ± days around the target date that we request in one call */ - private static final int DAYS_LOOKBACK_BUFFER = 7; - - private static final long SECONDS_PER_DAY = 86_400L; - - private final HttpClient httpClient = - HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build(); - - private final ObjectMapper mapper = new ObjectMapper(); - - @Override - public float priceOn(Asset asset, Date date) { - // --- 1. Build period1 / period2 (Unix seconds) ----------------------- - long targetEpoch = date.toInstant().getEpochSecond(); - long period1 = targetEpoch - DAYS_LOOKBACK_BUFFER * SECONDS_PER_DAY; - long period2 = targetEpoch + DAYS_LOOKBACK_BUFFER * SECONDS_PER_DAY; - - // --- 2. Build URL ---------------------------------------------------- - String encoded = URLEncoder.encode(asset.ticker(), StandardCharsets.UTF_8); - String url = - String.format( - "https://query2.finance.yahoo.com/v8/finance/chart/%s" - + "?period1=%d&period2=%d&interval=1d&events=history", - encoded, period1, period2); - - try { - // --- 3. Execute HTTP request ------------------------------------ - HttpRequest request = - HttpRequest.newBuilder() - .uri(URI.create(url)) - .header("User-Agent", "Mozilla/5.0") // helps avoid 429s - .timeout(Duration.ofSeconds(10)) - .GET() - .build(); - - HttpResponse response = - httpClient.send(request, HttpResponse.BodyHandlers.ofString()); - - if (response.statusCode() != HttpURLConnection.HTTP_OK) { - throw new PriceFetchFailedException( - String.format("HTTP %d for %s (%s)", response.statusCode(), asset, url)); - } - - // --- 4. Parse JSON ------------------------------------------------ - JsonNode root = mapper.readTree(response.body()); - JsonNode result = root.at("/chart/result/0"); - if (result.isMissingNode()) { - throw new PriceNotAvailableException("Empty result for " + asset); - } - - JsonNode timestamps = result.path("timestamp"); - JsonNode closes = result.at("/indicators/quote/0/close"); - - if (!timestamps.isArray() || !closes.isArray() || timestamps.size() != closes.size()) { - throw new PriceNotAvailableException("Malformed data for " + asset); - } - - // --- 5. Walk arrays to find the latest bar ≤ target date --------- - float chosen = Float.NaN; - for (int i = 0; i < timestamps.size(); i++) { - long ts = timestamps.get(i).asLong() * 1000; // to millis - if (ts > date.getTime()) break; // past target - - if (!closes.get(i).isNull()) { - chosen = closes.get(i).floatValue(); + private static final Logger logger = LoggerFactory.getLogger(YFinanceAssetPriceFetcher.class); + + /** ± days around the target date that we request in one call */ + private static final int DAYS_LOOKBACK_BUFFER = 7; + + private static final long SECONDS_PER_DAY = 86_400L; + + private final HttpClient httpClient = + HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build(); + + private final ObjectMapper mapper = new ObjectMapper(); + + @Override + public float priceOn(Asset asset, Date date) { + // --- 1. Build period1 / period2 (Unix seconds) ----------------------- + long targetEpoch = date.toInstant().getEpochSecond(); + long period1 = targetEpoch - DAYS_LOOKBACK_BUFFER * SECONDS_PER_DAY; + long period2 = targetEpoch + DAYS_LOOKBACK_BUFFER * SECONDS_PER_DAY; + + // --- 2. Build URL ---------------------------------------------------- + String encoded = URLEncoder.encode(asset.ticker(), StandardCharsets.UTF_8); + String url = + String.format( + "https://query2.finance.yahoo.com/v8/finance/chart/%s" + + "?period1=%d&period2=%d&interval=1d&events=history", + encoded, period1, period2); + + try { + // --- 3. Execute HTTP request ------------------------------------ + HttpRequest request = + HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("User-Agent", "Mozilla/5.0") // helps avoid 429s + .timeout(Duration.ofSeconds(10)) + .GET() + .build(); + + HttpResponse response = + httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() != HttpURLConnection.HTTP_OK) { + throw new PriceFetchFailedException( + String.format("HTTP %d for %s (%s)", response.statusCode(), asset, url)); + } + + // --- 4. Parse JSON ------------------------------------------------ + JsonNode root = mapper.readTree(response.body()); + JsonNode result = root.at("/chart/result/0"); + if (result.isMissingNode()) { + throw new PriceNotAvailableException("Empty result for " + asset); + } + + JsonNode timestamps = result.path("timestamp"); + JsonNode closes = result.at("/indicators/quote/0/close"); + + if (!timestamps.isArray() || !closes.isArray() || timestamps.size() != closes.size()) { + throw new PriceNotAvailableException("Malformed data for " + asset); + } + + // --- 5. Walk arrays to find the latest bar ≤ target date --------- + float chosen = Float.NaN; + for (int i = 0; i < timestamps.size(); i++) { + long ts = timestamps.get(i).asLong() * 1000; // to millis + if (ts > date.getTime()) break; // past target + + if (!closes.get(i).isNull()) { + chosen = closes.get(i).floatValue(); + } + } + + if (Float.isNaN(chosen)) { + // Fallback to meta.regularMarketPrice if available + JsonNode fallback = result.at("/meta/regularMarketPrice"); + if (!fallback.isMissingNode() && !fallback.isNull()) { + chosen = (float) fallback.asDouble(); + } else { + throw new PriceNotAvailableException("No bar ≤ target date for " + asset); + } + } + return chosen; + + } catch (IOException | InterruptedException ex) { + logger.error("Error fetching price for {} on {}:", asset, date, ex); + Thread.currentThread().interrupt(); + throw new PriceFetchFailedException( + String.format("Unable to fetch price for %s (%s)", asset, ex)); } - } - - if (Float.isNaN(chosen)) { - // Fallback to meta.regularMarketPrice if available - JsonNode fallback = result.at("/meta/regularMarketPrice"); - if (!fallback.isMissingNode() && !fallback.isNull()) { - chosen = (float) fallback.asDouble(); - } else { - throw new PriceNotAvailableException("No bar ≤ target date for " + asset); - } - } - return chosen; - - } catch (IOException | InterruptedException ex) { - logger.error("Error fetching price for {} on {}:", asset, date, ex); - Thread.currentThread().interrupt(); - throw new PriceFetchFailedException( - String.format("Unable to fetch price for %s (%s)", asset, ex)); } - } } diff --git a/src/main/java/io/autoinvestor/infrastructure/repositories/AssetDocument.java b/src/main/java/io/autoinvestor/infrastructure/repositories/AssetDocument.java index c059d1a..5c40515 100644 --- a/src/main/java/io/autoinvestor/infrastructure/repositories/AssetDocument.java +++ b/src/main/java/io/autoinvestor/infrastructure/repositories/AssetDocument.java @@ -8,9 +8,9 @@ @Document(collection = "assets") public record AssetDocument( - @Id String assetId, - String mic, - String ticker, - String name, - @Field("occurredAt") Instant occurredAt, - Instant updatedAt) {} + @Id String assetId, + String mic, + String ticker, + String name, + @Field("occurredAt") Instant occurredAt, + Instant updatedAt) {} diff --git a/src/main/java/io/autoinvestor/infrastructure/repositories/AssetMapper.java b/src/main/java/io/autoinvestor/infrastructure/repositories/AssetMapper.java index a4c1d9b..6700614 100644 --- a/src/main/java/io/autoinvestor/infrastructure/repositories/AssetMapper.java +++ b/src/main/java/io/autoinvestor/infrastructure/repositories/AssetMapper.java @@ -9,23 +9,23 @@ @Component class AssetMapper { - AssetDocument toDocument(Asset domain) { - return new AssetDocument( - domain.getId().value(), - domain.mic(), - domain.ticker(), - domain.name(), - domain.getCreatedAt().toInstant(), - domain.getUpdatedAt().toInstant()); - } + AssetDocument toDocument(Asset domain) { + return new AssetDocument( + domain.getId().value(), + domain.mic(), + domain.ticker(), + domain.name(), + domain.getCreatedAt().toInstant(), + domain.getUpdatedAt().toInstant()); + } - public Asset toDomain(AssetDocument assetDocument) { - return Asset.from( - assetDocument.assetId(), - assetDocument.mic(), - assetDocument.ticker(), - assetDocument.name(), - Date.from(assetDocument.occurredAt()), - Date.from(assetDocument.updatedAt())); - } + public Asset toDomain(AssetDocument assetDocument) { + return Asset.from( + assetDocument.assetId(), + assetDocument.mic(), + assetDocument.ticker(), + assetDocument.name(), + Date.from(assetDocument.occurredAt()), + Date.from(assetDocument.updatedAt())); + } } diff --git a/src/main/java/io/autoinvestor/infrastructure/repositories/InMemoryAssetRepository.java b/src/main/java/io/autoinvestor/infrastructure/repositories/InMemoryAssetRepository.java index ff9135f..6838e1c 100644 --- a/src/main/java/io/autoinvestor/infrastructure/repositories/InMemoryAssetRepository.java +++ b/src/main/java/io/autoinvestor/infrastructure/repositories/InMemoryAssetRepository.java @@ -12,30 +12,30 @@ @Repository @Profile("local") public class InMemoryAssetRepository implements AssetRepository { - private final Map assetStore = new HashMap<>(); - - @Override - public void save(Asset asset) { - assetStore.put(asset.id(), asset); - } - - @Override - public boolean exists(String mic, String ticker) { - return assetStore.values().stream() - .anyMatch(asset -> asset.mic().equals(mic) && asset.ticker().equals(ticker)); - } - - @Override - public Optional findById(AssetId assetId) { - return Optional.ofNullable(assetStore.get(assetId.value())); - } - - @Override - public List findAll() { - return new ArrayList<>(assetStore.values()); - } - - public void clear() { - assetStore.clear(); - } + private final Map assetStore = new HashMap<>(); + + @Override + public void save(Asset asset) { + assetStore.put(asset.id(), asset); + } + + @Override + public boolean exists(String mic, String ticker) { + return assetStore.values().stream() + .anyMatch(asset -> asset.mic().equals(mic) && asset.ticker().equals(ticker)); + } + + @Override + public Optional findById(AssetId assetId) { + return Optional.ofNullable(assetStore.get(assetId.value())); + } + + @Override + public List findAll() { + return new ArrayList<>(assetStore.values()); + } + + public void clear() { + assetStore.clear(); + } } diff --git a/src/main/java/io/autoinvestor/infrastructure/repositories/MongoAssetRepository.java b/src/main/java/io/autoinvestor/infrastructure/repositories/MongoAssetRepository.java index 3e8ea26..9dd28d7 100644 --- a/src/main/java/io/autoinvestor/infrastructure/repositories/MongoAssetRepository.java +++ b/src/main/java/io/autoinvestor/infrastructure/repositories/MongoAssetRepository.java @@ -18,41 +18,41 @@ @Profile("prod") class MongoAssetRepository implements AssetRepository { - private final MongoTemplate template; - private final AssetMapper mapper; - - public MongoAssetRepository(MongoTemplate template, AssetMapper mapper) { - this.template = template; - this.mapper = mapper; - } - - @Override - public void save(Asset asset) { - template.save(mapper.toDocument(asset)); - } - - @Override - public boolean exists(String mic, String ticker) { - var q = Query.query(Criteria.where("mic").is(mic).and("ticker").is(ticker)); - return template.exists(q, AssetDocument.class); - } - - @Override - public Optional findById(AssetId assetId) { - String id = assetId.value(); - var q = Query.query(Criteria.where("_id").is(id)); - AssetDocument doc = template.findOne(q, AssetDocument.class); - return Optional.ofNullable(doc).map(mapper::toDomain); - } - - @Override - public List findAll() { - var q = new Query(); - var assetDocuments = template.find(q, AssetDocument.class); - List assets = new ArrayList<>(); - for (AssetDocument assetDocument : assetDocuments) { - assets.add(mapper.toDomain(assetDocument)); + private final MongoTemplate template; + private final AssetMapper mapper; + + public MongoAssetRepository(MongoTemplate template, AssetMapper mapper) { + this.template = template; + this.mapper = mapper; + } + + @Override + public void save(Asset asset) { + template.save(mapper.toDocument(asset)); + } + + @Override + public boolean exists(String mic, String ticker) { + var q = Query.query(Criteria.where("mic").is(mic).and("ticker").is(ticker)); + return template.exists(q, AssetDocument.class); + } + + @Override + public Optional findById(AssetId assetId) { + String id = assetId.value(); + var q = Query.query(Criteria.where("_id").is(id)); + AssetDocument doc = template.findOne(q, AssetDocument.class); + return Optional.ofNullable(doc).map(mapper::toDomain); + } + + @Override + public List findAll() { + var q = new Query(); + var assetDocuments = template.find(q, AssetDocument.class); + List assets = new ArrayList<>(); + for (AssetDocument assetDocument : assetDocuments) { + assets.add(mapper.toDomain(assetDocument)); + } + return assets; } - return assets; - } } diff --git a/src/main/java/io/autoinvestor/ui/AssetDTO.java b/src/main/java/io/autoinvestor/ui/AssetDTO.java index bdb84f1..9904f55 100644 --- a/src/main/java/io/autoinvestor/ui/AssetDTO.java +++ b/src/main/java/io/autoinvestor/ui/AssetDTO.java @@ -3,7 +3,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; public record AssetDTO( - @JsonFormat(shape = JsonFormat.Shape.STRING) String assetId, - String mic, - String ticker, - String name) {} + @JsonFormat(shape = JsonFormat.Shape.STRING) String assetId, + String mic, + String ticker, + String name) {} diff --git a/src/main/java/io/autoinvestor/ui/ErrorResponse.java b/src/main/java/io/autoinvestor/ui/ErrorResponse.java index 48c86f0..568827b 100644 --- a/src/main/java/io/autoinvestor/ui/ErrorResponse.java +++ b/src/main/java/io/autoinvestor/ui/ErrorResponse.java @@ -1,7 +1,7 @@ package io.autoinvestor.ui; public record ErrorResponse(int status, String error) { - public static ErrorResponseBuilder builder() { - return new ErrorResponseBuilder(); - } + public static ErrorResponseBuilder builder() { + return new ErrorResponseBuilder(); + } } diff --git a/src/main/java/io/autoinvestor/ui/ErrorResponseBuilder.java b/src/main/java/io/autoinvestor/ui/ErrorResponseBuilder.java index 371baad..b6a8d2b 100644 --- a/src/main/java/io/autoinvestor/ui/ErrorResponseBuilder.java +++ b/src/main/java/io/autoinvestor/ui/ErrorResponseBuilder.java @@ -4,28 +4,28 @@ import org.springframework.http.ResponseEntity; public class ErrorResponseBuilder { - private int status = HttpStatus.INTERNAL_SERVER_ERROR.value(); - private String message = "An unexpected error occurred"; + private int status = HttpStatus.INTERNAL_SERVER_ERROR.value(); + private String message = "An unexpected error occurred"; - ErrorResponseBuilder() {} + ErrorResponseBuilder() {} - public ErrorResponseBuilder status(HttpStatus status) { - this.status = status.value(); - return this; - } + public ErrorResponseBuilder status(HttpStatus status) { + this.status = status.value(); + return this; + } - public ErrorResponseBuilder status(int status) { - this.status = status; - return this; - } + public ErrorResponseBuilder status(int status) { + this.status = status; + return this; + } - public ErrorResponseBuilder message(String message) { - this.message = message; - return this; - } + public ErrorResponseBuilder message(String message) { + this.message = message; + return this; + } - public ResponseEntity build() { - ErrorResponse errorResponse = new ErrorResponse(this.status, this.message); - return ResponseEntity.status(this.status).body(errorResponse); - } + public ResponseEntity build() { + ErrorResponse errorResponse = new ErrorResponse(this.status, this.message); + return ResponseEntity.status(this.status).body(errorResponse); + } } diff --git a/src/main/java/io/autoinvestor/ui/GetAllAssetsController.java b/src/main/java/io/autoinvestor/ui/GetAllAssetsController.java index 57be914..c59988b 100644 --- a/src/main/java/io/autoinvestor/ui/GetAllAssetsController.java +++ b/src/main/java/io/autoinvestor/ui/GetAllAssetsController.java @@ -13,19 +13,19 @@ @RequestMapping("/assets") public class GetAllAssetsController { - private final GetAllAssetsCommandHandler handler; + private final GetAllAssetsCommandHandler handler; - public GetAllAssetsController(GetAllAssetsCommandHandler handler) { - this.handler = handler; - } + public GetAllAssetsController(GetAllAssetsCommandHandler handler) { + this.handler = handler; + } - @GetMapping - public ResponseEntity> getAllAssets() { - List assets = handler.handle(); - List dtos = - assets.stream() - .map(a -> new AssetDTO(a.assetId(), a.mic(), a.ticker(), a.name())) - .collect(Collectors.toList()); - return ResponseEntity.ok(dtos); - } + @GetMapping + public ResponseEntity> getAllAssets() { + List assets = handler.handle(); + List dtos = + assets.stream() + .map(a -> new AssetDTO(a.assetId(), a.mic(), a.ticker(), a.name())) + .collect(Collectors.toList()); + return ResponseEntity.ok(dtos); + } } diff --git a/src/main/java/io/autoinvestor/ui/GetAssetController.java b/src/main/java/io/autoinvestor/ui/GetAssetController.java index 6b2e6a7..b089f40 100644 --- a/src/main/java/io/autoinvestor/ui/GetAssetController.java +++ b/src/main/java/io/autoinvestor/ui/GetAssetController.java @@ -11,17 +11,18 @@ @RequestMapping("/assets") public class GetAssetController { - private final GetAssetCommandHandler handler; + private final GetAssetCommandHandler handler; - public GetAssetController(GetAssetCommandHandler handler) { - this.handler = handler; - } + public GetAssetController(GetAssetCommandHandler handler) { + this.handler = handler; + } - @GetMapping("/{assetId}") - public ResponseEntity getAsset(@PathVariable String assetId) { - GetAssetResponse response = handler.handle(new GetAssetCommand(assetId)); - AssetDTO dto = - new AssetDTO(response.assetId(), response.mic(), response.ticker(), response.name()); - return ResponseEntity.ok(dto); - } + @GetMapping("/{assetId}") + public ResponseEntity getAsset(@PathVariable String assetId) { + GetAssetResponse response = handler.handle(new GetAssetCommand(assetId)); + AssetDTO dto = + new AssetDTO( + response.assetId(), response.mic(), response.ticker(), response.name()); + return ResponseEntity.ok(dto); + } } diff --git a/src/main/java/io/autoinvestor/ui/GetAssetPriceController.java b/src/main/java/io/autoinvestor/ui/GetAssetPriceController.java index 37baa38..9e2e9e4 100644 --- a/src/main/java/io/autoinvestor/ui/GetAssetPriceController.java +++ b/src/main/java/io/autoinvestor/ui/GetAssetPriceController.java @@ -15,22 +15,23 @@ @RequestMapping("/assets") public class GetAssetPriceController { - private final GetAssetPriceCommandHandler handler; + private final GetAssetPriceCommandHandler handler; - public GetAssetPriceController(GetAssetPriceCommandHandler handler) { - this.handler = handler; - } + public GetAssetPriceController(GetAssetPriceCommandHandler handler) { + this.handler = handler; + } - @GetMapping("/{assetId}/price") - public ResponseEntity getPrice( - @PathVariable String assetId, - @RequestParam(name = "at", required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) - Date at) { + @GetMapping("/{assetId}/price") + public ResponseEntity getPrice( + @PathVariable String assetId, + @RequestParam(name = "at", required = false) + @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) + Date at) { - Date date = at != null ? at : Date.from(Instant.now()); + Date date = at != null ? at : Date.from(Instant.now()); - GetAssetPriceResponse response = handler.handle(new GetAssetPriceCommand(assetId, date)); - PriceDTO dto = new PriceDTO(response.date(), Math.round(response.price() * 100)); - return ResponseEntity.ok(dto); - } + GetAssetPriceResponse response = handler.handle(new GetAssetPriceCommand(assetId, date)); + PriceDTO dto = new PriceDTO(response.date(), Math.round(response.price() * 100)); + return ResponseEntity.ok(dto); + } } diff --git a/src/main/java/io/autoinvestor/ui/GlobalExceptionHandler.java b/src/main/java/io/autoinvestor/ui/GlobalExceptionHandler.java index a7df645..e813776 100644 --- a/src/main/java/io/autoinvestor/ui/GlobalExceptionHandler.java +++ b/src/main/java/io/autoinvestor/ui/GlobalExceptionHandler.java @@ -10,47 +10,50 @@ import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice( - assignableTypes = { - RegisterAssetController.class, - GetAllAssetsController.class, - GetAssetController.class, - GetAssetPriceController.class, - }) + assignableTypes = { + RegisterAssetController.class, + GetAllAssetsController.class, + GetAssetController.class, + GetAssetPriceController.class, + }) public class GlobalExceptionHandler { - @ExceptionHandler(DuplicatedException.class) - public ResponseEntity handleDuplicatedException(DuplicatedException ex) { - return ErrorResponse.builder().status(HttpStatus.CONFLICT).message(ex.getMessage()).build(); - } - - @ExceptionHandler(BadRequestException.class) - public ResponseEntity handleBadRequestException(BadRequestException ex) { - return ErrorResponse.builder().status(HttpStatus.BAD_REQUEST).message(ex.getMessage()).build(); - } - - @ExceptionHandler(PriceNotAvailableException.class) - public ResponseEntity handlePriceNotAvailableException( - PriceNotAvailableException ex) { - return ErrorResponse.builder() - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .message(ex.getMessage()) - .build(); - } - - @ExceptionHandler(PriceFetchFailedException.class) - public ResponseEntity handlePriceFetchFailedException( - PriceFetchFailedException ex) { - return ErrorResponse.builder() - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .message(ex.getMessage()) - .build(); - } - - @ExceptionHandler(InternalErrorException.class) - public ResponseEntity handleInternalErrorException(InternalErrorException ex) { - return ErrorResponse.builder() - .status(HttpStatus.INTERNAL_SERVER_ERROR) - .message(ex.getMessage()) - .build(); - } + @ExceptionHandler(DuplicatedException.class) + public ResponseEntity handleDuplicatedException(DuplicatedException ex) { + return ErrorResponse.builder().status(HttpStatus.CONFLICT).message(ex.getMessage()).build(); + } + + @ExceptionHandler(BadRequestException.class) + public ResponseEntity handleBadRequestException(BadRequestException ex) { + return ErrorResponse.builder() + .status(HttpStatus.BAD_REQUEST) + .message(ex.getMessage()) + .build(); + } + + @ExceptionHandler(PriceNotAvailableException.class) + public ResponseEntity handlePriceNotAvailableException( + PriceNotAvailableException ex) { + return ErrorResponse.builder() + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .message(ex.getMessage()) + .build(); + } + + @ExceptionHandler(PriceFetchFailedException.class) + public ResponseEntity handlePriceFetchFailedException( + PriceFetchFailedException ex) { + return ErrorResponse.builder() + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .message(ex.getMessage()) + .build(); + } + + @ExceptionHandler(InternalErrorException.class) + public ResponseEntity handleInternalErrorException(InternalErrorException ex) { + return ErrorResponse.builder() + .status(HttpStatus.INTERNAL_SERVER_ERROR) + .message(ex.getMessage()) + .build(); + } } diff --git a/src/main/java/io/autoinvestor/ui/InvalidRegisterAssetInputException.java b/src/main/java/io/autoinvestor/ui/InvalidRegisterAssetInputException.java index 0ec4fc8..22af6ca 100644 --- a/src/main/java/io/autoinvestor/ui/InvalidRegisterAssetInputException.java +++ b/src/main/java/io/autoinvestor/ui/InvalidRegisterAssetInputException.java @@ -3,7 +3,7 @@ import io.autoinvestor.exceptions.BadRequestException; public class InvalidRegisterAssetInputException extends BadRequestException { - public InvalidRegisterAssetInputException(String message) { - super(message); - } + public InvalidRegisterAssetInputException(String message) { + super(message); + } } diff --git a/src/main/java/io/autoinvestor/ui/RegisterAssetController.java b/src/main/java/io/autoinvestor/ui/RegisterAssetController.java index a10fdfc..8db8768 100644 --- a/src/main/java/io/autoinvestor/ui/RegisterAssetController.java +++ b/src/main/java/io/autoinvestor/ui/RegisterAssetController.java @@ -14,19 +14,21 @@ @RequestMapping("/assets") public class RegisterAssetController { - private final RegisterAssetCommandHandler commandHandler; + private final RegisterAssetCommandHandler commandHandler; - public RegisterAssetController(RegisterAssetCommandHandler commandHandler) { - this.commandHandler = commandHandler; - } + public RegisterAssetController(RegisterAssetCommandHandler commandHandler) { + this.commandHandler = commandHandler; + } - @PostMapping - public ResponseEntity handle(@RequestBody RegisterAssetDTO queryDto) { - RegisterAssetResponse response = - this.commandHandler.handle( - new RegisterAssetCommand(queryDto.mic(), queryDto.ticker(), queryDto.name())); - AssetDTO dto = - new AssetDTO(response.assetId(), response.mic(), response.ticker(), response.name()); - return ResponseEntity.ok(dto); - } + @PostMapping + public ResponseEntity handle(@RequestBody RegisterAssetDTO queryDto) { + RegisterAssetResponse response = + this.commandHandler.handle( + new RegisterAssetCommand( + queryDto.mic(), queryDto.ticker(), queryDto.name())); + AssetDTO dto = + new AssetDTO( + response.assetId(), response.mic(), response.ticker(), response.name()); + return ResponseEntity.ok(dto); + } } diff --git a/src/main/java/io/autoinvestor/ui/RegisterAssetDTO.java b/src/main/java/io/autoinvestor/ui/RegisterAssetDTO.java index 68a9395..b2dc3f7 100644 --- a/src/main/java/io/autoinvestor/ui/RegisterAssetDTO.java +++ b/src/main/java/io/autoinvestor/ui/RegisterAssetDTO.java @@ -5,26 +5,26 @@ public record RegisterAssetDTO(String mic, String ticker, String name) { - @JsonCreator - public RegisterAssetDTO( - @JsonProperty("mic") String mic, - @JsonProperty("ticker") String ticker, - @JsonProperty("name") String name) { - if (mic == null - || ticker == null - || name == null - || mic.isEmpty() - || ticker.isEmpty() - || name.isEmpty()) { - throw new InvalidRegisterAssetInputException("All fields should not be null"); - } + @JsonCreator + public RegisterAssetDTO( + @JsonProperty("mic") String mic, + @JsonProperty("ticker") String ticker, + @JsonProperty("name") String name) { + if (mic == null + || ticker == null + || name == null + || mic.isEmpty() + || ticker.isEmpty() + || name.isEmpty()) { + throw new InvalidRegisterAssetInputException("All fields should not be null"); + } - if (mic.trim().isEmpty() || ticker.trim().isEmpty() || name.trim().isEmpty()) { - throw new InvalidRegisterAssetInputException("All fields should not be empty"); - } + if (mic.trim().isEmpty() || ticker.trim().isEmpty() || name.trim().isEmpty()) { + throw new InvalidRegisterAssetInputException("All fields should not be empty"); + } - this.mic = mic; - this.ticker = ticker; - this.name = name; - } + this.mic = mic; + this.ticker = ticker; + this.name = name; + } }