Skip to content

Commit 9be4526

Browse files
committed
replication saga to update student courses in trax
1 parent 291dca1 commit 9be4526

14 files changed

Lines changed: 735 additions & 117 deletions

File tree

api/src/main/java/ca/bc/gov/educ/api/pen/replication/choreographer/ChoreographEventHandler.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import ca.bc.gov.educ.api.pen.replication.repository.EventRepository;
66
import ca.bc.gov.educ.api.pen.replication.service.EventService;
77
import ca.bc.gov.educ.api.pen.replication.struct.*;
8+
import ca.bc.gov.educ.api.pen.replication.struct.saga.StudentCourseUpdateSagaData;
89
import ca.bc.gov.educ.api.pen.replication.util.JsonUtil;
910
import com.fasterxml.jackson.core.type.TypeReference;
1011
import com.google.common.util.concurrent.ThreadFactoryBuilder;
@@ -76,8 +77,8 @@ public void handleEvent(@NonNull final Event event) {
7677
break;
7778
case "UPDATE_STUDENT_COURSES":
7879
log.info("Processing UPDATE_STUDENT_COURSES event record :: {} ", event);
79-
val studentCourseUpdateList = JsonUtil.getJsonObjectFromString(StudentCourseUpdate.class, event.getEventPayload());
80-
final EventService<StudentCourseUpdate> studentCourseEventService = (EventService<StudentCourseUpdate>) this.eventServiceMap.get(UPDATE_STUDENT_COURSES.toString());
80+
val studentCourseUpdateList = JsonUtil.getJsonObjectFromString(StudentCourseUpdateSagaData.class, event.getEventPayload());
81+
final EventService<StudentCourseUpdateSagaData> studentCourseEventService = (EventService<StudentCourseUpdateSagaData>) this.eventServiceMap.get(UPDATE_STUDENT_COURSES.toString());
8182
studentCourseEventService.processEvent(studentCourseUpdateList, event);
8283
break;
8384
case "ADOPT_GRAD_STUDENT":

api/src/main/java/ca/bc/gov/educ/api/pen/replication/constants/EventOutcome.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ public enum EventOutcome {
8787

8888
STUDENT_COURSES_UPDATED,
8989

90+
STUDENT_COURSE_UPDATE_PREPARED,
91+
92+
STUDENT_COURSES_DELETED,
93+
94+
STUDENT_COURSES_SAVED,
95+
9096
GRAD_STUDENT_ADOPTED,
9197

9298
SCHOOL_OF_RECORD_UPDATED,

api/src/main/java/ca/bc/gov/educ/api/pen/replication/constants/EventType.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,12 @@ public enum EventType {
8484

8585
UPDATE_STUDENT_COURSES,
8686

87+
PREPARE_STUDENT_COURSE_UPDATE,
88+
89+
DELETE_STUDENT_COURSES,
90+
91+
SAVE_STUDENT_COURSES,
92+
8793
ADOPT_GRAD_STUDENT,
8894

8995
UPDATE_GRAD_STUDENT_CITIZENSHIP;

api/src/main/java/ca/bc/gov/educ/api/pen/replication/constants/SagaEnum.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ public enum SagaEnum {
2121
/**
2222
* Pen replication possible match delete saga saga enum.
2323
*/
24-
PEN_REPLICATION_POSSIBLE_MATCH_DELETE_SAGA("PEN_REPLICATION_POSSIBLE_MATCH_DELETE_SAGA");
24+
PEN_REPLICATION_POSSIBLE_MATCH_DELETE_SAGA("PEN_REPLICATION_POSSIBLE_MATCH_DELETE_SAGA"),
25+
/**
26+
* Pen replication student course update saga saga enum.
27+
*/
28+
PEN_REPLICATION_STUDENT_COURSE_UPDATE_SAGA("PEN_REPLICATION_STUDENT_COURSE_UPDATE_SAGA");
2529
@Getter
2630
private final String code;
2731

api/src/main/java/ca/bc/gov/educ/api/pen/replication/constants/SagaTopicsEnum.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ public enum SagaTopicsEnum {
3030
* Pen replication possible match delete saga topic saga topics enum.
3131
*/
3232
PEN_REPLICATION_POSSIBLE_MATCH_DELETE_SAGA_TOPIC("PEN_REPLICATION_POSSIBLE_MATCH_DELETE_SAGA_TOPIC"),
33+
/**
34+
* Pen replication student course update saga topic saga topics enum.
35+
*/
36+
PEN_REPLICATION_STUDENT_COURSE_UPDATE_SAGA_TOPIC("PEN_REPLICATION_STUDENT_COURSE_UPDATE_SAGA_TOPIC"),
3337
/**
3438
* Pen match api topic saga topics enum.
3539
*/
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
package ca.bc.gov.educ.api.pen.replication.orchestrator;
2+
3+
import ca.bc.gov.educ.api.pen.replication.constants.SagaEnum;
4+
import ca.bc.gov.educ.api.pen.replication.constants.SagaTopicsEnum;
5+
import ca.bc.gov.educ.api.pen.replication.exception.PenReplicationAPIRuntimeException;
6+
import ca.bc.gov.educ.api.pen.replication.messaging.MessagePublisher;
7+
import ca.bc.gov.educ.api.pen.replication.model.Saga;
8+
import ca.bc.gov.educ.api.pen.replication.model.SagaEvent;
9+
import ca.bc.gov.educ.api.pen.replication.model.StudXcrseId;
10+
import ca.bc.gov.educ.api.pen.replication.model.TraxStudentCourseEntity;
11+
import ca.bc.gov.educ.api.pen.replication.orchestrator.base.BaseOrchestrator;
12+
import ca.bc.gov.educ.api.pen.replication.rest.RestUtils;
13+
import ca.bc.gov.educ.api.pen.replication.service.SagaService;
14+
import ca.bc.gov.educ.api.pen.replication.service.TraxStudentCourseService;
15+
import ca.bc.gov.educ.api.pen.replication.service.TraxStudentService;
16+
import ca.bc.gov.educ.api.pen.replication.struct.Event;
17+
import ca.bc.gov.educ.api.pen.replication.struct.StudentCourse;
18+
import ca.bc.gov.educ.api.pen.replication.struct.saga.StudentCourseUpdateSagaData;
19+
import ca.bc.gov.educ.api.pen.replication.util.JsonUtil;
20+
import com.fasterxml.jackson.core.JsonProcessingException;
21+
import jakarta.persistence.EntityManagerFactory;
22+
import lombok.extern.slf4j.Slf4j;
23+
import lombok.val;
24+
import org.apache.commons.lang3.StringUtils;
25+
import org.springframework.stereotype.Component;
26+
27+
import java.util.ArrayList;
28+
import java.util.List;
29+
import java.util.Objects;
30+
31+
import static ca.bc.gov.educ.api.pen.replication.constants.EventOutcome.*;
32+
import static ca.bc.gov.educ.api.pen.replication.constants.EventType.*;
33+
34+
@Component
35+
@Slf4j
36+
public class StudentCourseUpdateOrchestrator extends BaseOrchestrator<StudentCourseUpdateSagaData> {
37+
38+
private final TraxStudentCourseService traxStudentCourseService;
39+
private final TraxStudentService traxStudentService;
40+
private final RestUtils restUtils;
41+
42+
public StudentCourseUpdateOrchestrator(final SagaService sagaService,
43+
final MessagePublisher messagePublisher,
44+
final EntityManagerFactory entityManagerFactory,
45+
final TraxStudentCourseService traxStudentCourseService,
46+
final TraxStudentService traxStudentService,
47+
final RestUtils restUtils) {
48+
super(entityManagerFactory, sagaService, messagePublisher, StudentCourseUpdateSagaData.class,
49+
SagaEnum.PEN_REPLICATION_STUDENT_COURSE_UPDATE_SAGA,
50+
SagaTopicsEnum.PEN_REPLICATION_STUDENT_COURSE_UPDATE_SAGA_TOPIC);
51+
this.traxStudentCourseService = traxStudentCourseService;
52+
this.traxStudentService = traxStudentService;
53+
this.restUtils = restUtils;
54+
}
55+
56+
@Override
57+
public void populateStepsToExecuteMap() {
58+
this.stepBuilder()
59+
.begin(PREPARE_STUDENT_COURSE_UPDATE, this::prepareStudentCourseUpdate)
60+
.step(PREPARE_STUDENT_COURSE_UPDATE, STUDENT_COURSE_UPDATE_PREPARED, DELETE_STUDENT_COURSES, this::deleteStudentCourses)
61+
.step(PREPARE_STUDENT_COURSE_UPDATE, STUDENT_NOT_FOUND, MARK_SAGA_COMPLETE, this::markSagaComplete)
62+
.step(DELETE_STUDENT_COURSES, STUDENT_COURSES_DELETED, SAVE_STUDENT_COURSES, this::saveStudentCourses)
63+
.end(SAVE_STUDENT_COURSES, STUDENT_COURSES_SAVED);
64+
}
65+
66+
private void prepareStudentCourseUpdate(final Event event, final Saga saga, final StudentCourseUpdateSagaData sagaData) {
67+
saga.setSagaState(PREPARE_STUDENT_COURSE_UPDATE.toString());
68+
final SagaEvent eventStates = this.createEventState(saga, event.getEventType(), event.getEventOutcome(), event.getEventPayload());
69+
this.getSagaService().updateAttachedSagaWithEvents(saga, eventStates);
70+
71+
try {
72+
var studentPEN = restUtils.getStudentPen(sagaData.getStudentID());
73+
val existingTraxStudentRecord = this.traxStudentService.findTraxStudentByPen(StringUtils.rightPad(studentPEN, 10));
74+
75+
if (existingTraxStudentRecord.isEmpty()) {
76+
log.error("Student course event not processed for saga {} :: student does not yet exist in TRAX STUDENT_MASTER", saga.getSagaId());
77+
throw new PenReplicationAPIRuntimeException("Student not found in TRAX with ID: " + studentPEN + "This should never happen.");
78+
}
79+
var existingCourses = this.traxStudentCourseService.findTraxStudentCoursesByPen(studentPEN);
80+
existingCourses = existingCourses.stream().filter(Objects::nonNull).toList();
81+
val newCourses = getStudentCourseEntityList(studentPEN, sagaData.getStudentCourses(), existingCourses);
82+
83+
sagaData.setStudentPEN(studentPEN);
84+
sagaData.setNewCourses(newCourses);
85+
86+
try {
87+
val updatedPayload = JsonUtil.getJsonStringFromObject(sagaData);
88+
saga.setPayload(updatedPayload);
89+
this.getSagaService().updateAttachedEntityDuringSagaProcess(saga);
90+
} catch (JsonProcessingException e) {
91+
log.error("Error updating saga payload for saga: {}", saga.getSagaId(), e);
92+
throw new RuntimeException("Error updating saga payload", e);
93+
}
94+
95+
log.debug("Prepared student course update for PEN: {}, existing courses to delete: {}, new courses to save: {}",
96+
studentPEN,
97+
existingCourses.size(),
98+
newCourses.size());
99+
100+
val nextEvent = Event.builder().sagaId(saga.getSagaId())
101+
.eventType(PREPARE_STUDENT_COURSE_UPDATE)
102+
.eventOutcome(STUDENT_COURSE_UPDATE_PREPARED)
103+
.eventPayload("PREPARED")
104+
.build();
105+
this.postMessageToTopic(this.getTopicToSubscribe().getCode(), nextEvent);
106+
log.debug("responded via NATS to {} for {} Event. :: {}", this.getTopicToSubscribe(), PREPARE_STUDENT_COURSE_UPDATE, saga.getSagaId());
107+
} catch (Exception e) {
108+
log.error("Error preparing student course update for saga: {}", saga.getSagaId(), e);
109+
throw e;
110+
}
111+
}
112+
113+
private void deleteStudentCourses(final Event event, final Saga saga, final StudentCourseUpdateSagaData sagaData) {
114+
saga.setSagaState(DELETE_STUDENT_COURSES.toString());
115+
final SagaEvent eventStates = this.createEventState(saga, event.getEventType(), event.getEventOutcome(), event.getEventPayload());
116+
this.getSagaService().updateAttachedSagaWithEvents(saga, eventStates);
117+
118+
try {
119+
this.traxStudentCourseService.deleteTraxStudentCourses(sagaData.getStudentPEN());
120+
121+
val nextEvent = Event.builder().sagaId(saga.getSagaId())
122+
.eventType(DELETE_STUDENT_COURSES)
123+
.eventOutcome(STUDENT_COURSES_DELETED)
124+
.eventPayload("DELETED")
125+
.build();
126+
this.postMessageToTopic(this.getTopicToSubscribe().getCode(), nextEvent);
127+
log.info("responded via NATS to {} for {} Event. :: {}", this.getTopicToSubscribe(), DELETE_STUDENT_COURSES, saga.getSagaId());
128+
} catch (Exception e) {
129+
log.error("Error deleting student courses for saga: {}", saga.getSagaId(), e);
130+
throw e;
131+
}
132+
}
133+
134+
private void saveStudentCourses(final Event event, final Saga saga, final StudentCourseUpdateSagaData sagaData) {
135+
saga.setSagaState(SAVE_STUDENT_COURSES.toString());
136+
final SagaEvent eventStates = this.createEventState(saga, event.getEventType(), event.getEventOutcome(), event.getEventPayload());
137+
this.getSagaService().updateAttachedSagaWithEvents(saga, eventStates);
138+
139+
try {
140+
this.traxStudentCourseService.saveTraxStudentCourses(sagaData.getNewCourses());
141+
142+
val nextEvent = Event.builder().sagaId(saga.getSagaId())
143+
.eventType(SAVE_STUDENT_COURSES)
144+
.eventOutcome(STUDENT_COURSES_SAVED)
145+
.eventPayload("SAVED")
146+
.build();
147+
this.postMessageToTopic(this.getTopicToSubscribe().getCode(), nextEvent);
148+
log.info("responded via NATS to {} for {} Event. :: {}", this.getTopicToSubscribe(), SAVE_STUDENT_COURSES, saga.getSagaId());
149+
} catch (Exception e) {
150+
log.error("Error saving student courses for saga: {}", saga.getSagaId(), e);
151+
throw e;
152+
}
153+
}
154+
155+
private List<TraxStudentCourseEntity> getStudentCourseEntityList(String studentPEN,
156+
List<StudentCourse> studentCourse,
157+
List<TraxStudentCourseEntity> existingTraxStudentCourses) {
158+
var entityList = new ArrayList<TraxStudentCourseEntity>();
159+
studentCourse.forEach(student -> {
160+
TraxStudentCourseEntity traxStudentCourseEntity = new TraxStudentCourseEntity();
161+
traxStudentCourseEntity.setStudXcrseId(new StudXcrseId());
162+
setCourseCodeAndLevel(traxStudentCourseEntity, student.getCourseID());
163+
traxStudentCourseEntity.getStudXcrseId().setStudNo(StringUtils.trimToNull(studentPEN));
164+
traxStudentCourseEntity.getStudXcrseId().setCourseSession(StringUtils.trimToNull(student.getCourseSession()));
165+
traxStudentCourseEntity.setFinalLetterGrade(StringUtils.trimToNull(student.getFinalLetterGrade()));
166+
traxStudentCourseEntity.setFinalPercentage(student.getFinalPercent() != null ? student.getFinalPercent().toString() : null);
167+
traxStudentCourseEntity.setNumberOfCredits(student.getCredits() != null ? student.getCredits().toString() : null);
168+
setStudyTypeAndUsedForGradFields(traxStudentCourseEntity, existingTraxStudentCourses);
169+
entityList.add(traxStudentCourseEntity);
170+
});
171+
return entityList;
172+
}
173+
174+
private void setStudyTypeAndUsedForGradFields(TraxStudentCourseEntity traxStudentCourseEntity, List<TraxStudentCourseEntity> existingTraxStudentCourses){
175+
for(TraxStudentCourseEntity course: existingTraxStudentCourses){
176+
if(course != null) {
177+
String courseCourseCode = StringUtils.trimToNull(course.getStudXcrseId().getCourseCode());
178+
String courseCourseLevel = StringUtils.trimToNull(course.getStudXcrseId().getCourseLevel());
179+
String courseCourseSession = StringUtils.trimToNull(course.getStudXcrseId().getCourseSession());
180+
181+
String traxCourseCode = StringUtils.trimToNull(traxStudentCourseEntity.getStudXcrseId().getCourseCode());
182+
String traxCourseLevel = StringUtils.trimToNull(traxStudentCourseEntity.getStudXcrseId().getCourseLevel());
183+
String traxCourseSession = StringUtils.trimToNull(traxStudentCourseEntity.getStudXcrseId().getCourseSession());
184+
185+
if (Objects.equals(courseCourseCode, traxCourseCode) &&
186+
Objects.equals(courseCourseLevel, traxCourseLevel) &&
187+
Objects.equals(courseCourseSession, traxCourseSession)) {
188+
traxStudentCourseEntity.setStudyType(course.getStudyType());
189+
traxStudentCourseEntity.setUsedForGrad(course.getUsedForGrad());
190+
break;
191+
}
192+
}
193+
}
194+
}
195+
196+
private void setCourseCodeAndLevel(TraxStudentCourseEntity traxStudentCourseEntity, String courseID){
197+
var optionalCourse = restUtils.getCoreg39CourseByID(courseID);
198+
if (optionalCourse.isPresent()) {
199+
var course = optionalCourse.get();
200+
if(course.getExternalCode().length() > 5) {
201+
traxStudentCourseEntity.getStudXcrseId().setCourseCode(StringUtils.trimToNull(course.getExternalCode().substring(0, 4)));
202+
traxStudentCourseEntity.getStudXcrseId().setCourseLevel(StringUtils.trimToNull(course.getExternalCode().substring(5)));
203+
}else{
204+
traxStudentCourseEntity.getStudXcrseId().setCourseCode(StringUtils.trimToNull(course.getExternalCode()));
205+
traxStudentCourseEntity.getStudXcrseId().setCourseLevel(" ");//trax needs the whitespace
206+
}
207+
}else{
208+
log.info("No course was found for ID {} :: this should not have happened", courseID);
209+
throw new PenReplicationAPIRuntimeException("No course was found for ID " + courseID + " :: this should not have happened");
210+
}
211+
}
212+
}
213+

api/src/main/java/ca/bc/gov/educ/api/pen/replication/repository/TraxStudentCourseRepository.java

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

33
import ca.bc.gov.educ.api.pen.replication.model.TraxStudentCourseEntity;
44
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.data.jpa.repository.Modifying;
6+
import org.springframework.data.jpa.repository.Query;
57
import org.springframework.stereotype.Repository;
8+
import org.springframework.transaction.annotation.Transactional;
69

710
import java.util.List;
811

912
@Repository
1013
public interface TraxStudentCourseRepository extends JpaRepository<TraxStudentCourseEntity, String> {
1114
void deleteAllByStudXcrseId_StudNo(String studNo);
1215
List<TraxStudentCourseEntity> findAllByStudXcrseId_StudNo(String studNo);
16+
17+
@Modifying
18+
@Query(value = "DELETE FROM STUD_XCRSE WHERE STUD_NO = ?", nativeQuery = true)
19+
@Transactional
20+
void deleteAllByStudNoNative(String studNo);
1321
}

api/src/main/java/ca/bc/gov/educ/api/pen/replication/schedulers/EventTaskScheduler.java

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
package ca.bc.gov.educ.api.pen.replication.schedulers;
22

3+
import ca.bc.gov.educ.api.pen.replication.constants.EventType;
34
import ca.bc.gov.educ.api.pen.replication.constants.SagaEnum;
45
import ca.bc.gov.educ.api.pen.replication.constants.SagaStatusEnum;
56
import ca.bc.gov.educ.api.pen.replication.helpers.LogHelper;
67
import ca.bc.gov.educ.api.pen.replication.model.Saga;
78
import ca.bc.gov.educ.api.pen.replication.orchestrator.base.Orchestrator;
89
import ca.bc.gov.educ.api.pen.replication.repository.SagaRepository;
10+
import ca.bc.gov.educ.api.pen.replication.service.SagaService;
11+
import ca.bc.gov.educ.api.pen.replication.struct.saga.StudentCourseUpdateSagaData;
12+
import ca.bc.gov.educ.api.pen.replication.util.JsonUtil;
913
import lombok.Getter;
1014
import lombok.extern.slf4j.Slf4j;
1115
import lombok.val;
@@ -35,9 +39,15 @@ public class EventTaskScheduler {
3539
*/
3640
@Getter(PRIVATE)
3741
private final SagaRepository sagaRepository;
42+
/**
43+
* The Saga service.
44+
*/
45+
@Getter(PRIVATE)
46+
private final SagaService sagaService;
3847

39-
public EventTaskScheduler(final SagaRepository sagaRepository, final List<Orchestrator> orchestrators) {
48+
public EventTaskScheduler(final SagaRepository sagaRepository, final SagaService sagaService, final List<Orchestrator> orchestrators) {
4049
this.sagaRepository = sagaRepository;
50+
this.sagaService = sagaService;
4151
orchestrators.forEach(orchestrator -> this.sagaEnumOrchestratorMap.put(orchestrator.getSagaName(), orchestrator));
4252
log.info("'{}' Saga Orchestrators are loaded.", this.sagaEnumOrchestratorMap.keySet().stream().map(SagaEnum::getCode).collect(Collectors.joining(",")));
4353
}
@@ -65,15 +75,40 @@ private void processUncompletedSagas(final List<Saga> sagas) {
6575
if (saga.getCreateDate().isBefore(compareDate)
6676
&& this.getSagaEnumOrchestratorMap().containsKey(SagaEnum.getKeyFromValue(saga.getSagaName()))) {
6777
try {
68-
this.setRetryCountAndLog(saga);
69-
this.getSagaEnumOrchestratorMap().get(SagaEnum.getKeyFromValue(saga.getSagaName())).replaySaga(saga);
78+
if (SagaStatusEnum.STARTED.toString().equals(saga.getStatus())
79+
&& EventType.INITIATED.toString().equals(saga.getSagaState())) {
80+
this.processQueuedSaga(saga);
81+
} else {
82+
this.setRetryCountAndLog(saga);
83+
this.getSagaEnumOrchestratorMap().get(SagaEnum.getKeyFromValue(saga.getSagaName())).replaySaga(saga);
84+
}
7085
} catch (final InterruptedException ex) {
7186
Thread.currentThread().interrupt();
72-
log.error("InterruptedException while findAndProcessPendingSagaEvents :: for saga :: {} :: {}", saga, ex);
87+
log.error("InterruptedException while findAndProcessPendingSagaEvents :: for saga :: {} ", saga, ex);
7388
} catch (final Exception e) {
74-
log.error("Exception while findAndProcessPendingSagaEvents :: for saga :: {} :: {}", saga, e);
89+
log.error("Exception while findAndProcessPendingSagaEvents :: for saga :: {} ", saga, e);
90+
}
91+
}
92+
}
93+
}
94+
95+
private void processQueuedSaga(final Saga saga) {
96+
try {
97+
val sagaData = JsonUtil.getJsonObjectFromString(StudentCourseUpdateSagaData.class, saga.getPayload());
98+
99+
if (sagaData != null) {
100+
val studentID = sagaData.getStudentID();
101+
102+
// Check if there's a conflicting in-progress saga for this student
103+
val conflictingSagas = this.getSagaService().findInProgressStudentCourseUpdateSagasByStudentID(studentID).stream().filter(s -> !s.getSagaId().equals(saga.getSagaId())).toList();
104+
105+
if (conflictingSagas.isEmpty()) {
106+
this.getSagaEnumOrchestratorMap().get(SagaEnum.getKeyFromValue(saga.getSagaName())).startSaga(saga);
107+
log.info("Started queued saga {} for studentID {}", saga.getSagaId(), studentID);
75108
}
76109
}
110+
} catch (Exception e) {
111+
log.error("Error processing queued saga {}: {}", saga.getSagaId(), e.getMessage(), e);
77112
}
78113
}
79114

0 commit comments

Comments
 (0)