diff --git a/HELP.md b/HELP.md index f876abc..bdbf568 100644 --- a/HELP.md +++ b/HELP.md @@ -11,4 +11,3 @@ For further reference, please consider the following sections: These additional references should also help you: * [Gradle Build Scans – insights for your project's build](https://scans.gradle.com#gradle) - diff --git a/build.gradle b/build.gradle index 8a43ec2..3bc36e2 100644 --- a/build.gradle +++ b/build.gradle @@ -1,53 +1,74 @@ plugins { - id 'java' - id 'org.springframework.boot' version '3.4.3' - id 'io.spring.dependency-management' version '1.1.7' - id 'io.freefair.lombok' version '8.13.1' + id 'java' + id 'org.springframework.boot' version '3.4.3' + id 'io.spring.dependency-management' version '1.1.7' + id 'io.freefair.lombok' version '8.13.1' + id 'com.diffplug.spotless' version '7.0.4' } group = 'io.autoinvestor' java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } } repositories { - mavenCentral() + mavenCentral() } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-web' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + implementation 'org.springframework.boot:spring-boot-starter-web' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' - implementation 'com.google.cloud:google-cloud-pubsub:1.123.0' - implementation "com.google.cloud:spring-cloud-gcp-starter-pubsub:6.1.1" - implementation 'com.fasterxml.jackson.core:jackson-databind' - implementation 'org.springframework.integration:spring-integration-core' - implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' - implementation("jakarta.validation:jakarta.validation-api:3.0.2") + implementation 'com.google.cloud:google-cloud-pubsub:1.123.0' + implementation "com.google.cloud:spring-cloud-gcp-starter-pubsub:6.1.1" + implementation 'com.fasterxml.jackson.core:jackson-databind' + implementation 'org.springframework.integration:spring-integration-core' + implementation 'org.springframework.boot:spring-boot-starter-data-mongodb' + implementation("jakarta.validation:jakarta.validation-api:3.0.2") - testImplementation 'org.springframework.boot:spring-boot-testcontainers' - testImplementation 'org.testcontainers:testcontainers' - testImplementation 'org.testcontainers:junit-jupiter' - testImplementation 'org.testcontainers:gcloud' + testImplementation 'org.springframework.boot:spring-boot-testcontainers' + testImplementation 'org.testcontainers:testcontainers' + testImplementation 'org.testcontainers:junit-jupiter' + testImplementation 'org.testcontainers:gcloud' - compileOnly 'org.projectlombok:lombok:1.18.38' - annotationProcessor 'org.projectlombok:lombok:1.18.38' + compileOnly 'org.projectlombok:lombok:1.18.38' + annotationProcessor 'org.projectlombok:lombok:1.18.38' - testCompileOnly 'org.projectlombok:lombok:1.18.38' - testAnnotationProcessor 'org.projectlombok:lombok:1.18.38' + testCompileOnly 'org.projectlombok:lombok:1.18.38' + testAnnotationProcessor 'org.projectlombok:lombok:1.18.38' } tasks.named('test') { - useJUnitPlatform() + useJUnitPlatform() } + tasks.withType(JavaCompile).configureEach { - options.compilerArgs << '-parameters' + options.compilerArgs << '-parameters' } bootBuildImage { - publish = false + publish = false +} + +spotless { + java { + googleJavaFormat('1.22.0').aosp() + removeUnusedImports() + trimTrailingWhitespace() + leadingTabsToSpaces() + endWithNewline() + importOrder '', 'java', 'javax', 'org', 'com' + target 'src/**/*.java' + } + + format 'misc', { + target '*.gradle', '*.md', '.gitignore' + leadingTabsToSpaces() + trimTrailingWhitespace() + endWithNewline() + } } diff --git a/src/main/java/io/autoinvestor/application/HoldingDeleteUseCase/HoldingDeleteCommand.java b/src/main/java/io/autoinvestor/application/HoldingDeleteUseCase/HoldingDeleteCommand.java index f193208..4708f18 100644 --- a/src/main/java/io/autoinvestor/application/HoldingDeleteUseCase/HoldingDeleteCommand.java +++ b/src/main/java/io/autoinvestor/application/HoldingDeleteUseCase/HoldingDeleteCommand.java @@ -1,7 +1,3 @@ package io.autoinvestor.application.HoldingDeleteUseCase; -public record HoldingDeleteCommand( - String userId, - String assetId -) { -} +public record HoldingDeleteCommand(String userId, String assetId) {} diff --git a/src/main/java/io/autoinvestor/application/HoldingDeleteUseCase/HoldingDeleteCommandHandler.java b/src/main/java/io/autoinvestor/application/HoldingDeleteUseCase/HoldingDeleteCommandHandler.java index 395b111..b83c47e 100644 --- a/src/main/java/io/autoinvestor/application/HoldingDeleteUseCase/HoldingDeleteCommandHandler.java +++ b/src/main/java/io/autoinvestor/application/HoldingDeleteUseCase/HoldingDeleteCommandHandler.java @@ -9,10 +9,11 @@ import io.autoinvestor.domain.model.WalletId; import io.autoinvestor.exceptions.UserWithoutPortfolio; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; import java.util.List; +import org.springframework.stereotype.Service; + @Service @RequiredArgsConstructor public class HoldingDeleteCommandHandler { @@ -22,13 +23,15 @@ public class HoldingDeleteCommandHandler { private final HoldingsReadModel holdingsReadModel; private final EventPublisher eventPublisher; - public void handle (HoldingDeleteCommand command) { + public void handle(HoldingDeleteCommand command) { String walletId = this.usersWalletReadModel.getWalletId(command.userId()); if (walletId == null) { throw UserWithoutPortfolio.with(command.userId()); } - Wallet wallet = this.eventStore.get(WalletId.of(walletId)) - .orElseThrow(() -> UserWithoutPortfolio.with(command.userId())); + Wallet wallet = + this.eventStore + .get(WalletId.of(walletId)) + .orElseThrow(() -> UserWithoutPortfolio.with(command.userId())); wallet.deleteHolding(command.userId(), command.assetId()); List> events = wallet.getUncommittedEvents(); diff --git a/src/main/java/io/autoinvestor/application/HoldingsReadModel.java b/src/main/java/io/autoinvestor/application/HoldingsReadModel.java index cc7cf0b..4afb59b 100644 --- a/src/main/java/io/autoinvestor/application/HoldingsReadModel.java +++ b/src/main/java/io/autoinvestor/application/HoldingsReadModel.java @@ -4,8 +4,12 @@ public interface HoldingsReadModel { void add(HoldingsReadModelDTO dto); + void update(HoldingsReadModelDTO dto); + boolean delete(String userId, String assetId); + List getHoldings(String userId); + boolean assetAlreadyExists(String userIs, String assetId); } diff --git a/src/main/java/io/autoinvestor/application/HoldingsReadModelDTO.java b/src/main/java/io/autoinvestor/application/HoldingsReadModelDTO.java index efad3c3..036b2f3 100644 --- a/src/main/java/io/autoinvestor/application/HoldingsReadModelDTO.java +++ b/src/main/java/io/autoinvestor/application/HoldingsReadModelDTO.java @@ -1,9 +1,4 @@ package io.autoinvestor.application; public record HoldingsReadModelDTO( - String userId, - String assetId, - Integer amount, - Integer boughtPrice -) { -} + String userId, String assetId, Integer amount, Integer boughtPrice) {} diff --git a/src/main/java/io/autoinvestor/application/NewHoldingUseCase/NewHoldingCommand.java b/src/main/java/io/autoinvestor/application/NewHoldingUseCase/NewHoldingCommand.java index fa469e2..2c3148b 100644 --- a/src/main/java/io/autoinvestor/application/NewHoldingUseCase/NewHoldingCommand.java +++ b/src/main/java/io/autoinvestor/application/NewHoldingUseCase/NewHoldingCommand.java @@ -1,9 +1,4 @@ package io.autoinvestor.application.NewHoldingUseCase; public record NewHoldingCommand( - String assetId, - String userId, - Integer amount, - Integer boughtPrice -) { -} + String assetId, String userId, Integer amount, Integer boughtPrice) {} diff --git a/src/main/java/io/autoinvestor/application/NewHoldingUseCase/NewHoldingCommandHandler.java b/src/main/java/io/autoinvestor/application/NewHoldingUseCase/NewHoldingCommandHandler.java index 9123bc9..2514710 100644 --- a/src/main/java/io/autoinvestor/application/NewHoldingUseCase/NewHoldingCommandHandler.java +++ b/src/main/java/io/autoinvestor/application/NewHoldingUseCase/NewHoldingCommandHandler.java @@ -12,10 +12,11 @@ import io.autoinvestor.exceptions.AssetAlreadyExists; import io.autoinvestor.exceptions.UserWithoutPortfolio; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; import java.util.List; +import org.springframework.stereotype.Service; + @Service @RequiredArgsConstructor public class NewHoldingCommandHandler { @@ -34,22 +35,32 @@ public void handle(NewHoldingCommand command) { if (this.holdingsReadModel.assetAlreadyExists(command.userId(), command.assetId())) { throw AssetAlreadyExists.with(command.userId(), command.assetId()); } - Wallet wallet = this.eventStore.get(WalletId.of(walletId)) - .orElseThrow(() -> UserWithoutPortfolio.with(command.userId())); + Wallet wallet = + this.eventStore + .get(WalletId.of(walletId)) + .orElseThrow(() -> UserWithoutPortfolio.with(command.userId())); - wallet.createHolding(command.userId(), command.assetId(), command.amount(), command.boughtPrice()); + wallet.createHolding( + command.userId(), command.assetId(), command.amount(), command.boughtPrice()); List> events = wallet.getUncommittedEvents(); this.eventStore.save(wallet); - HoldingsReadModelDTO dto = new HoldingsReadModelDTO( - wallet.getState().getUserId().value(), - command.assetId(), - wallet.getState().getHoldings().get(AssetId.of(command.assetId())).amount().value(), - wallet.getState().getHoldings().get(AssetId.of(command.assetId())).boughtPrice().value() - - ); + HoldingsReadModelDTO dto = + new HoldingsReadModelDTO( + wallet.getState().getUserId().value(), + command.assetId(), + wallet.getState() + .getHoldings() + .get(AssetId.of(command.assetId())) + .amount() + .value(), + wallet.getState() + .getHoldings() + .get(AssetId.of(command.assetId())) + .boughtPrice() + .value()); this.holdingsReadModel.add(dto); this.eventPublisher.publish(events); diff --git a/src/main/java/io/autoinvestor/application/QueryHoldingsUseCase/GetHoldingsQuery.java b/src/main/java/io/autoinvestor/application/QueryHoldingsUseCase/GetHoldingsQuery.java index 744f6c3..bf80510 100644 --- a/src/main/java/io/autoinvestor/application/QueryHoldingsUseCase/GetHoldingsQuery.java +++ b/src/main/java/io/autoinvestor/application/QueryHoldingsUseCase/GetHoldingsQuery.java @@ -1,5 +1,3 @@ package io.autoinvestor.application.QueryHoldingsUseCase; -public record GetHoldingsQuery( - String userId -) {} +public record GetHoldingsQuery(String userId) {} diff --git a/src/main/java/io/autoinvestor/application/QueryHoldingsUseCase/GetHoldingsQueryHandler.java b/src/main/java/io/autoinvestor/application/QueryHoldingsUseCase/GetHoldingsQueryHandler.java index 65b7a48..e3f6817 100644 --- a/src/main/java/io/autoinvestor/application/QueryHoldingsUseCase/GetHoldingsQueryHandler.java +++ b/src/main/java/io/autoinvestor/application/QueryHoldingsUseCase/GetHoldingsQueryHandler.java @@ -3,17 +3,18 @@ import io.autoinvestor.application.HoldingsReadModel; import io.autoinvestor.application.HoldingsReadModelDTO; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; import java.util.List; +import org.springframework.stereotype.Service; + @Service @RequiredArgsConstructor public class GetHoldingsQueryHandler { private final HoldingsReadModel readModel; - public List handle (GetHoldingsQuery query) { + public List handle(GetHoldingsQuery query) { return this.readModel.getHoldings(query.userId()); } } diff --git a/src/main/java/io/autoinvestor/application/UptdateHoldingUseCase/UpdateHoldingCommand.java b/src/main/java/io/autoinvestor/application/UptdateHoldingUseCase/UpdateHoldingCommand.java index d9d2012..240e018 100644 --- a/src/main/java/io/autoinvestor/application/UptdateHoldingUseCase/UpdateHoldingCommand.java +++ b/src/main/java/io/autoinvestor/application/UptdateHoldingUseCase/UpdateHoldingCommand.java @@ -1,9 +1,4 @@ package io.autoinvestor.application.UptdateHoldingUseCase; public record UpdateHoldingCommand( - String userId, - String assetId, - Integer amount, - Integer boughtPrice -) { -} + String userId, String assetId, Integer amount, Integer boughtPrice) {} diff --git a/src/main/java/io/autoinvestor/application/UptdateHoldingUseCase/UpdateHoldingCommandHandler.java b/src/main/java/io/autoinvestor/application/UptdateHoldingUseCase/UpdateHoldingCommandHandler.java index 1ef4ccb..1b70357 100644 --- a/src/main/java/io/autoinvestor/application/UptdateHoldingUseCase/UpdateHoldingCommandHandler.java +++ b/src/main/java/io/autoinvestor/application/UptdateHoldingUseCase/UpdateHoldingCommandHandler.java @@ -3,18 +3,19 @@ import io.autoinvestor.application.HoldingsReadModel; import io.autoinvestor.application.HoldingsReadModelDTO; import io.autoinvestor.application.UsersWalletReadModel; +import io.autoinvestor.domain.events.Event; import io.autoinvestor.domain.events.EventPublisher; +import io.autoinvestor.domain.events.WalletEventStoreRepository; import io.autoinvestor.domain.model.AssetId; -import io.autoinvestor.domain.events.Event; import io.autoinvestor.domain.model.Wallet; -import io.autoinvestor.domain.events.WalletEventStoreRepository; import io.autoinvestor.domain.model.WalletId; import io.autoinvestor.exceptions.UserWithoutPortfolio; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; import java.util.List; +import org.springframework.stereotype.Service; + @Service @RequiredArgsConstructor public class UpdateHoldingCommandHandler { @@ -24,27 +25,38 @@ public class UpdateHoldingCommandHandler { private final WalletEventStoreRepository eventStore; private final EventPublisher eventPublisher; - public void handle (UpdateHoldingCommand command) { + public void handle(UpdateHoldingCommand command) { String walletId = this.usersWalletReadModel.getWalletId(command.userId()); if (walletId == null) { throw UserWithoutPortfolio.with(command.userId()); } - Wallet wallet = this.eventStore.get(WalletId.of(walletId)) - .orElseThrow(() -> UserWithoutPortfolio.with(command.userId())); + Wallet wallet = + this.eventStore + .get(WalletId.of(walletId)) + .orElseThrow(() -> UserWithoutPortfolio.with(command.userId())); - wallet.updateHolding(command.userId(), command.assetId(), command.amount(), command.boughtPrice()); + wallet.updateHolding( + command.userId(), command.assetId(), command.amount(), command.boughtPrice()); List> events = wallet.getUncommittedEvents(); this.eventStore.save(wallet); - HoldingsReadModelDTO dto = new HoldingsReadModelDTO( - wallet.getState().getUserId().value(), - command.assetId(), - wallet.getState().getHoldings().get(AssetId.of(command.assetId())).amount().value(), - wallet.getState().getHoldings().get(AssetId.of(command.assetId())).boughtPrice().value() - ); + HoldingsReadModelDTO dto = + new HoldingsReadModelDTO( + wallet.getState().getUserId().value(), + command.assetId(), + wallet.getState() + .getHoldings() + .get(AssetId.of(command.assetId())) + .amount() + .value(), + wallet.getState() + .getHoldings() + .get(AssetId.of(command.assetId())) + .boughtPrice() + .value()); this.holdingsReadModel.update(dto); this.eventPublisher.publish(events); diff --git a/src/main/java/io/autoinvestor/application/UsersWalletReadModel.java b/src/main/java/io/autoinvestor/application/UsersWalletReadModel.java index 7b641fe..219cf40 100644 --- a/src/main/java/io/autoinvestor/application/UsersWalletReadModel.java +++ b/src/main/java/io/autoinvestor/application/UsersWalletReadModel.java @@ -2,5 +2,6 @@ public interface UsersWalletReadModel { void add(UsersWalletReadModelDTO dto); + String getWalletId(String userId); } diff --git a/src/main/java/io/autoinvestor/application/UsersWalletReadModelDTO.java b/src/main/java/io/autoinvestor/application/UsersWalletReadModelDTO.java index 9dbdf1a..5d1eeff 100644 --- a/src/main/java/io/autoinvestor/application/UsersWalletReadModelDTO.java +++ b/src/main/java/io/autoinvestor/application/UsersWalletReadModelDTO.java @@ -1,7 +1,3 @@ package io.autoinvestor.application; -public record UsersWalletReadModelDTO( - String walletId, - String userId -) { -} +public record UsersWalletReadModelDTO(String walletId, String userId) {} diff --git a/src/main/java/io/autoinvestor/application/WalletCreatedUseCase/WalletCreateCommand.java b/src/main/java/io/autoinvestor/application/WalletCreatedUseCase/WalletCreateCommand.java index a9ea383..a4dfdca 100644 --- a/src/main/java/io/autoinvestor/application/WalletCreatedUseCase/WalletCreateCommand.java +++ b/src/main/java/io/autoinvestor/application/WalletCreatedUseCase/WalletCreateCommand.java @@ -1,5 +1,3 @@ package io.autoinvestor.application.WalletCreatedUseCase; -public record WalletCreateCommand( - String userId -) {} +public record WalletCreateCommand(String userId) {} diff --git a/src/main/java/io/autoinvestor/application/WalletCreatedUseCase/WalletCreatedHandler.java b/src/main/java/io/autoinvestor/application/WalletCreatedUseCase/WalletCreatedHandler.java index 16b7ee6..4cb92b0 100644 --- a/src/main/java/io/autoinvestor/application/WalletCreatedUseCase/WalletCreatedHandler.java +++ b/src/main/java/io/autoinvestor/application/WalletCreatedUseCase/WalletCreatedHandler.java @@ -1,15 +1,17 @@ package io.autoinvestor.application.WalletCreatedUseCase; + import io.autoinvestor.application.UsersWalletReadModel; import io.autoinvestor.application.UsersWalletReadModelDTO; import io.autoinvestor.domain.events.Event; -import io.autoinvestor.domain.events.WalletEventStoreRepository; import io.autoinvestor.domain.events.EventPublisher; +import io.autoinvestor.domain.events.WalletEventStoreRepository; import io.autoinvestor.domain.model.Wallet; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; import java.util.List; +import org.springframework.stereotype.Component; + @Component @RequiredArgsConstructor public class WalletCreatedHandler { @@ -25,10 +27,10 @@ public void handle(WalletCreateCommand command) { this.eventStore.save(wallet); - UsersWalletReadModelDTO dto = new UsersWalletReadModelDTO( - wallet.getState().getWalletId().value(), - wallet.getState().getUserId().value() - ); + UsersWalletReadModelDTO dto = + new UsersWalletReadModelDTO( + wallet.getState().getWalletId().value(), + wallet.getState().getUserId().value()); this.readModel.add(dto); this.eventPublisher.publish(events); @@ -36,4 +38,3 @@ public void handle(WalletCreateCommand command) { wallet.markEventsAsCommitted(); } } - diff --git a/src/main/java/io/autoinvestor/domain/Id.java b/src/main/java/io/autoinvestor/domain/Id.java index 5c43c52..198a118 100644 --- a/src/main/java/io/autoinvestor/domain/Id.java +++ b/src/main/java/io/autoinvestor/domain/Id.java @@ -20,10 +20,8 @@ protected static String generateId() { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (!(o instanceof Id that)) - return false; + if (this == o) return true; + if (!(o instanceof Id that)) return false; return id.equals(that.id); } diff --git a/src/main/java/io/autoinvestor/domain/events/Event.java b/src/main/java/io/autoinvestor/domain/events/Event.java index 940207c..fe6725e 100644 --- a/src/main/java/io/autoinvestor/domain/events/Event.java +++ b/src/main/java/io/autoinvestor/domain/events/Event.java @@ -27,7 +27,8 @@ protected Event(Id aggregateId, String type, P payload, int version) { this.version = version; } - protected Event(EventId id, Id aggregateId, String type, P payload, Date occurredAt, int version) { + protected Event( + EventId id, Id aggregateId, String type, P payload, Date occurredAt, int version) { this.id = id; this.aggregateId = aggregateId; this.type = type; diff --git a/src/main/java/io/autoinvestor/domain/events/EventSourcedEntity.java b/src/main/java/io/autoinvestor/domain/events/EventSourcedEntity.java index e9b67b0..911eb3b 100644 --- a/src/main/java/io/autoinvestor/domain/events/EventSourcedEntity.java +++ b/src/main/java/io/autoinvestor/domain/events/EventSourcedEntity.java @@ -37,4 +37,3 @@ public void markEventsAsCommitted() { appliedEvents.clear(); } } - diff --git a/src/main/java/io/autoinvestor/domain/events/HoldingWasCreatedEvent.java b/src/main/java/io/autoinvestor/domain/events/HoldingWasCreatedEvent.java index 9fd39cd..14581f1 100644 --- a/src/main/java/io/autoinvestor/domain/events/HoldingWasCreatedEvent.java +++ b/src/main/java/io/autoinvestor/domain/events/HoldingWasCreatedEvent.java @@ -12,30 +12,33 @@ private HoldingWasCreatedEvent(Id aggregateId, HoldingWasCreatedEventPayload pay super(aggregateId, TYPE, payload); } - protected HoldingWasCreatedEvent(EventId id, - Id aggregateId, - HoldingWasCreatedEventPayload payload, - Date occurredAt, - int version) { + protected HoldingWasCreatedEvent( + EventId id, + Id aggregateId, + HoldingWasCreatedEventPayload payload, + Date occurredAt, + int version) { super(id, aggregateId, TYPE, payload, occurredAt, version); } - public static HoldingWasCreatedEvent with(WalletId walletId, - UserId userId, - AssetId assetId, - Amount amount, - BoughtPrice boughtPrice) { - HoldingWasCreatedEventPayload payload = new HoldingWasCreatedEventPayload( - userId.value(), assetId.value(), amount.value(), boughtPrice.value() - ); + public static HoldingWasCreatedEvent with( + WalletId walletId, + UserId userId, + AssetId assetId, + Amount amount, + BoughtPrice boughtPrice) { + HoldingWasCreatedEventPayload payload = + new HoldingWasCreatedEventPayload( + userId.value(), assetId.value(), amount.value(), boughtPrice.value()); return new HoldingWasCreatedEvent(walletId, payload); } - public static HoldingWasCreatedEvent hydrate(EventId id, - Id aggregateId, - HoldingWasCreatedEventPayload payload, - Date occurredAt, - int version) { + public static HoldingWasCreatedEvent hydrate( + EventId id, + Id aggregateId, + HoldingWasCreatedEventPayload payload, + Date occurredAt, + int version) { return new HoldingWasCreatedEvent(id, aggregateId, payload, occurredAt, version); } } diff --git a/src/main/java/io/autoinvestor/domain/events/HoldingWasCreatedEventPayload.java b/src/main/java/io/autoinvestor/domain/events/HoldingWasCreatedEventPayload.java index c2e38b0..00fb8b7 100644 --- a/src/main/java/io/autoinvestor/domain/events/HoldingWasCreatedEventPayload.java +++ b/src/main/java/io/autoinvestor/domain/events/HoldingWasCreatedEventPayload.java @@ -3,18 +3,13 @@ import java.util.Map; public record HoldingWasCreatedEventPayload( - String userId, - String assetId, - int amount, - int boughtPrice -) implements EventPayload { + String userId, String assetId, int amount, int boughtPrice) implements EventPayload { @Override public Map asMap() { return Map.of( "userId", userId, "assetId", assetId, "amount", amount, - "boughtPrice", boughtPrice - ); + "boughtPrice", boughtPrice); } } diff --git a/src/main/java/io/autoinvestor/domain/events/HoldingWasDeletedEvent.java b/src/main/java/io/autoinvestor/domain/events/HoldingWasDeletedEvent.java index df8313d..8e234e0 100644 --- a/src/main/java/io/autoinvestor/domain/events/HoldingWasDeletedEvent.java +++ b/src/main/java/io/autoinvestor/domain/events/HoldingWasDeletedEvent.java @@ -1,7 +1,6 @@ package io.autoinvestor.domain.events; import io.autoinvestor.domain.Id; -import io.autoinvestor.domain.model.Amount; import io.autoinvestor.domain.model.AssetId; import io.autoinvestor.domain.model.UserId; import io.autoinvestor.domain.model.WalletId; @@ -12,7 +11,7 @@ public class HoldingWasDeletedEvent extends Event public static final String TYPE = "PORTFOLIO_ASSET_REMOVED"; private HoldingWasDeletedEvent(Id aggregateId, HoldingWasDeletedEventPayload payload) { - super (aggregateId, TYPE, payload); + super(aggregateId, TYPE, payload); } protected HoldingWasDeletedEvent( @@ -24,21 +23,18 @@ protected HoldingWasDeletedEvent( super(id, aggregateId, TYPE, payload, occurredAt, version); } - public static HoldingWasDeletedEvent with(WalletId walletId, - UserId userId, - AssetId assetId) { - HoldingWasDeletedEventPayload payload = new HoldingWasDeletedEventPayload( - userId.value(), assetId.value() - ); + public static HoldingWasDeletedEvent with(WalletId walletId, UserId userId, AssetId assetId) { + HoldingWasDeletedEventPayload payload = + new HoldingWasDeletedEventPayload(userId.value(), assetId.value()); return new HoldingWasDeletedEvent(walletId, payload); } - public static HoldingWasDeletedEvent hydrate (EventId id, - Id aggregateId, - HoldingWasDeletedEventPayload payload, - Date occurredAt, - int version) { + public static HoldingWasDeletedEvent hydrate( + EventId id, + Id aggregateId, + HoldingWasDeletedEventPayload payload, + Date occurredAt, + int version) { return new HoldingWasDeletedEvent(id, aggregateId, payload, occurredAt, version); } - } diff --git a/src/main/java/io/autoinvestor/domain/events/HoldingWasDeletedEventPayload.java b/src/main/java/io/autoinvestor/domain/events/HoldingWasDeletedEventPayload.java index 5f200b4..772a499 100644 --- a/src/main/java/io/autoinvestor/domain/events/HoldingWasDeletedEventPayload.java +++ b/src/main/java/io/autoinvestor/domain/events/HoldingWasDeletedEventPayload.java @@ -2,15 +2,11 @@ import java.util.Map; -public record HoldingWasDeletedEventPayload( - String userId, - String assetId -) implements EventPayload { +public record HoldingWasDeletedEventPayload(String userId, String assetId) implements EventPayload { @Override public Map asMap() { return Map.of( - "userId" , userId, - "assetId", assetId - ); + "userId", userId, + "assetId", assetId); } } diff --git a/src/main/java/io/autoinvestor/domain/events/HoldingWasUpdatedEvent.java b/src/main/java/io/autoinvestor/domain/events/HoldingWasUpdatedEvent.java index 17547bd..8062773 100644 --- a/src/main/java/io/autoinvestor/domain/events/HoldingWasUpdatedEvent.java +++ b/src/main/java/io/autoinvestor/domain/events/HoldingWasUpdatedEvent.java @@ -12,30 +12,33 @@ private HoldingWasUpdatedEvent(Id aggregateId, HoldingWasUpdatedEventPayload pay super(aggregateId, TYPE, payload); } - protected HoldingWasUpdatedEvent(EventId id, - Id aggregateId, - HoldingWasUpdatedEventPayload payload, - Date occurredAt, - int version) { + protected HoldingWasUpdatedEvent( + EventId id, + Id aggregateId, + HoldingWasUpdatedEventPayload payload, + Date occurredAt, + int version) { super(id, aggregateId, TYPE, payload, occurredAt, version); } - public static HoldingWasUpdatedEvent with(WalletId walletId, - UserId userId, - AssetId assetId, - Amount amount, - BoughtPrice boughtPrice) { - HoldingWasUpdatedEventPayload payload = new HoldingWasUpdatedEventPayload( - userId.value(), assetId.value(), amount.value(), boughtPrice.value() - ); + public static HoldingWasUpdatedEvent with( + WalletId walletId, + UserId userId, + AssetId assetId, + Amount amount, + BoughtPrice boughtPrice) { + HoldingWasUpdatedEventPayload payload = + new HoldingWasUpdatedEventPayload( + userId.value(), assetId.value(), amount.value(), boughtPrice.value()); return new HoldingWasUpdatedEvent(walletId, payload); } - public static HoldingWasUpdatedEvent hydrate(EventId id, - Id aggregateId, - HoldingWasUpdatedEventPayload payload, - Date occurredAt, - int version) { + public static HoldingWasUpdatedEvent hydrate( + EventId id, + Id aggregateId, + HoldingWasUpdatedEventPayload payload, + Date occurredAt, + int version) { return new HoldingWasUpdatedEvent(id, aggregateId, payload, occurredAt, version); } } diff --git a/src/main/java/io/autoinvestor/domain/events/HoldingWasUpdatedEventPayload.java b/src/main/java/io/autoinvestor/domain/events/HoldingWasUpdatedEventPayload.java index 26af7fb..3274475 100644 --- a/src/main/java/io/autoinvestor/domain/events/HoldingWasUpdatedEventPayload.java +++ b/src/main/java/io/autoinvestor/domain/events/HoldingWasUpdatedEventPayload.java @@ -3,18 +3,13 @@ import java.util.Map; public record HoldingWasUpdatedEventPayload( - String userId, - String assetId, - int amount, - int boughtPrice -) implements EventPayload { + String userId, String assetId, int amount, int boughtPrice) implements EventPayload { @Override public Map asMap() { return Map.of( "userId", userId, "assetId", assetId, "amount", amount, - "boughtPrice", boughtPrice - ); + "boughtPrice", boughtPrice); } } diff --git a/src/main/java/io/autoinvestor/domain/events/WalletEventStoreRepository.java b/src/main/java/io/autoinvestor/domain/events/WalletEventStoreRepository.java index 01cee38..df947e7 100644 --- a/src/main/java/io/autoinvestor/domain/events/WalletEventStoreRepository.java +++ b/src/main/java/io/autoinvestor/domain/events/WalletEventStoreRepository.java @@ -7,5 +7,6 @@ public interface WalletEventStoreRepository { Optional get(WalletId walletId); + void save(Wallet wallet); } diff --git a/src/main/java/io/autoinvestor/domain/events/WalletWasCreatedEvent.java b/src/main/java/io/autoinvestor/domain/events/WalletWasCreatedEvent.java index 7061b81..30f88d3 100644 --- a/src/main/java/io/autoinvestor/domain/events/WalletWasCreatedEvent.java +++ b/src/main/java/io/autoinvestor/domain/events/WalletWasCreatedEvent.java @@ -12,28 +12,26 @@ private WalletWasCreatedEvent(Id aggregateId, WalletWasCreatedEventPayload paylo super(aggregateId, TYPE, payload); } - protected WalletWasCreatedEvent(EventId id, - Id aggregateId, - WalletWasCreatedEventPayload payload, - Date occurredAt, - int version) { + protected WalletWasCreatedEvent( + EventId id, + Id aggregateId, + WalletWasCreatedEventPayload payload, + Date occurredAt, + int version) { super(id, aggregateId, TYPE, payload, occurredAt, version); } - public static WalletWasCreatedEvent with(WalletId walletId, - UserId userId) { - WalletWasCreatedEventPayload payload = new WalletWasCreatedEventPayload( - userId.value() - ); + public static WalletWasCreatedEvent with(WalletId walletId, UserId userId) { + WalletWasCreatedEventPayload payload = new WalletWasCreatedEventPayload(userId.value()); return new WalletWasCreatedEvent(walletId, payload); } - public static WalletWasCreatedEvent hydrate(EventId id, - Id aggregateId, - WalletWasCreatedEventPayload payload, - Date occurredAt, - int version) { + public static WalletWasCreatedEvent hydrate( + EventId id, + Id aggregateId, + WalletWasCreatedEventPayload payload, + Date occurredAt, + int version) { return new WalletWasCreatedEvent(id, aggregateId, payload, occurredAt, version); } - } diff --git a/src/main/java/io/autoinvestor/domain/events/WalletWasCreatedEventPayload.java b/src/main/java/io/autoinvestor/domain/events/WalletWasCreatedEventPayload.java index 5a3fd0d..0ebe54b 100644 --- a/src/main/java/io/autoinvestor/domain/events/WalletWasCreatedEventPayload.java +++ b/src/main/java/io/autoinvestor/domain/events/WalletWasCreatedEventPayload.java @@ -2,13 +2,9 @@ import java.util.Map; -public record WalletWasCreatedEventPayload( - String userId -) implements EventPayload { +public record WalletWasCreatedEventPayload(String userId) implements EventPayload { @Override public Map asMap() { - return Map.of( - "userId", userId - ); + return Map.of("userId", userId); } } diff --git a/src/main/java/io/autoinvestor/domain/model/BoughtPrice.java b/src/main/java/io/autoinvestor/domain/model/BoughtPrice.java index 00feae2..ef54f23 100644 --- a/src/main/java/io/autoinvestor/domain/model/BoughtPrice.java +++ b/src/main/java/io/autoinvestor/domain/model/BoughtPrice.java @@ -15,7 +15,7 @@ public static BoughtPrice of(Integer boughtPrice) { return new BoughtPrice(boughtPrice); } - private static void validate (Integer boughtPrice) { + private static void validate(Integer boughtPrice) { if (boughtPrice < 1) { throw InvalidBoughtPrice.with(boughtPrice); } diff --git a/src/main/java/io/autoinvestor/domain/model/Holding.java b/src/main/java/io/autoinvestor/domain/model/Holding.java index bb35446..0cf07a0 100644 --- a/src/main/java/io/autoinvestor/domain/model/Holding.java +++ b/src/main/java/io/autoinvestor/domain/model/Holding.java @@ -1,9 +1,6 @@ package io.autoinvestor.domain.model; -public record Holding( - Amount amount, - BoughtPrice boughtPrice -) { +public record Holding(Amount amount, BoughtPrice boughtPrice) { public static Holding of(Amount amount, BoughtPrice boughtPrice) { return new Holding(amount, boughtPrice); } diff --git a/src/main/java/io/autoinvestor/domain/model/UserId.java b/src/main/java/io/autoinvestor/domain/model/UserId.java index 353d71d..108f326 100644 --- a/src/main/java/io/autoinvestor/domain/model/UserId.java +++ b/src/main/java/io/autoinvestor/domain/model/UserId.java @@ -3,7 +3,7 @@ import io.autoinvestor.domain.Id; public class UserId extends Id { - public UserId(String id){ + public UserId(String id) { super(id); } @@ -11,7 +11,7 @@ public static UserId generate() { return new UserId(generateId()); } - public static UserId of(String userId){ + public static UserId of(String userId) { return new UserId(userId); } diff --git a/src/main/java/io/autoinvestor/domain/model/Wallet.java b/src/main/java/io/autoinvestor/domain/model/Wallet.java index e51e25e..ab4b514 100644 --- a/src/main/java/io/autoinvestor/domain/model/Wallet.java +++ b/src/main/java/io/autoinvestor/domain/model/Wallet.java @@ -29,40 +29,36 @@ public static Wallet from(List> stream) { public static Wallet create(String userId) { Wallet wallet = Wallet.empty(); - wallet.apply(WalletWasCreatedEvent.with( - wallet.getState().getWalletId(), - UserId.of(userId)) - ); + wallet.apply( + WalletWasCreatedEvent.with(wallet.getState().getWalletId(), UserId.of(userId))); return wallet; } public void createHolding(String userId, String assetId, Integer amount, Integer boughtPrice) { - this.apply(HoldingWasCreatedEvent.with( - this.state.getWalletId(), - UserId.of(userId), - AssetId.of(assetId), - Amount.of(amount), - BoughtPrice.of(boughtPrice) - )); + this.apply( + HoldingWasCreatedEvent.with( + this.state.getWalletId(), + UserId.of(userId), + AssetId.of(assetId), + Amount.of(amount), + BoughtPrice.of(boughtPrice))); } public void updateHolding(String userId, String assetId, Integer amount, Integer boughtPrice) { - this.apply(HoldingWasUpdatedEvent.with( - this.state.getWalletId(), - UserId.of(userId), - AssetId.of(assetId), - Amount.of(amount), - BoughtPrice.of(boughtPrice) - )); + this.apply( + HoldingWasUpdatedEvent.with( + this.state.getWalletId(), + UserId.of(userId), + AssetId.of(assetId), + Amount.of(amount), + BoughtPrice.of(boughtPrice))); } public void deleteHolding(String userId, String assetId) { - this.apply(HoldingWasDeletedEvent.with( - this.state.getWalletId(), - UserId.of(userId), - AssetId.of(assetId) - )); + this.apply( + HoldingWasDeletedEvent.with( + this.state.getWalletId(), UserId.of(userId), AssetId.of(assetId))); } @Override diff --git a/src/main/java/io/autoinvestor/domain/model/WalletState.java b/src/main/java/io/autoinvestor/domain/model/WalletState.java index 66e9d38..d5dd298 100644 --- a/src/main/java/io/autoinvestor/domain/model/WalletState.java +++ b/src/main/java/io/autoinvestor/domain/model/WalletState.java @@ -1,6 +1,5 @@ package io.autoinvestor.domain.model; - import io.autoinvestor.domain.events.*; import lombok.Getter; @@ -28,42 +27,31 @@ public WalletState withWalletCreated(WalletWasCreatedEvent event) { return new WalletState( WalletId.of(event.getAggregateId().value()), UserId.of(payload.userId()), - new HashMap<>() - ); + new HashMap<>()); } public WalletState withHoldingCreated(HoldingWasCreatedEvent event) { HoldingWasCreatedEventPayload payload = event.getPayload(); WalletId walletId = (WalletId) event.getAggregateId(); UserId userId = UserId.of(payload.userId()); - Holding newHolding = Holding.of(Amount.of(payload.amount()), BoughtPrice.of(payload.boughtPrice())); - Map holdingsUpdated = new HashMap<>(this.holdings); + Holding newHolding = + Holding.of(Amount.of(payload.amount()), BoughtPrice.of(payload.boughtPrice())); + Map holdingsUpdated = new HashMap<>(this.holdings); holdingsUpdated.put(AssetId.of(payload.assetId()), newHolding); - return new WalletState( - walletId, - userId, - holdingsUpdated - ); + return new WalletState(walletId, userId, holdingsUpdated); } public WalletState withHoldingUpdated(HoldingWasUpdatedEvent event) { HoldingWasUpdatedEventPayload payload = event.getPayload(); - Holding holdingUpdated = Holding.of(Amount.of(payload.amount()), BoughtPrice.of(payload.boughtPrice())); + Holding holdingUpdated = + Holding.of(Amount.of(payload.amount()), BoughtPrice.of(payload.boughtPrice())); holdings.put(AssetId.of(payload.assetId()), holdingUpdated); - return new WalletState( - this.walletId, - this.userId, - this.holdings - ); + return new WalletState(this.walletId, this.userId, this.holdings); } public WalletState withHoldingDeleted(HoldingWasDeletedEvent event) { HoldingWasDeletedEventPayload payload = event.getPayload(); holdings.remove(AssetId.of(payload.assetId())); - return new WalletState( - this.walletId, - this.userId, - this.holdings - ); + return new WalletState(this.walletId, this.userId, this.holdings); } } diff --git a/src/main/java/io/autoinvestor/exceptions/AssetAlreadyExists.java b/src/main/java/io/autoinvestor/exceptions/AssetAlreadyExists.java index 91021d8..feeaf84 100644 --- a/src/main/java/io/autoinvestor/exceptions/AssetAlreadyExists.java +++ b/src/main/java/io/autoinvestor/exceptions/AssetAlreadyExists.java @@ -5,7 +5,7 @@ private AssetAlreadyExists(String message) { super(message); } - public static AssetAlreadyExists with (String userId, String assetId) { + public static AssetAlreadyExists with(String userId, String assetId) { String message = "Duplicate asset " + assetId + " for user " + userId; return new AssetAlreadyExists(message); } diff --git a/src/main/java/io/autoinvestor/exceptions/AssetNotFound.java b/src/main/java/io/autoinvestor/exceptions/AssetNotFound.java index 7f6f19b..5b1dcf7 100644 --- a/src/main/java/io/autoinvestor/exceptions/AssetNotFound.java +++ b/src/main/java/io/autoinvestor/exceptions/AssetNotFound.java @@ -5,7 +5,7 @@ private AssetNotFound(String message) { super(message); } - public static AssetNotFound with (String assetId) { + public static AssetNotFound with(String assetId) { String errorMessage = "Asset with assetId " + assetId + " doesn't exist"; return new AssetNotFound(errorMessage); } diff --git a/src/main/java/io/autoinvestor/exceptions/InvalidAmount.java b/src/main/java/io/autoinvestor/exceptions/InvalidAmount.java index e6221c0..029cbc2 100644 --- a/src/main/java/io/autoinvestor/exceptions/InvalidAmount.java +++ b/src/main/java/io/autoinvestor/exceptions/InvalidAmount.java @@ -5,7 +5,7 @@ private InvalidAmount(String message) { super(message); } - public static InvalidAmount with (Integer amount) { + public static InvalidAmount with(Integer amount) { String exceptionMessage = "Amount of " + amount + " is not valid"; return new InvalidAmount(exceptionMessage); } diff --git a/src/main/java/io/autoinvestor/exceptions/InvalidBoughtPrice.java b/src/main/java/io/autoinvestor/exceptions/InvalidBoughtPrice.java index bce3c82..f81d233 100644 --- a/src/main/java/io/autoinvestor/exceptions/InvalidBoughtPrice.java +++ b/src/main/java/io/autoinvestor/exceptions/InvalidBoughtPrice.java @@ -5,9 +5,11 @@ private InvalidBoughtPrice(String message) { super(message); } - public static InvalidBoughtPrice with (Integer boughtPrice) { - String exceptionMessage = "The bought price of " + boughtPrice + - " is not valid. It should be greater than 0."; + public static InvalidBoughtPrice with(Integer boughtPrice) { + String exceptionMessage = + "The bought price of " + + boughtPrice + + " is not valid. It should be greater than 0."; return new InvalidBoughtPrice(exceptionMessage); } } diff --git a/src/main/java/io/autoinvestor/exceptions/UserWithoutPortfolio.java b/src/main/java/io/autoinvestor/exceptions/UserWithoutPortfolio.java index 64966a2..8939e5d 100644 --- a/src/main/java/io/autoinvestor/exceptions/UserWithoutPortfolio.java +++ b/src/main/java/io/autoinvestor/exceptions/UserWithoutPortfolio.java @@ -4,7 +4,8 @@ public class UserWithoutPortfolio extends RuntimeException { private UserWithoutPortfolio(String message) { super(message); } - public static UserWithoutPortfolio with (String userId) { + + public static UserWithoutPortfolio with(String userId) { String message = "User with userId " + userId + " doesn't have a portfolio associated."; return new UserWithoutPortfolio(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 831d947..a1a81d2 100644 --- a/src/main/java/io/autoinvestor/infrastructure/event_publishers/EventMessageMapper.java +++ b/src/main/java/io/autoinvestor/infrastructure/event_publishers/EventMessageMapper.java @@ -1,9 +1,5 @@ package io.autoinvestor.infrastructure.event_publishers; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.protobuf.ByteString; -import com.google.pubsub.v1.PubsubMessage; import io.autoinvestor.domain.events.Event; import io.autoinvestor.exceptions.InternalErrorException; @@ -11,6 +7,10 @@ import java.util.HashMap; import java.util.Map; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.protobuf.ByteString; +import com.google.pubsub.v1.PubsubMessage; final class EventMessageMapper { @@ -27,14 +27,12 @@ PubsubMessage toMessage(Event event) { envelope.put("eventId", event.getId().value()); envelope.put("type", event.getType()); envelope.put("aggregateId", event.getAggregateId().value()); - envelope.put("occurredAt", - Instant.ofEpochMilli(event.getOccurredAt().getTime()).toString()); + 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(); + 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/InMemoryEventPublisher.java b/src/main/java/io/autoinvestor/infrastructure/event_publishers/InMemoryEventPublisher.java index edbe2f1..f2e4760 100644 --- a/src/main/java/io/autoinvestor/infrastructure/event_publishers/InMemoryEventPublisher.java +++ b/src/main/java/io/autoinvestor/infrastructure/event_publishers/InMemoryEventPublisher.java @@ -2,8 +2,10 @@ import io.autoinvestor.domain.events.Event; import io.autoinvestor.domain.events.EventPublisher; + import java.util.ArrayList; import java.util.List; + import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; @@ -27,6 +29,9 @@ public void publish(List> events) { public boolean hasPublishedEvent(String type, String aggregateId) { return publishedEvents.stream() - .anyMatch(event -> event.getType().equals(type) && event.getAggregateId().value().equals(aggregateId)); + .anyMatch( + event -> + event.getType().equals(type) + && event.getAggregateId().value().equals(aggregateId)); } } diff --git a/src/main/java/io/autoinvestor/infrastructure/event_publishers/PubsubEventPublisher.java b/src/main/java/io/autoinvestor/infrastructure/event_publishers/PubsubEventPublisher.java index 488a965..8c3a585 100644 --- a/src/main/java/io/autoinvestor/infrastructure/event_publishers/PubsubEventPublisher.java +++ b/src/main/java/io/autoinvestor/infrastructure/event_publishers/PubsubEventPublisher.java @@ -1,18 +1,20 @@ package io.autoinvestor.infrastructure.event_publishers; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.cloud.pubsub.v1.Publisher; -import com.google.pubsub.v1.ProjectTopicName; import io.autoinvestor.domain.events.Event; import io.autoinvestor.domain.events.EventPublisher; import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.concurrent.TimeUnit; + import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; -import java.util.List; -import java.util.concurrent.TimeUnit; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.cloud.pubsub.v1.Publisher; +import com.google.pubsub.v1.ProjectTopicName; @Slf4j @Component @@ -25,8 +27,8 @@ public class PubsubEventPublisher implements EventPublisher { public PubsubEventPublisher( @Value("${GCP_PROJECT}") String projectId, @Value("${PUBSUB_TOPIC}") String topic, - ObjectMapper objectMapper - ) throws Exception { + ObjectMapper objectMapper) + throws Exception { this.mapper = new EventMessageMapper(objectMapper); ProjectTopicName topicName = ProjectTopicName.of(projectId, topic); this.publisher = Publisher.newBuilder(topicName).build(); @@ -45,12 +47,17 @@ public void publish(List> events) { events.stream() .map(mapper::toMessage) - .forEach(msg -> { - publisher.publish(msg).addListener( - () -> log.debug("Published msgId={}", msg.getMessageId()), - Runnable::run - ); - }); + .forEach( + msg -> { + publisher + .publish(msg) + .addListener( + () -> + log.debug( + "Published msgId={}", + msg.getMessageId()), + Runnable::run); + }); } @PreDestroy diff --git a/src/main/java/io/autoinvestor/infrastructure/listeners/PubSubEvent.java b/src/main/java/io/autoinvestor/infrastructure/listeners/PubSubEvent.java index 36ca5e6..79719e0 100644 --- a/src/main/java/io/autoinvestor/infrastructure/listeners/PubSubEvent.java +++ b/src/main/java/io/autoinvestor/infrastructure/listeners/PubSubEvent.java @@ -1,6 +1,5 @@ package io.autoinvestor.infrastructure.listeners; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -8,8 +7,13 @@ import java.util.Map; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + @JsonIgnoreProperties(ignoreUnknown = true) -@Data @Builder @NoArgsConstructor @AllArgsConstructor +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor public class PubSubEvent { private String aggregateId; private String type; diff --git a/src/main/java/io/autoinvestor/infrastructure/listeners/PubSubEventMapper.java b/src/main/java/io/autoinvestor/infrastructure/listeners/PubSubEventMapper.java index 5e46779..7d685b9 100644 --- a/src/main/java/io/autoinvestor/infrastructure/listeners/PubSubEventMapper.java +++ b/src/main/java/io/autoinvestor/infrastructure/listeners/PubSubEventMapper.java @@ -1,11 +1,13 @@ package io.autoinvestor.infrastructure.listeners; -import com.fasterxml.jackson.databind.ObjectMapper; import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; import java.util.Map; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.databind.ObjectMapper; + @Component @RequiredArgsConstructor public class PubSubEventMapper { @@ -14,4 +16,4 @@ public class PubSubEventMapper { public PubSubEvent fromMap(Map raw) { return mapper.convertValue(raw, PubSubEvent.class); } -} \ No newline at end of file +} diff --git a/src/main/java/io/autoinvestor/infrastructure/listeners/PubSubUsersSubscriber.java b/src/main/java/io/autoinvestor/infrastructure/listeners/PubSubUsersSubscriber.java index f6851bd..049d6c0 100644 --- a/src/main/java/io/autoinvestor/infrastructure/listeners/PubSubUsersSubscriber.java +++ b/src/main/java/io/autoinvestor/infrastructure/listeners/PubSubUsersSubscriber.java @@ -1,23 +1,25 @@ package io.autoinvestor.infrastructure.listeners; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.google.api.core.ApiService; -import com.google.cloud.pubsub.v1.AckReplyConsumer; -import com.google.cloud.pubsub.v1.MessageReceiver; -import com.google.pubsub.v1.ProjectSubscriptionName; -import com.google.pubsub.v1.PubsubMessage; import io.autoinvestor.application.WalletCreatedUseCase.WalletCreateCommand; import io.autoinvestor.application.WalletCreatedUseCase.WalletCreatedHandler; import jakarta.annotation.PostConstruct; import jakarta.annotation.PreDestroy; import lombok.extern.slf4j.Slf4j; + +import java.util.Map; + import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Component; -import com.google.cloud.pubsub.v1.Subscriber; -import java.util.Map; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.api.core.ApiService; +import com.google.cloud.pubsub.v1.AckReplyConsumer; +import com.google.cloud.pubsub.v1.MessageReceiver; +import com.google.cloud.pubsub.v1.Subscriber; +import com.google.pubsub.v1.ProjectSubscriptionName; +import com.google.pubsub.v1.PubsubMessage; @Slf4j @Component @@ -30,15 +32,14 @@ public class PubSubUsersSubscriber { private Subscriber subscriber; - public PubSubUsersSubscriber(WalletCreatedHandler walletCreatedHandler, - PubSubEventMapper mapper, - @Value("${GCP_PROJECT}") String projectId, - @Value("${PUBSUB_SUBSCRIPTION_USERS}") String subscriptionId) - { + public PubSubUsersSubscriber( + WalletCreatedHandler walletCreatedHandler, + PubSubEventMapper mapper, + @Value("${GCP_PROJECT}") String projectId, + @Value("${PUBSUB_SUBSCRIPTION_USERS}") String subscriptionId) { this.commandHandler = walletCreatedHandler; this.eventMapper = mapper; this.subscriptionName = ProjectSubscriptionName.of(projectId, subscriptionId); - } @PostConstruct @@ -48,11 +49,18 @@ public void listen() { MessageReceiver receiver = this::processMessage; this.subscriber = Subscriber.newBuilder(subscriptionName, receiver).build(); - this.subscriber.addListener(new ApiService.Listener() { - @Override public void failed(ApiService.State from, Throwable failure) { - log.error("Subscriber failed from state {}: {}", from, failure.toString(), failure); // ERROR - } - }, Runnable::run); + this.subscriber.addListener( + new ApiService.Listener() { + @Override + public void failed(ApiService.State from, Throwable failure) { + log.error( + "Subscriber failed from state {}: {}", + from, + failure.toString(), + failure); // ERROR + } + }, + Runnable::run); this.subscriber.startAsync().awaitRunning(); log.info("Subscriber running"); } @@ -70,20 +78,22 @@ private void processMessage(PubsubMessage message, AckReplyConsumer consumer) { log.debug("Received message msgId={} size={}B", msgId, message.getData().size()); try { - Map raw = objectMapper.readValue(message.getData().toByteArray(), new TypeReference<>() {}); + Map raw = + objectMapper.readValue( + message.getData().toByteArray(), new TypeReference<>() {}); PubSubEvent event = eventMapper.fromMap(raw); log.info("Processing event type={} msgId={}", event.getType(), msgId); if ("USER_CREATED".equals(event.getType())) { if (event.getAggregateId() == null) { - log.warn("Malformed event: Skipping USER_CREATED event with missing aggregateId msgId={}", msgId); + log.warn( + "Malformed event: Skipping USER_CREATED event with missing aggregateId msgId={}", + msgId); consumer.ack(); return; } - WalletCreateCommand command = new WalletCreateCommand( - event.getAggregateId() - ); + WalletCreateCommand command = new WalletCreateCommand(event.getAggregateId()); this.commandHandler.handle(command); log.info("User registered for userId={} msgId={}", command.userId(), msgId); } else { diff --git a/src/main/java/io/autoinvestor/infrastructure/read_models/DocumentMapper.java b/src/main/java/io/autoinvestor/infrastructure/read_models/DocumentMapper.java index 55283b3..f263d64 100644 --- a/src/main/java/io/autoinvestor/infrastructure/read_models/DocumentMapper.java +++ b/src/main/java/io/autoinvestor/infrastructure/read_models/DocumentMapper.java @@ -2,29 +2,22 @@ import io.autoinvestor.application.HoldingsReadModelDTO; import io.autoinvestor.application.UsersWalletReadModelDTO; + import org.springframework.stereotype.Component; @Component public class DocumentMapper { public MongoUsersWalletReadModelDocument toDocument(UsersWalletReadModelDTO dto) { - return new MongoUsersWalletReadModelDocument( - dto.userId(), - dto.walletId() - ); + return new MongoUsersWalletReadModelDocument(dto.userId(), dto.walletId()); } public MongoHoldingsReadModelDocument toDocument(HoldingsReadModelDTO dto) { return new MongoHoldingsReadModelDocument( - dto.userId(), dto.assetId(), dto.amount(), dto. boughtPrice() - ); + dto.userId(), dto.assetId(), dto.amount(), dto.boughtPrice()); } public HoldingsReadModelDTO toDTO(MongoHoldingsReadModelDocument document) { return new HoldingsReadModelDTO( - document.userId(), - document.assetId(), - document.amount(), - document.boughtPrice() - ); + document.userId(), document.assetId(), document.amount(), document.boughtPrice()); } } diff --git a/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryHoldingsReadModel.java b/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryHoldingsReadModel.java index 7f871f8..cfa943b 100644 --- a/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryHoldingsReadModel.java +++ b/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryHoldingsReadModel.java @@ -2,23 +2,20 @@ import io.autoinvestor.application.HoldingsReadModel; import io.autoinvestor.application.HoldingsReadModelDTO; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; import java.util.List; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + @Repository @Profile("local") public class InMemoryHoldingsReadModel implements HoldingsReadModel { @Override - public void add(HoldingsReadModelDTO dto) { - - } + public void add(HoldingsReadModelDTO dto) {} @Override - public void update(HoldingsReadModelDTO dto) { - - } + public void update(HoldingsReadModelDTO dto) {} @Override public boolean delete(String userId, String assetId) { diff --git a/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryUsersWalletReadModel.java b/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryUsersWalletReadModel.java index db45fdd..3d34d44 100644 --- a/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryUsersWalletReadModel.java +++ b/src/main/java/io/autoinvestor/infrastructure/read_models/InMemoryUsersWalletReadModel.java @@ -2,6 +2,7 @@ import io.autoinvestor.application.UsersWalletReadModel; import io.autoinvestor.application.UsersWalletReadModelDTO; + import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Repository; @@ -9,9 +10,7 @@ @Profile("local") public class InMemoryUsersWalletReadModel implements UsersWalletReadModel { @Override - public void add(UsersWalletReadModelDTO dto) { - - } + public void add(UsersWalletReadModelDTO dto) {} @Override public String getWalletId(String userId) { diff --git a/src/main/java/io/autoinvestor/infrastructure/read_models/MongoHoldingsReadModel.java b/src/main/java/io/autoinvestor/infrastructure/read_models/MongoHoldingsReadModel.java index e6e8c00..16e8232 100644 --- a/src/main/java/io/autoinvestor/infrastructure/read_models/MongoHoldingsReadModel.java +++ b/src/main/java/io/autoinvestor/infrastructure/read_models/MongoHoldingsReadModel.java @@ -1,8 +1,10 @@ package io.autoinvestor.infrastructure.read_models; -import com.mongodb.client.result.DeleteResult; import io.autoinvestor.application.HoldingsReadModel; import io.autoinvestor.application.HoldingsReadModelDTO; + +import java.util.List; + import org.springframework.context.annotation.Profile; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.query.Criteria; @@ -10,7 +12,7 @@ import org.springframework.data.mongodb.core.query.Update; import org.springframework.stereotype.Repository; -import java.util.List; +import com.mongodb.client.result.DeleteResult; @Repository @Profile("prod") @@ -30,20 +32,19 @@ public void add(HoldingsReadModelDTO dto) { @Override public void update(HoldingsReadModelDTO dto) { - Query query = new Query(Criteria.where("userId").is(dto.userId()) - .and("assetId").is(dto.assetId())); + Query query = + new Query( + Criteria.where("userId").is(dto.userId()).and("assetId").is(dto.assetId())); - Update update = new Update() - .set("amount", dto.amount()) - .set("boughtPrice", dto.boughtPrice()); + Update update = + new Update().set("amount", dto.amount()).set("boughtPrice", dto.boughtPrice()); this.template.updateFirst(query, update, MongoHoldingsReadModelDocument.class); } @Override public boolean delete(String userId, String assetId) { - Query query = new Query(Criteria.where("userId").is(userId) - .and("assetId").is(assetId)); + Query query = new Query(Criteria.where("userId").is(userId).and("assetId").is(assetId)); DeleteResult result = this.template.remove(query, MongoHoldingsReadModelDocument.class); return result.getDeletedCount() > 0; } @@ -51,15 +52,14 @@ public boolean delete(String userId, String assetId) { @Override public List getHoldings(String userId) { Query query = new Query(Criteria.where("userId").is(userId)); - return this.template.find(query, MongoHoldingsReadModelDocument.class) - .stream().map(mapper::toDTO).toList(); + return this.template.find(query, MongoHoldingsReadModelDocument.class).stream() + .map(mapper::toDTO) + .toList(); } @Override public boolean assetAlreadyExists(String userId, String assetId) { - Query query = new Query(Criteria.where("userId").is(userId) - .and("assetId").is(assetId)); + Query query = new Query(Criteria.where("userId").is(userId).and("assetId").is(assetId)); return this.template.exists(query, MongoHoldingsReadModelDocument.class); } - } diff --git a/src/main/java/io/autoinvestor/infrastructure/read_models/MongoHoldingsReadModelDocument.java b/src/main/java/io/autoinvestor/infrastructure/read_models/MongoHoldingsReadModelDocument.java index b52b798..93f9750 100644 --- a/src/main/java/io/autoinvestor/infrastructure/read_models/MongoHoldingsReadModelDocument.java +++ b/src/main/java/io/autoinvestor/infrastructure/read_models/MongoHoldingsReadModelDocument.java @@ -4,8 +4,4 @@ @Document(collection = "holdings") public record MongoHoldingsReadModelDocument( - String userId, - String assetId, - Integer amount, - Integer boughtPrice -) {} + String userId, String assetId, Integer amount, Integer boughtPrice) {} diff --git a/src/main/java/io/autoinvestor/infrastructure/read_models/MongoUsersWalletReadModel.java b/src/main/java/io/autoinvestor/infrastructure/read_models/MongoUsersWalletReadModel.java index 9cd61bb..9e174bc 100644 --- a/src/main/java/io/autoinvestor/infrastructure/read_models/MongoUsersWalletReadModel.java +++ b/src/main/java/io/autoinvestor/infrastructure/read_models/MongoUsersWalletReadModel.java @@ -2,6 +2,7 @@ import io.autoinvestor.application.UsersWalletReadModel; import io.autoinvestor.application.UsersWalletReadModelDTO; + import org.springframework.context.annotation.Profile; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Repository; @@ -24,7 +25,8 @@ public void add(UsersWalletReadModelDTO dto) { @Override public String getWalletId(String userId) { - MongoUsersWalletReadModelDocument doc = template.findById(userId, MongoUsersWalletReadModelDocument.class); + MongoUsersWalletReadModelDocument doc = + template.findById(userId, MongoUsersWalletReadModelDocument.class); if (doc != null) { return doc.walletId(); diff --git a/src/main/java/io/autoinvestor/infrastructure/read_models/MongoUsersWalletReadModelDocument.java b/src/main/java/io/autoinvestor/infrastructure/read_models/MongoUsersWalletReadModelDocument.java index 87c7912..90bb3c2 100644 --- a/src/main/java/io/autoinvestor/infrastructure/read_models/MongoUsersWalletReadModelDocument.java +++ b/src/main/java/io/autoinvestor/infrastructure/read_models/MongoUsersWalletReadModelDocument.java @@ -4,8 +4,4 @@ import org.springframework.data.mongodb.core.mapping.Document; @Document(collection = "users") -public record MongoUsersWalletReadModelDocument( - @Id String userId, - String walletId -) { -} +public record MongoUsersWalletReadModelDocument(@Id String userId, String walletId) {} diff --git a/src/main/java/io/autoinvestor/infrastructure/repositories/EventDocument.java b/src/main/java/io/autoinvestor/infrastructure/repositories/EventDocument.java index 8db9923..dbc737a 100644 --- a/src/main/java/io/autoinvestor/infrastructure/repositories/EventDocument.java +++ b/src/main/java/io/autoinvestor/infrastructure/repositories/EventDocument.java @@ -2,49 +2,45 @@ import lombok.Getter; import lombok.Setter; -import org.springframework.data.annotation.Id; -import org.springframework.data.mongodb.core.mapping.Document; -import org.springframework.data.mongodb.core.mapping.Field; import java.util.Date; import java.util.Map; +import org.springframework.data.annotation.Id; +import org.springframework.data.mongodb.core.mapping.Document; +import org.springframework.data.mongodb.core.mapping.Field; + @Getter @Setter @Document(collection = "events") public class EventDocument { - @Id - private String id; + @Id private String id; - @Field - private String aggregateId; + @Field private String aggregateId; - @Field - private String type; + @Field private String type; - @Field - private Map payload; + @Field private Map payload; - @Field - private Date occurredAt; + @Field private Date occurredAt; - @Field - private int version; + @Field private int version; - public EventDocument() { } + public EventDocument() {} - public EventDocument(String id, - String aggregateId, - String type, - Map payload, - Date occurredAt, - int version) { - this.id = id; + public EventDocument( + String id, + String aggregateId, + String type, + Map payload, + Date occurredAt, + int version) { + this.id = id; this.aggregateId = aggregateId; - this.type = type; - this.payload = payload; - this.occurredAt = occurredAt; - this.version = version; + this.type = type; + this.payload = payload; + this.occurredAt = occurredAt; + this.version = version; } } diff --git a/src/main/java/io/autoinvestor/infrastructure/repositories/EventMapper.java b/src/main/java/io/autoinvestor/infrastructure/repositories/EventMapper.java index ed194ca..031c783 100644 --- a/src/main/java/io/autoinvestor/infrastructure/repositories/EventMapper.java +++ b/src/main/java/io/autoinvestor/infrastructure/repositories/EventMapper.java @@ -1,14 +1,16 @@ package io.autoinvestor.infrastructure.repositories; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; import io.autoinvestor.domain.events.*; import io.autoinvestor.domain.model.*; -import org.springframework.stereotype.Component; import java.util.Date; import java.util.Map; +import org.springframework.stereotype.Component; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; + @Component public class EventMapper { @@ -24,8 +26,7 @@ public

EventDocument toDocument(Event

evt) { evt.getType(), payloadMap, evt.getOccurredAt(), - evt.getVersion() - ); + evt.getVersion()); } public Event toDomain(EventDocument doc) { @@ -59,8 +60,7 @@ public Event toDomain(EventDocument doc) { json.convertValue(doc.getPayload(), HoldingWasDeletedEventPayload.class); return HoldingWasDeletedEvent.hydrate(id, aggId, payload, occurred, version); } - default -> throw new IllegalArgumentException("Unknown event type: " + doc.getType() - ); + default -> throw new IllegalArgumentException("Unknown event type: " + doc.getType()); } } } diff --git a/src/main/java/io/autoinvestor/infrastructure/repositories/InMemoryEventStoreRepository.java b/src/main/java/io/autoinvestor/infrastructure/repositories/InMemoryEventStoreRepository.java index 345cb8b..cab739a 100644 --- a/src/main/java/io/autoinvestor/infrastructure/repositories/InMemoryEventStoreRepository.java +++ b/src/main/java/io/autoinvestor/infrastructure/repositories/InMemoryEventStoreRepository.java @@ -4,8 +4,6 @@ import io.autoinvestor.domain.events.WalletEventStoreRepository; import io.autoinvestor.domain.model.Wallet; import io.autoinvestor.domain.model.WalletId; -import org.springframework.context.annotation.Profile; -import org.springframework.stereotype.Repository; import java.util.Comparator; import java.util.List; @@ -13,6 +11,9 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; + @Repository @Profile("local") public class InMemoryEventStoreRepository implements WalletEventStoreRepository { @@ -26,10 +27,11 @@ public void save(Wallet wallet) { @Override public Optional get(WalletId walletId) { - List> events = eventStore.stream() - .filter(e -> e.getAggregateId().value().equals(walletId.value())) - .sorted(Comparator.comparingLong(Event::getVersion)) - .collect(Collectors.toList()); + List> events = + eventStore.stream() + .filter(e -> e.getAggregateId().value().equals(walletId.value())) + .sorted(Comparator.comparingLong(Event::getVersion)) + .collect(Collectors.toList()); if (events.isEmpty()) { return Optional.empty(); diff --git a/src/main/java/io/autoinvestor/infrastructure/repositories/MongoEventStoreRepository.java b/src/main/java/io/autoinvestor/infrastructure/repositories/MongoEventStoreRepository.java index df884be..dff042c 100644 --- a/src/main/java/io/autoinvestor/infrastructure/repositories/MongoEventStoreRepository.java +++ b/src/main/java/io/autoinvestor/infrastructure/repositories/MongoEventStoreRepository.java @@ -4,6 +4,11 @@ import io.autoinvestor.domain.events.WalletEventStoreRepository; import io.autoinvestor.domain.model.Wallet; import io.autoinvestor.domain.model.WalletId; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + import org.springframework.context.annotation.Profile; import org.springframework.data.domain.Sort; import org.springframework.data.mongodb.core.MongoTemplate; @@ -11,10 +16,6 @@ import org.springframework.data.mongodb.core.query.Query; import org.springframework.stereotype.Repository; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - @Repository @Profile("prod") public class MongoEventStoreRepository implements WalletEventStoreRepository { @@ -30,20 +31,18 @@ public MongoEventStoreRepository(MongoTemplate template, EventMapper mapper) { @Override public void save(Wallet wallet) { - List docs = wallet.getUncommittedEvents() - .stream() - .map(mapper::toDocument) - .collect(Collectors.toList()); + List docs = + wallet.getUncommittedEvents().stream() + .map(mapper::toDocument) + .collect(Collectors.toList()); template.insertAll(docs); } @Override public Optional get(WalletId walletId) { - Query q = Query.query( - Criteria.where("aggregateId") - .is(walletId.value()) - ) - .with(Sort.by("version")); + Query q = + Query.query(Criteria.where("aggregateId").is(walletId.value())) + .with(Sort.by("version")); List docs = template.find(q, EventDocument.class, COLLECTION); @@ -51,9 +50,7 @@ public Optional get(WalletId walletId) { return Optional.empty(); } - List> events = docs.stream() - .map(mapper::toDomain) - .collect(Collectors.toList()); + List> events = docs.stream().map(mapper::toDomain).collect(Collectors.toList()); if (events.isEmpty()) { return Optional.empty(); diff --git a/src/main/java/io/autoinvestor/ui/GetHoldingResponse.java b/src/main/java/io/autoinvestor/ui/GetHoldingResponse.java index e8388fe..d150804 100644 --- a/src/main/java/io/autoinvestor/ui/GetHoldingResponse.java +++ b/src/main/java/io/autoinvestor/ui/GetHoldingResponse.java @@ -1,8 +1,3 @@ package io.autoinvestor.ui; -public record GetHoldingResponse( - String assetId, - Integer amount, - Integer boughtPrice -) { -} +public record GetHoldingResponse(String assetId, Integer amount, Integer boughtPrice) {} diff --git a/src/main/java/io/autoinvestor/ui/GetHoldingResponseDocumentMapper.java b/src/main/java/io/autoinvestor/ui/GetHoldingResponseDocumentMapper.java index facd335..df831f2 100644 --- a/src/main/java/io/autoinvestor/ui/GetHoldingResponseDocumentMapper.java +++ b/src/main/java/io/autoinvestor/ui/GetHoldingResponseDocumentMapper.java @@ -1,16 +1,14 @@ package io.autoinvestor.ui; import io.autoinvestor.application.HoldingsReadModelDTO; + import org.springframework.stereotype.Service; @Service public class GetHoldingResponseDocumentMapper { -public GetHoldingResponse map(HoldingsReadModelDTO document) { - return new GetHoldingResponse( - document.assetId(), - document.amount(), - document.boughtPrice() - ); + public GetHoldingResponse map(HoldingsReadModelDTO document) { + return new GetHoldingResponse( + document.assetId(), document.amount(), document.boughtPrice()); } } diff --git a/src/main/java/io/autoinvestor/ui/HoldingRequestDTO.java b/src/main/java/io/autoinvestor/ui/HoldingRequestDTO.java index 58ab875..60f1ca1 100644 --- a/src/main/java/io/autoinvestor/ui/HoldingRequestDTO.java +++ b/src/main/java/io/autoinvestor/ui/HoldingRequestDTO.java @@ -1,9 +1,3 @@ package io.autoinvestor.ui; -import jakarta.validation.constraints.NotNull; - -public record HoldingRequestDTO( - String assetId, - Integer amount, - Integer boughtPrice - ) {} +public record HoldingRequestDTO(String assetId, Integer amount, Integer boughtPrice) {} diff --git a/src/main/java/io/autoinvestor/ui/PortfolioController.java b/src/main/java/io/autoinvestor/ui/PortfolioController.java index c6c2a22..cd51fc4 100644 --- a/src/main/java/io/autoinvestor/ui/PortfolioController.java +++ b/src/main/java/io/autoinvestor/ui/PortfolioController.java @@ -11,14 +11,14 @@ import io.autoinvestor.application.UptdateHoldingUseCase.UpdateHoldingCommandHandler; import io.autoinvestor.application.WalletCreatedUseCase.WalletCreateCommand; import io.autoinvestor.application.WalletCreatedUseCase.WalletCreatedHandler; -import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; + +import java.util.List; + import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; -import java.util.List; - @RestController @RequestMapping("/portfolio/holdings") @RequiredArgsConstructor @@ -30,61 +30,53 @@ public class PortfolioController { private final GetHoldingResponseDocumentMapper mapperGetHoldingResponse; private final UpdateHoldingCommandHandler updateHoldingCommandHandler; private final HoldingDeleteCommandHandler holdingDeleteCommandHandler; + @PostMapping public ResponseEntity addHolding( @RequestHeader(value = "X-User-Id", required = true) String userId, @RequestBody List holdingRequestDTO) { for (HoldingRequestDTO holding : holdingRequestDTO) { - newHoldingCommandHandler.handle(new NewHoldingCommand( - holding.assetId(), - userId, - holding.amount(), - holding.boughtPrice() - )); + newHoldingCommandHandler.handle( + new NewHoldingCommand( + holding.assetId(), userId, holding.amount(), holding.boughtPrice())); } return ResponseEntity.status(HttpStatus.CREATED).build(); } @GetMapping public ResponseEntity> getHoldings( - @RequestHeader(value = "X-User-Id", required = true) String userId - ) { - List documents = getHoldingsQueryHandler.handle(new GetHoldingsQuery(userId)); + @RequestHeader(value = "X-User-Id", required = true) String userId) { + List documents = + getHoldingsQueryHandler.handle(new GetHoldingsQuery(userId)); return ResponseEntity.ok(documents.stream().map(mapperGetHoldingResponse::map).toList()); } + @PutMapping - public ResponseEntity putHolding ( - @RequestHeader (value = "X-User-Id", required = true) String userId, - @RequestBody List holdingRequestDTO - ) { + public ResponseEntity putHolding( + @RequestHeader(value = "X-User-Id", required = true) String userId, + @RequestBody List holdingRequestDTO) { for (HoldingRequestDTO holding : holdingRequestDTO) { - updateHoldingCommandHandler.handle(new UpdateHoldingCommand( - userId, - holding.assetId(), - holding.amount(), - holding.boughtPrice() - )); + updateHoldingCommandHandler.handle( + new UpdateHoldingCommand( + userId, holding.assetId(), holding.amount(), holding.boughtPrice())); } return ResponseEntity.status(HttpStatus.CREATED).build(); } @DeleteMapping - public ResponseEntity deleteHolding ( - @RequestHeader (value = "X-User-Id", required = true) String userId, - @RequestParam(value = "assetId", required = true) String assetId - ) { + public ResponseEntity deleteHolding( + @RequestHeader(value = "X-User-Id", required = true) String userId, + @RequestParam(value = "assetId", required = true) String assetId) { holdingDeleteCommandHandler.handle(new HoldingDeleteCommand(userId, assetId)); return ResponseEntity.status(HttpStatus.OK).build(); } @PostMapping("/user") - public ResponseEntity simulateIncomingUserCreatedMessage ( - @RequestBody SimulateUserIncomingMessageDTO dto - ) { + public ResponseEntity simulateIncomingUserCreatedMessage( + @RequestBody SimulateUserIncomingMessageDTO dto) { walletCreatedHandler.handle(new WalletCreateCommand(dto.payload().userId())); return ResponseEntity.status(HttpStatus.CREATED).build(); - } } diff --git a/src/main/java/io/autoinvestor/ui/SimulateUserIncomingMessageDTO.java b/src/main/java/io/autoinvestor/ui/SimulateUserIncomingMessageDTO.java index 151c5b7..cc227ce 100644 --- a/src/main/java/io/autoinvestor/ui/SimulateUserIncomingMessageDTO.java +++ b/src/main/java/io/autoinvestor/ui/SimulateUserIncomingMessageDTO.java @@ -6,9 +6,6 @@ public record SimulateUserIncomingMessageDTO( String aggregateId, String version, String type, - Payload payload -) { - public record Payload ( - String userId - ) {} + Payload payload) { + public record Payload(String userId) {} } diff --git a/src/main/java/io/autoinvestor/ui/error_handling/ErrorResponseBuilder.java b/src/main/java/io/autoinvestor/ui/error_handling/ErrorResponseBuilder.java index 1457ef4..1740d21 100644 --- a/src/main/java/io/autoinvestor/ui/error_handling/ErrorResponseBuilder.java +++ b/src/main/java/io/autoinvestor/ui/error_handling/ErrorResponseBuilder.java @@ -8,8 +8,7 @@ public class ErrorResponseBuilder { 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(); diff --git a/src/main/java/io/autoinvestor/ui/error_handling/GlobalExceptionHandler.java b/src/main/java/io/autoinvestor/ui/error_handling/GlobalExceptionHandler.java index ceed4d9..2c2bb09 100644 --- a/src/main/java/io/autoinvestor/ui/error_handling/GlobalExceptionHandler.java +++ b/src/main/java/io/autoinvestor/ui/error_handling/GlobalExceptionHandler.java @@ -3,6 +3,7 @@ import io.autoinvestor.exceptions.AssetAlreadyExists; import io.autoinvestor.exceptions.UserWithoutPortfolio; import io.autoinvestor.ui.PortfolioController; + import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MissingRequestHeaderException; import org.springframework.web.bind.annotation.ExceptionHandler; @@ -12,16 +13,18 @@ public class GlobalExceptionHandler { @ExceptionHandler(MissingRequestHeaderException.class) - public ResponseEntity handleMissingRequestHeaderException(MissingRequestHeaderException ex) { + public ResponseEntity handleMissingRequestHeaderException( + MissingRequestHeaderException ex) { return ErrorResponse.builder().status(400).message(ex.getMessage()).build(); } + @ExceptionHandler(UserWithoutPortfolio.class) public ResponseEntity handleUserWithoutPortfolio(UserWithoutPortfolio ex) { return ErrorResponse.builder().status(400).message(ex.getMessage()).build(); } + @ExceptionHandler(AssetAlreadyExists.class) - public ResponseEntity handleAssetALreadyExists(AssetAlreadyExists ex) { + public ResponseEntity handleAssetALreadyExists(AssetAlreadyExists ex) { return ErrorResponse.builder().status(400).message(ex.getMessage()).build(); } - }