Skip to content

Commit 753af2f

Browse files
authored
Hotfix/resolve analyze job per user (#133)
1 parent 7339c02 commit 753af2f

File tree

21 files changed

+195
-44
lines changed

21 files changed

+195
-44
lines changed

domain/src/main/java/com/jongsoft/finance/domain/insight/AnalyzeJob.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package com.jongsoft.finance.domain.insight;
22

33
import com.jongsoft.finance.core.exception.StatusException;
4+
import com.jongsoft.finance.domain.user.UserIdentifier;
45
import com.jongsoft.finance.messaging.commands.insight.CompleteAnalyzeJob;
56
import com.jongsoft.finance.messaging.commands.insight.CreateAnalyzeJob;
7+
import com.jongsoft.finance.messaging.commands.insight.FailAnalyzeJob;
68
import java.time.YearMonth;
79
import java.util.UUID;
810
import lombok.AllArgsConstructor;
@@ -16,13 +18,15 @@ public class AnalyzeJob {
1618

1719
private final String jobId;
1820
private final YearMonth month;
21+
private final UserIdentifier user;
1922
private boolean completed;
2023

21-
public AnalyzeJob(YearMonth month) {
24+
public AnalyzeJob(UserIdentifier user, YearMonth month) {
2225
this.jobId = UUID.randomUUID().toString();
2326
this.month = month;
27+
this.user = user;
2428

25-
CreateAnalyzeJob.createAnalyzeJob(month);
29+
CreateAnalyzeJob.createAnalyzeJob(user, month);
2630
}
2731

2832
public void complete() {
@@ -32,6 +36,15 @@ public void complete() {
3236
}
3337

3438
completed = true;
35-
CompleteAnalyzeJob.completeAnalyzeJob(month);
39+
CompleteAnalyzeJob.completeAnalyzeJob(user, month);
40+
}
41+
42+
public void fail() {
43+
if (completed) {
44+
throw StatusException.badRequest(
45+
"Cannot fail an analyze job that has already completed.", "AnalyzeJob.completed");
46+
}
47+
48+
FailAnalyzeJob.failAnalyzeJob(user, month);
3649
}
3750
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.jongsoft.finance.messaging.commands.insight;
2+
3+
import com.jongsoft.finance.messaging.ApplicationEvent;
4+
import java.time.YearMonth;
5+
6+
public record CleanInsightsForMonth(YearMonth month) implements ApplicationEvent {
7+
8+
public static void cleanInsightsForMonth(YearMonth month) {
9+
new CleanInsightsForMonth(month).publish();
10+
}
11+
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package com.jongsoft.finance.messaging.commands.insight;
22

3+
import com.jongsoft.finance.domain.user.UserIdentifier;
34
import com.jongsoft.finance.messaging.ApplicationEvent;
45
import java.time.YearMonth;
56

6-
public record CompleteAnalyzeJob(YearMonth month) implements ApplicationEvent {
7+
public record CompleteAnalyzeJob(UserIdentifier user, YearMonth month) implements ApplicationEvent {
78

8-
public static void completeAnalyzeJob(YearMonth month) {
9-
new CompleteAnalyzeJob(month).publish();
9+
public static void completeAnalyzeJob(UserIdentifier user, YearMonth month) {
10+
new CompleteAnalyzeJob(user, month).publish();
1011
}
1112
}
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
package com.jongsoft.finance.messaging.commands.insight;
22

3+
import com.jongsoft.finance.domain.user.UserIdentifier;
34
import com.jongsoft.finance.messaging.ApplicationEvent;
45
import java.time.YearMonth;
56

6-
public record CreateAnalyzeJob(YearMonth month) implements ApplicationEvent {
7+
public record CreateAnalyzeJob(UserIdentifier user, YearMonth month) implements ApplicationEvent {
78

8-
public static void createAnalyzeJob(YearMonth month) {
9-
new CreateAnalyzeJob(month).publish();
9+
public static void createAnalyzeJob(UserIdentifier user, YearMonth month) {
10+
new CreateAnalyzeJob(user, month).publish();
1011
}
1112
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.jongsoft.finance.messaging.commands.insight;
2+
3+
import com.jongsoft.finance.domain.user.UserIdentifier;
4+
import com.jongsoft.finance.messaging.ApplicationEvent;
5+
import java.time.YearMonth;
6+
7+
public record FailAnalyzeJob(UserIdentifier user, YearMonth month) implements ApplicationEvent {
8+
9+
public static void failAnalyzeJob(UserIdentifier user, YearMonth month) {
10+
new FailAnalyzeJob(user, month).publish();
11+
}
12+
}

domain/src/test/java/com/jongsoft/finance/domain/insight/AnalyzeJobTest.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.jongsoft.finance.domain.insight;
22

33
import com.jongsoft.finance.core.exception.StatusException;
4+
import com.jongsoft.finance.domain.user.UserIdentifier;
45
import com.jongsoft.finance.messaging.EventBus;
56
import com.jongsoft.finance.messaging.commands.insight.CompleteAnalyzeJob;
67
import com.jongsoft.finance.messaging.commands.insight.CreateAnalyzeJob;
@@ -28,23 +29,23 @@ void setup() {
2829
void shouldCompleteAnalyzeJobSuccessfully() {
2930
// Arrange
3031
YearMonth month = YearMonth.of(2023, 9);
31-
AnalyzeJob analyzeJob = new AnalyzeJob(month);
32+
AnalyzeJob analyzeJob = new AnalyzeJob(new UserIdentifier("test@local"), month);
3233
var mockedCompleteAnalyzeJob = mockStatic(CompleteAnalyzeJob.class);
3334

3435
// Act
3536
analyzeJob.complete();
3637

3738
// Assert
3839
assertTrue(analyzeJob.isCompleted(), "Expected analyze job to be marked as completed.");
39-
mockedCompleteAnalyzeJob.verify(() -> CompleteAnalyzeJob.completeAnalyzeJob(month));
40+
mockedCompleteAnalyzeJob.verify(() -> CompleteAnalyzeJob.completeAnalyzeJob(new UserIdentifier("test@local"), month));
4041
mockedCompleteAnalyzeJob.close();
4142
}
4243

4344
@Test
4445
void shouldThrowExceptionWhenCompletingAlreadyCompletedJob() {
4546
// Arrange
4647
YearMonth month = YearMonth.of(2023, 9);
47-
AnalyzeJob analyzeJob = new AnalyzeJob(month);
48+
AnalyzeJob analyzeJob = new AnalyzeJob(new UserIdentifier("test@local"), month);
4849
analyzeJob.complete();
4950

5051
// Act & Assert
@@ -67,10 +68,10 @@ void shouldCreateAnalyzeJobOnConstruction() {
6768
var mockedCreateAnalyzeJob = mockStatic(CreateAnalyzeJob.class);
6869

6970
// Act
70-
new AnalyzeJob(month);
71+
new AnalyzeJob(new UserIdentifier("test@local"), month);
7172

7273
// Assert
73-
mockedCreateAnalyzeJob.verify(() -> CreateAnalyzeJob.createAnalyzeJob(month));
74+
mockedCreateAnalyzeJob.verify(() -> CreateAnalyzeJob.createAnalyzeJob(new UserIdentifier("test@local"), month));
7475
mockedCreateAnalyzeJob.close();
7576
}
7677
}

jpa-repository/src/main/java/com/jongsoft/finance/jpa/insight/AnalyzeJobJpa.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
package com.jongsoft.finance.jpa.insight;
22

3-
import jakarta.persistence.Column;
4-
import jakarta.persistence.Entity;
5-
import jakarta.persistence.Id;
6-
import jakarta.persistence.Table;
3+
import com.jongsoft.finance.jpa.user.entity.UserAccountJpa;
4+
import jakarta.persistence.*;
75
import lombok.Data;
86

97
@Data
@@ -17,5 +15,9 @@ public class AnalyzeJobJpa {
1715
@Column(name = "year_month_found")
1816
private String yearMonth;
1917

18+
@ManyToOne
19+
private UserAccountJpa user;
20+
2021
private boolean completed;
22+
private boolean failed;
2123
}

jpa-repository/src/main/java/com/jongsoft/finance/jpa/insight/AnalyzeJobProviderJpa.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
import com.jongsoft.finance.annotation.BusinessEventListener;
44
import com.jongsoft.finance.domain.insight.AnalyzeJob;
5+
import com.jongsoft.finance.domain.user.UserIdentifier;
56
import com.jongsoft.finance.jpa.query.ReactiveEntityManager;
7+
import com.jongsoft.finance.jpa.user.entity.UserAccountJpa;
68
import com.jongsoft.finance.messaging.commands.insight.CompleteAnalyzeJob;
79
import com.jongsoft.finance.messaging.commands.insight.CreateAnalyzeJob;
10+
import com.jongsoft.finance.messaging.commands.insight.FailAnalyzeJob;
811
import com.jongsoft.finance.providers.AnalyzeJobProvider;
912
import jakarta.inject.Singleton;
1013
import java.time.YearMonth;
@@ -27,6 +30,7 @@ public Optional<AnalyzeJob> first() {
2730
return entityManager
2831
.from(AnalyzeJobJpa.class)
2932
.fieldEq("completed", false)
33+
.fieldEq("failed", false)
3034
.orderBy("yearMonth", true)
3135
.limit(1)
3236
.singleResult()
@@ -41,6 +45,11 @@ public void createAnalyzeJob(CreateAnalyzeJob command) {
4145

4246
var entity = new AnalyzeJobJpa();
4347
entity.setId(UUID.randomUUID().toString());
48+
entity.setUser(entityManager
49+
.from(UserAccountJpa.class)
50+
.fieldEq("username", command.user().email())
51+
.singleResult()
52+
.get());
4453
entity.setYearMonth(command.month().toString());
4554

4655
entityManager.getEntityManager().persist(entity);
@@ -54,14 +63,29 @@ public void completeAnalyzeJob(CompleteAnalyzeJob command) {
5463
.update(AnalyzeJobJpa.class)
5564
.fieldEq("yearMonth", command.month().toString())
5665
.fieldEq("completed", false)
66+
.fieldEq("user.username", command.user().email())
5767
.set("completed", true)
5868
.execute();
5969
}
6070

71+
@BusinessEventListener
72+
public void completeAnalyzeJob(FailAnalyzeJob command) {
73+
log.info("Failing analyze job for month {}.", command.month());
74+
75+
entityManager
76+
.update(AnalyzeJobJpa.class)
77+
.fieldEq("yearMonth", command.month().toString())
78+
.fieldEq("completed", false)
79+
.fieldEq("user.username", command.user().email())
80+
.set("failed", true)
81+
.execute();
82+
}
83+
6184
private AnalyzeJob convert(AnalyzeJobJpa entity) {
6285
return AnalyzeJob.builder()
6386
.jobId(entity.getId())
6487
.month(YearMonth.parse(entity.getYearMonth()))
88+
.user(new UserIdentifier(entity.getUser().getUsername()))
6589
.completed(entity.isCompleted())
6690
.build();
6791
}

jpa-repository/src/main/java/com/jongsoft/finance/jpa/insight/SpendingInsightProviderJpa.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import com.jongsoft.finance.annotation.BusinessEventListener;
66
import com.jongsoft.finance.domain.insight.SpendingInsight;
77
import com.jongsoft.finance.jpa.query.ReactiveEntityManager;
8+
import com.jongsoft.finance.messaging.commands.insight.CleanInsightsForMonth;
89
import com.jongsoft.finance.messaging.commands.insight.CreateSpendingInsight;
910
import com.jongsoft.finance.providers.SpendingInsightProvider;
1011
import com.jongsoft.finance.security.AuthenticationFacade;
@@ -115,6 +116,18 @@ public void save(CreateSpendingInsight command) {
115116
entityManager.persist(jpa);
116117
}
117118

119+
@BusinessEventListener
120+
public void cleanForMonth(CleanInsightsForMonth command) {
121+
log.trace("Cleaning spending insights for month: {}", command.month());
122+
entityManager
123+
.getEntityManager()
124+
.createQuery(
125+
"DELETE FROM SpendingInsightJpa WHERE yearMonth = :yearMonth and user.id in (select id from UserAccountJpa a where a.username = :username)")
126+
.setParameter("yearMonth", command.month().toString())
127+
.setParameter("username", authenticationFacade.authenticated())
128+
.executeUpdate();
129+
}
130+
118131
private SpendingInsight convert(SpendingInsightJpa source) {
119132
if (source == null) {
120133
return null;

jpa-repository/src/main/java/com/jongsoft/finance/jpa/insight/SpendingPatternProviderJpa.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.jongsoft.finance.annotation.BusinessEventListener;
88
import com.jongsoft.finance.domain.insight.SpendingPattern;
99
import com.jongsoft.finance.jpa.query.ReactiveEntityManager;
10+
import com.jongsoft.finance.messaging.commands.insight.CleanInsightsForMonth;
1011
import com.jongsoft.finance.messaging.commands.insight.CreateSpendingPattern;
1112
import com.jongsoft.finance.providers.SpendingPatternProvider;
1213
import com.jongsoft.finance.security.AuthenticationFacade;
@@ -117,6 +118,18 @@ public void save(CreateSpendingPattern command) {
117118
entityManager.persist(jpa);
118119
}
119120

121+
@BusinessEventListener
122+
public void cleanForMonth(CleanInsightsForMonth command) {
123+
log.trace("Cleaning spending insights for month: {}", command.month());
124+
entityManager
125+
.getEntityManager()
126+
.createQuery(
127+
"DELETE FROM SpendingPatternJpa WHERE yearMonth = :yearMonth and user.id in (select id from UserAccountJpa a where a.username = :username)")
128+
.setParameter("yearMonth", command.month().toString())
129+
.setParameter("username", authenticationFacade.authenticated())
130+
.executeUpdate();
131+
}
132+
120133
private SpendingPattern convert(SpendingPatternJpa source) {
121134
if (source == null) {
122135
return null;

0 commit comments

Comments
 (0)