Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/post-commit-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ jobs:
build_test_release:
runs-on: ubuntu-22.04
env:
SPRING_PROFILES_ACTIVE: prod
BP_SPRING_PROFILES_ACTIVE: prod
SPRING_PROFILES_ACTIVE: local
outputs:
new-version: ${{ (steps.version_increment.outputs.bump != 'none' && steps.calculate_version.outputs.new_version) || '' }}
steps:
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/pull-request-pipeline.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ on:
jobs:
build-and-test-pipeline:
runs-on: ubuntu-22.04
env:
SPRING_PROFILES_ACTIVE: local
steps:
- name: Check out the repository
uses: actions/checkout@v4
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,4 +46,8 @@ public List<AlertDTO> get(String userId) {
.filter(alert -> alert.userId().equals(userId))
.collect(Collectors.toList());
}

public void clear() {
alerts.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import io.autoinvestor.application.AlertDTO;
import io.autoinvestor.application.AlertsReadModelRepository;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

Expand All @@ -13,6 +14,7 @@

@Repository
@Profile("prod")
@Slf4j
public class MongoAlertsReadModelRepository implements AlertsReadModelRepository {

private static final String COLLECTION = "alerts";
Expand All @@ -23,11 +25,23 @@ public class MongoAlertsReadModelRepository implements AlertsReadModelRepository
public MongoAlertsReadModelRepository(MongoTemplate template, DecisionMapper mapper) {
this.template = template;
this.mapper = mapper;
log.info("MongoAlertsReadModelRepository initialized.");
}

@Override
public void save(AlertDTO alertDTO) {
template.save(mapper.toDocument(alertDTO), COLLECTION);
try {
template.save(mapper.toDocument(alertDTO), COLLECTION);
log.info("Saved AlertDTO for userId={}", alertDTO.userId());
} catch (Exception ex) {
log.error(
"Failed to save AlertDTO[userId={}, assetId={}]: {}",
alertDTO.userId(),
alertDTO.assetId(),
ex.getMessage(),
ex);
throw ex;
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ public Optional<InboxId> getInboxId(UserId userId) {
String raw = inbox.get(userId.value());
return raw != null ? Optional.of(InboxId.from(raw)) : Optional.empty();
}

public void clear() {
inbox.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import io.autoinvestor.application.InboxReadModelRepository;
import io.autoinvestor.domain.model.InboxId;
import io.autoinvestor.domain.model.UserId;
import lombok.extern.slf4j.Slf4j;

import java.util.Optional;

Expand All @@ -12,29 +13,55 @@

@Repository
@Profile("prod")
@Slf4j
public class MongoInboxReadModelRepository implements InboxReadModelRepository {

private final MongoTemplate template;

public MongoInboxReadModelRepository(MongoTemplate template) {
this.template = template;
log.info("MongoInboxReadModelRepository initialized.");
}

@Override
public void save(UserId userId, InboxId inboxId) {
String userIdStr = userId.value();
String inboxIdStr = inboxId.value();
DecisionDocument doc = new DecisionDocument(userIdStr, inboxIdStr);
template.save(doc);
DecisionDocument doc = new DecisionDocument(userId.value(), inboxId.value());
try {
template.save(doc);
log.info(
"Saved DecisionDocument[userId={} -> inboxId={}]",
userId.value(),
inboxId.value());
} catch (Exception ex) {
log.error(
"Failed to save DecisionDocument[userId={}]: {}",
userId.value(),
ex.getMessage(),
ex);
throw ex;
}
}

@Override
public Optional<InboxId> getInboxId(UserId userId) {
String userIdStr = userId.value();
DecisionDocument doc = template.findById(userIdStr, DecisionDocument.class);
DecisionDocument doc;
try {
doc = template.findById(userIdStr, DecisionDocument.class);
} catch (Exception ex) {
log.error(
"Error fetching DecisionDocument for userId={}: {}",
userIdStr,
ex.getMessage(),
ex);
throw ex;
}

if (doc == null) {
log.warn("No DecisionDocument found for userId={}", userIdStr);
return Optional.empty();
}

return Optional.of(InboxId.from(doc.getInboxId()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.autoinvestor.domain.events.EventStoreRepository;
import io.autoinvestor.domain.model.Inbox;
import io.autoinvestor.domain.model.InboxId;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Optional;
Expand All @@ -18,6 +19,7 @@

@Repository
@Profile("prod")
@Slf4j
public class MongoEventStoreRepository implements EventStoreRepository {
private static final String COLLECTION = "events";

Expand All @@ -27,32 +29,64 @@ public class MongoEventStoreRepository implements EventStoreRepository {
public MongoEventStoreRepository(MongoTemplate template, EventMapper mapper) {
this.template = template;
this.mapper = mapper;
log.info("MongoEventStoreRepository initialized.");
}

@Override
public void save(Inbox inbox) {
List<EventDocument> docs =
inbox.getUncommittedEvents().stream()
.map(mapper::toDocument)
.collect(Collectors.toList());
template.insertAll(docs);
List<Event<?>> uncommitted = inbox.getUncommittedEvents();
if (uncommitted.isEmpty()) {
log.debug(
"No uncommitted events to save for inboxId={}",
inbox.getState().getInboxId().value());
return;
}

try {
List<EventDocument> docs = uncommitted.stream().map(mapper::toDocument).toList();
template.insertAll(docs);
log.info("Inserted {} event(s) into '{}'", docs.size(), COLLECTION);
} catch (Exception ex) {
log.error(
"Failed to insert events for inboxId={}: {}",
inbox.getState().getInboxId().value(),
ex.getMessage(),
ex);
throw ex;
}
}

@Override
public Optional<Inbox> get(InboxId inboxId) {
Query q =
Query.query(Criteria.where("aggregateId").is(inboxId.value()))
.with(Sort.by("version"));
log.debug("Querying '{}' for aggregateId={}", COLLECTION, inboxId.value());

List<EventDocument> docs = template.find(q, EventDocument.class, COLLECTION);
List<EventDocument> docs;
try {
docs = template.find(q, EventDocument.class, COLLECTION);
} catch (Exception ex) {
log.error(
"Error querying events for inboxId={}: {}",
inboxId.value(),
ex.getMessage(),
ex);
throw ex;
}

if (docs.isEmpty()) {
log.warn("No EventDocument found for inboxId={}", inboxId.value());
return Optional.empty();
}

List<Event<?>> events = docs.stream().map(mapper::toDomain).collect(Collectors.toList());

if (events.isEmpty()) {
log.warn(
"Mapped {} EventDocument(s) but got 0 domain events for inboxId={}",
docs.size(),
inboxId.value());
return Optional.empty();
}

Expand All @@ -61,8 +95,21 @@ public Optional<Inbox> get(InboxId inboxId) {

@Override
public boolean exists(InboxId inboxId) {
Query q = Query.query(Criteria.where("aggregateId").is(inboxId.value()));

return template.exists(q, EventDocument.class, COLLECTION);
try {
boolean exists =
template.exists(
Query.query(Criteria.where("aggregateId").is(inboxId.value())),
EventDocument.class,
COLLECTION);
log.debug("exists(inboxId={}) = {}", inboxId.value(), exists);
return exists;
} catch (Exception ex) {
log.error(
"Error checking existence of events for inboxId={}: {}",
inboxId.value(),
ex.getMessage(),
ex);
throw ex;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@ public boolean existsPortfolioAsset(String userId, String assetId) {
.map(set -> set.contains(assetId))
.orElse(false);
}

public void clear() {
users.clear();
portfolio.clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import io.autoinvestor.domain.model.UserId;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.stream.Collectors;
Expand All @@ -20,6 +21,7 @@

@Repository
@Profile("prod")
@Slf4j
public class MongoPortfolioRepository implements PortfolioRepository {
public static final String PORTFOLIO_COLLECTION = "portfolio";
public static final String USERS_COLLECTION = "users";
Expand All @@ -28,6 +30,7 @@ public class MongoPortfolioRepository implements PortfolioRepository {

public MongoPortfolioRepository(MongoTemplate template) {
this.template = template;
log.info("MongoPortfolioRepository initialized.");
}

@Override
Expand All @@ -43,9 +46,22 @@ public List<UserId> getUsersIdByAssetAndRiskLevel(String assetId, int riskLevel)
AggregationResults<IdProjection> results =
template.aggregate(agg, PORTFOLIO_COLLECTION, IdProjection.class);

return results.getMappedResults().stream()
.map(p -> UserId.from(p.getUserId()))
.collect(Collectors.toList());
List<IdProjection> raw = results.getMappedResults();

if (raw.isEmpty()) {
log.warn("No users found for assetId='{}' with riskLevel={}", assetId, riskLevel);
return List.of();
}

List<UserId> userIds =
raw.stream().map(p -> UserId.from(p.getUserId())).collect(Collectors.toList());

log.info(
"Found {} user(s) for assetId='{}', riskLevel={}",
userIds.size(),
assetId,
riskLevel);
return userIds;
}

@Setter
Expand All @@ -56,12 +72,34 @@ private static class IdProjection {

@Override
public void addUser(String userId, int riskLevel) {
template.save(new UserDocument(null, userId, riskLevel));
try {
template.save(new UserDocument(null, userId, riskLevel));
log.info("Saved UserDocument[userId={}]", userId);
} catch (Exception ex) {
log.error(
"Failed to save UserDocument[userId={}, riskLevel={}]: {}",
userId,
riskLevel,
ex.getMessage(),
ex);
throw ex;
}
}

@Override
public void addPortfolioAsset(String userId, String assetId) {
template.save(new PortfolioDocument(null, userId, assetId));
try {
template.save(new PortfolioDocument(null, userId, assetId));
log.info("Saved PortfolioDocument[userId={}, assetId={}]", userId, assetId);
} catch (Exception ex) {
log.error(
"Failed to save PortfolioDocument[userId={}, assetId={}]: {}",
userId,
assetId,
ex.getMessage(),
ex);
throw ex;
}
}

@Override
Expand Down
10 changes: 4 additions & 6 deletions src/main/resources/application-local.properties
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
spring.autoconfigure.exclude=\
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration

spring.autoconfigure.exclude+=,\
com.google.cloud.spring.autoconfigure.core.GcpContextAutoConfiguration,\
com.google.cloud.spring.autoconfigure.pubsub.GcpPubSubAutoConfiguration
org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\
org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\
com.google.cloud.spring.autoconfigure.core.GcpContextAutoConfiguration,\
com.google.cloud.spring.autoconfigure.pubsub.GcpPubSubAutoConfiguration
2 changes: 1 addition & 1 deletion src/main/resources/application.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
spring.application.name=alerts
spring.profiles.active=local
spring.profiles.active=prod
Loading
Loading