From cfa0b16d93044d44e3fe37d0e4e8982f215d2674 Mon Sep 17 00:00:00 2001 From: Chris Ditcher Date: Thu, 8 Aug 2024 11:49:30 -0700 Subject: [PATCH] GRAD2-2648 (#341) * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * GRAD2-2637 - Cache institute api data into Redis * Commit to save * Ignoring application-local.yaml * Changed Event to EventEntity for clarity * Added EventHistoryEntity * Added EventHistoryRepository * Implemented add to history * Fixed unit test * Added more coverage. * Fixing maintainability * Added school updated and supporting unit tests. * Added school updated and supporting unit tests. * Added school created and supporting unit tests. * Added school moved and supporting unit tests. * Added update district and supporting tests * Added missing property * Correctly transforming to entity here. * Updated unit testing * Finalizing unit testing and move school. * Forgot to add these files. * Fixing Test Coverage. * Fixing duplication of code. --------- Co-authored-by: Kamal Mohammed Co-authored-by: chris.ditcher --- .gitignore | 3 + .../ChoreographEventHandler.java | 103 ++++++----- .../educ/api/trax/constant/EventOutcome.java | 2 +- .../educ/api/trax/constant/EventStatus.java | 2 +- .../gov/educ/api/trax/constant/EventType.java | 2 +- .../trax/controller/v2/SchoolController.java | 1 + .../api/trax/exception/BusinessError.java | 2 +- .../trax/messaging/jetstream/Publisher.java | 2 +- .../trax/model/dto/ChoreographedEvent.java | 6 +- .../trax/model/dto/institute/District.java | 5 + .../model/dto/institute/MoveSchoolData.java | 33 ++++ .../api/trax/model/dto/institute/School.java | 2 + .../model/dto/institute/SchoolDetail.java | 7 - .../entity/{Event.java => EventEntity.java} | 20 +-- .../trax/model/entity/EventHistoryEntity.java | 38 ++++ .../model/entity/TraxUpdatedPubEvent.java | 4 +- .../entity/institute/DistrictEntity.java | 1 + .../entity/institute/SchoolContactEntity.java | 2 +- .../api/trax/model/entity/v2/BaseEntity.java | 49 ++++++ .../repository/EventHistoryRepository.java | 12 ++ .../api/trax/repository/EventRepository.java | 10 +- .../scheduler/JetStreamEventScheduler.java | 6 +- .../AuthorityContactCreatedService.java | 8 +- .../AuthorityContactDeletedService.java | 8 +- .../AuthorityContactUpdatedService.java | 8 +- .../ChoreographedEventPersistenceService.java | 8 +- .../DistrictContactCreatedService.java | 8 +- .../DistrictContactDeletedService.java | 8 +- .../DistrictContactUpdatedService.java | 25 ++- .../trax/service/DistrictUpdatedService.java | 39 +++++ .../api/trax/service/EventBaseService.java | 27 ++- .../api/trax/service/EventCommonService.java | 8 +- .../service/EventHandlerDelegatorService.java | 2 +- .../educ/api/trax/service/EventService.java | 4 +- .../educ/api/trax/service/RESTService.java | 23 ++- .../service/SchoolContactCreatedService.java | 8 +- .../service/SchoolContactDeletedService.java | 8 +- .../service/SchoolContactUpdatedService.java | 23 ++- .../trax/service/SchoolCreatedService.java | 39 +++++ .../api/trax/service/SchoolMovedService.java | 41 +++++ .../trax/service/SchoolUpdatedService.java | 23 +++ .../service/institute/DistrictService.java | 13 ++ .../trax/service/institute/SchoolService.java | 24 +++ .../trax/util/EducGradTraxApiConstants.java | 2 + api/src/main/resources/application.yaml | 2 + ...reographedEventPersistenceServiceTest.java | 34 ++-- .../DistrictContactUpdatedServiceTest.java | 24 +++ .../service/DistrictUpdatedServiceTest.java | 52 ++++++ .../SchoolContactUpdatedServiceTest.java | 23 +++ .../service/SchoolCreatedServiceTest.java | 52 ++++++ .../trax/service/SchoolMovedServiceTest.java | 52 ++++++ .../service/SchoolUpdatedServiceTest.java | 52 ++++++ .../InstituteDistrictServiceTest.java | 42 ++++- .../intstituteSchoolServiceTestToo.java | 163 ++++++++++++++++++ .../gov/educ/api/trax/support/TestUtils.java | 78 ++++++++- .../api/trax/util/ReplicationTestUtils.java | 6 +- api/src/test/resources/application.yaml | 2 + 57 files changed, 1087 insertions(+), 164 deletions(-) create mode 100644 api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/MoveSchoolData.java rename api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/{Event.java => EventEntity.java} (88%) create mode 100644 api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/EventHistoryEntity.java create mode 100644 api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/v2/BaseEntity.java create mode 100644 api/src/main/java/ca/bc/gov/educ/api/trax/repository/EventHistoryRepository.java create mode 100644 api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictUpdatedService.java create mode 100644 api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolCreatedService.java create mode 100644 api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolMovedService.java create mode 100644 api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolUpdatedService.java create mode 100644 api/src/test/java/ca/bc/gov/educ/api/trax/service/DistrictUpdatedServiceTest.java create mode 100644 api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolCreatedServiceTest.java create mode 100644 api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolMovedServiceTest.java create mode 100644 api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolUpdatedServiceTest.java create mode 100644 api/src/test/java/ca/bc/gov/educ/api/trax/service/institute/intstituteSchoolServiceTestToo.java diff --git a/.gitignore b/.gitignore index 1263624c..0d3181fb 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,6 @@ build/ ### VS Code ### .vscode/ + +### local dev ### +application-local.yaml diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/choreographer/ChoreographEventHandler.java b/api/src/main/java/ca/bc/gov/educ/api/trax/choreographer/ChoreographEventHandler.java index 38a1bd3f..90e07eb8 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/choreographer/ChoreographEventHandler.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/choreographer/ChoreographEventHandler.java @@ -6,7 +6,10 @@ import ca.bc.gov.educ.api.trax.model.dto.DistrictContact; import ca.bc.gov.educ.api.trax.model.dto.GradStatusEventPayloadDTO; import ca.bc.gov.educ.api.trax.model.dto.SchoolContact; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.dto.institute.District; +import ca.bc.gov.educ.api.trax.model.dto.institute.MoveSchoolData; +import ca.bc.gov.educ.api.trax.model.dto.institute.School; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import ca.bc.gov.educ.api.trax.repository.EventRepository; import ca.bc.gov.educ.api.trax.service.EventService; import ca.bc.gov.educ.api.trax.util.JsonUtil; @@ -48,74 +51,90 @@ public ChoreographEventHandler(final List eventServices, final Eve eventServices.forEach(eventService -> this.eventServiceMap.put(eventService.getEventType(), eventService)); } - public void handleEvent(@NonNull final Event event) { + public void handleEvent(@NonNull final EventEntity eventEntity) { //only one thread will process all the request. since RDB won't handle concurrent requests. this.eventExecutor.execute(() -> { try { - switch (EventType.valueOf(event.getEventType())) { + switch (EventType.valueOf(eventEntity.getEventType())) { case GRAD_STUDENT_GRADUATED -> { - log.debug("Processing GRAD_STUDENT_GRADUATED event record :: {} ", event); - val studentGraduated = JsonUtil.getJsonObjectFromString(GradStatusEventPayloadDTO.class, event.getEventPayload()); - this.eventServiceMap.get(GRAD_STUDENT_GRADUATED.toString()).processEvent(studentGraduated, event); + log.debug("Processing GRAD_STUDENT_GRADUATED eventEntity record :: {} ", eventEntity); + val studentGraduated = JsonUtil.getJsonObjectFromString(GradStatusEventPayloadDTO.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(GRAD_STUDENT_GRADUATED.toString()).processEvent(studentGraduated, eventEntity); } case GRAD_STUDENT_UPDATED -> { - log.debug("Processing GRAD_STUDENT_UPDATED event record :: {} ", event); - val studentUpdated = JsonUtil.getJsonObjectFromString(GradStatusEventPayloadDTO.class, event.getEventPayload()); - this.eventServiceMap.get(GRAD_STUDENT_UPDATED.toString()).processEvent(studentUpdated, event); + log.debug("Processing GRAD_STUDENT_UPDATED eventEntity record :: {} ", eventEntity); + val studentUpdated = JsonUtil.getJsonObjectFromString(GradStatusEventPayloadDTO.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(GRAD_STUDENT_UPDATED.toString()).processEvent(studentUpdated, eventEntity); } case GRAD_STUDENT_UNDO_COMPLETION -> { - log.debug("Processing GRAD_STUDENT_UNDO_COMPLETION event record :: {} ", event); - val studentUndoCompletion = JsonUtil.getJsonObjectFromString(GradStatusEventPayloadDTO.class, event.getEventPayload()); - this.eventServiceMap.get(GRAD_STUDENT_UNDO_COMPLETION.toString()).processEvent(studentUndoCompletion, event); + log.debug("Processing GRAD_STUDENT_UNDO_COMPLETION eventEntity record :: {} ", eventEntity); + val studentUndoCompletion = JsonUtil.getJsonObjectFromString(GradStatusEventPayloadDTO.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(GRAD_STUDENT_UNDO_COMPLETION.toString()).processEvent(studentUndoCompletion, eventEntity); } case CREATE_SCHOOL_CONTACT -> { - log.debug("Processing {} event record :: {} ", event.getEventType(), event); - val schoolContactCreated = JsonUtil.getJsonObjectFromString(SchoolContact.class, event.getEventPayload()); - this.eventServiceMap.get(CREATE_SCHOOL_CONTACT.toString()).processEvent(schoolContactCreated, event); + log.debug("Processing {} eventEntity record :: {} ", eventEntity.getEventType(), eventEntity); + val schoolContactCreated = JsonUtil.getJsonObjectFromString(SchoolContact.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(CREATE_SCHOOL_CONTACT.toString()).processEvent(schoolContactCreated, eventEntity); } case UPDATE_SCHOOL_CONTACT -> { - log.debug("Processing {} event record :: {} ", event.getEventType(), event); - val schoolContactUpdated = JsonUtil.getJsonObjectFromString(SchoolContact.class, event.getEventPayload()); - this.eventServiceMap.get(UPDATE_SCHOOL_CONTACT.toString()).processEvent(schoolContactUpdated, event); + log.debug("Processing {} eventEntity record :: {} ", eventEntity.getEventType(), eventEntity); + val schoolContactUpdated = JsonUtil.getJsonObjectFromString(SchoolContact.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(UPDATE_SCHOOL_CONTACT.toString()).processEvent(schoolContactUpdated, eventEntity); } case DELETE_SCHOOL_CONTACT -> { - log.debug("Processing {} event record :: {} ", event.getEventType(), event); - val schoolContactDeleted = JsonUtil.getJsonObjectFromString(SchoolContact.class, event.getEventPayload()); - this.eventServiceMap.get(DELETE_SCHOOL_CONTACT.toString()).processEvent(schoolContactDeleted, event); + log.debug("Processing {} eventEntity record :: {} ", eventEntity.getEventType(), eventEntity); + val schoolContactDeleted = JsonUtil.getJsonObjectFromString(SchoolContact.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(DELETE_SCHOOL_CONTACT.toString()).processEvent(schoolContactDeleted, eventEntity); } case CREATE_AUTHORITY_CONTACT -> { - log.debug("Processing {} event record :: {} ", event.getEventType(), event); - val authorityContactCreated = JsonUtil.getJsonObjectFromString(AuthorityContact.class, event.getEventPayload()); - this.eventServiceMap.get(CREATE_AUTHORITY_CONTACT.toString()).processEvent(authorityContactCreated, event); + log.debug("Processing {} eventEntity record :: {} ", eventEntity.getEventType(), eventEntity); + val authorityContactCreated = JsonUtil.getJsonObjectFromString(AuthorityContact.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(CREATE_AUTHORITY_CONTACT.toString()).processEvent(authorityContactCreated, eventEntity); } case UPDATE_AUTHORITY_CONTACT -> { - log.debug("Processing {} event record :: {} ", event.getEventType(), event); - val authorityContactUpdated = JsonUtil.getJsonObjectFromString(AuthorityContact.class, event.getEventPayload()); - this.eventServiceMap.get(UPDATE_AUTHORITY_CONTACT.toString()).processEvent(authorityContactUpdated, event); + log.debug("Processing {} eventEntity record :: {} ", eventEntity.getEventType(), eventEntity); + val authorityContactUpdated = JsonUtil.getJsonObjectFromString(AuthorityContact.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(UPDATE_AUTHORITY_CONTACT.toString()).processEvent(authorityContactUpdated, eventEntity); } case DELETE_AUTHORITY_CONTACT -> { - log.debug("Processing {} event record :: {} ", event.getEventType(), event); - val authorityContactDeleted = JsonUtil.getJsonObjectFromString(AuthorityContact.class, event.getEventPayload()); - this.eventServiceMap.get(DELETE_AUTHORITY_CONTACT.toString()).processEvent(authorityContactDeleted, event); + log.debug("Processing {} eventEntity record :: {} ", eventEntity.getEventType(), eventEntity); + val authorityContactDeleted = JsonUtil.getJsonObjectFromString(AuthorityContact.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(DELETE_AUTHORITY_CONTACT.toString()).processEvent(authorityContactDeleted, eventEntity); } case CREATE_DISTRICT_CONTACT -> { - log.debug("Processing {} event record :: {} ", event.getEventType(), event); - val districtContactCreated = JsonUtil.getJsonObjectFromString(DistrictContact.class, event.getEventPayload()); - this.eventServiceMap.get(CREATE_DISTRICT_CONTACT.toString()).processEvent(districtContactCreated, event); + log.debug("Processing {} eventEntity record :: {} ", eventEntity.getEventType(), eventEntity); + val districtContactCreated = JsonUtil.getJsonObjectFromString(DistrictContact.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(CREATE_DISTRICT_CONTACT.toString()).processEvent(districtContactCreated, eventEntity); } case UPDATE_DISTRICT_CONTACT -> { - log.debug("Processing {} event record :: {} ", event.getEventType(), event); - val districtContactUpdated = JsonUtil.getJsonObjectFromString(DistrictContact.class, event.getEventPayload()); - this.eventServiceMap.get(UPDATE_DISTRICT_CONTACT.toString()).processEvent(districtContactUpdated, event); + log.debug("Processing {} eventEntity record :: {} ", eventEntity.getEventType(), eventEntity); + val districtContactUpdated = JsonUtil.getJsonObjectFromString(DistrictContact.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(UPDATE_DISTRICT_CONTACT.toString()).processEvent(districtContactUpdated, eventEntity); } case DELETE_DISTRICT_CONTACT -> { - log.debug("Processing {} event record :: {} ", event.getEventType(), event); - val districtContactDeleted = JsonUtil.getJsonObjectFromString(DistrictContact.class, event.getEventPayload()); - this.eventServiceMap.get(DELETE_DISTRICT_CONTACT.toString()).processEvent(districtContactDeleted, event); + log.debug("Processing {} eventEntity record :: {} ", eventEntity.getEventType(), eventEntity); + val districtContactDeleted = JsonUtil.getJsonObjectFromString(DistrictContact.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(DELETE_DISTRICT_CONTACT.toString()).processEvent(districtContactDeleted, eventEntity); + } + case UPDATE_SCHOOL -> { + val schoolUpdated = JsonUtil.getJsonObjectFromString(School.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(UPDATE_SCHOOL.toString()).processEvent(schoolUpdated, eventEntity); + } + case CREATE_SCHOOL -> { + val schoolCreated = JsonUtil.getJsonObjectFromString(School.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(CREATE_SCHOOL.toString()).processEvent(schoolCreated, eventEntity); + } + case MOVE_SCHOOL -> { + val schoolMoved = JsonUtil.getJsonObjectFromString(MoveSchoolData.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(MOVE_SCHOOL.toString()).processEvent(schoolMoved, eventEntity); + } + case UPDATE_DISTRICT -> { + val districtUpdated = JsonUtil.getJsonObjectFromString(District.class, eventEntity.getEventPayload()); + this.eventServiceMap.get(UPDATE_DISTRICT.toString()).processEvent(districtUpdated, eventEntity); } default -> { - log.warn("Silently ignoring event: {}", event); - this.eventRepository.findByEventId(event.getEventId()).ifPresent(existingEvent -> { + log.warn("Silently ignoring eventEntity: {}", eventEntity); + this.eventRepository.findByEventId(eventEntity.getEventId()).ifPresent(existingEvent -> { existingEvent.setEventStatus(EventStatus.PROCESSED.toString()); existingEvent.setUpdateDate(LocalDateTime.now()); this.eventRepository.save(existingEvent); @@ -124,7 +143,7 @@ public void handleEvent(@NonNull final Event event) { } } } catch (final Exception exception) { - log.error("Exception while processing event :: {}", event, exception); + log.error("Exception while processing eventEntity :: {}", eventEntity, exception); } }); diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/constant/EventOutcome.java b/api/src/main/java/ca/bc/gov/educ/api/trax/constant/EventOutcome.java index 9a3c2a3c..e9a079b4 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/constant/EventOutcome.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/constant/EventOutcome.java @@ -1,7 +1,7 @@ package ca.bc.gov.educ.api.trax.constant; /** - * The enum Event outcome. + * The enum EventEntity outcome. */ public enum EventOutcome { /** diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/constant/EventStatus.java b/api/src/main/java/ca/bc/gov/educ/api/trax/constant/EventStatus.java index 850a7a60..9d03f7f2 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/constant/EventStatus.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/constant/EventStatus.java @@ -1,7 +1,7 @@ package ca.bc.gov.educ.api.trax.constant; /** - * The enum Event status. + * The enum EventEntity status. */ public enum EventStatus { /** diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/constant/EventType.java b/api/src/main/java/ca/bc/gov/educ/api/trax/constant/EventType.java index 405b3554..0a4cda2e 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/constant/EventType.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/constant/EventType.java @@ -1,7 +1,7 @@ package ca.bc.gov.educ.api.trax.constant; /** - * The enum Event type. + * The enum EventEntity type. */ public enum EventType { /* =========================================================== diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/controller/v2/SchoolController.java b/api/src/main/java/ca/bc/gov/educ/api/trax/controller/v2/SchoolController.java index 160f155a..c5c501f7 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/controller/v2/SchoolController.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/controller/v2/SchoolController.java @@ -56,4 +56,5 @@ public List getAllSchoolDetails() { return schoolService.getSchoolDetailsFromRedisCache(); } + } diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/exception/BusinessError.java b/api/src/main/java/ca/bc/gov/educ/api/trax/exception/BusinessError.java index 80bf54c1..8be24366 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/exception/BusinessError.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/exception/BusinessError.java @@ -3,7 +3,7 @@ import lombok.Getter; public enum BusinessError { - EVENT_ALREADY_PERSISTED("Event with event id :: $? , is already persisted in DB, a duplicate message from Jet Stream."); + EVENT_ALREADY_PERSISTED("EventEntity with event id :: $? , is already persisted in DB, a duplicate message from Jet Stream."); @Getter private final String code; diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/messaging/jetstream/Publisher.java b/api/src/main/java/ca/bc/gov/educ/api/trax/messaging/jetstream/Publisher.java index f7ae45d4..8c3e6e37 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/messaging/jetstream/Publisher.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/messaging/jetstream/Publisher.java @@ -80,7 +80,7 @@ public void dispatchChoreographyEvent(final TraxUpdatedPubEvent traxUpdatedPubEv try { log.debug("Broadcasting replicationEvent :: {}", choreographedEvent); val pub = this.jetStream.publishAsync(TRAX_UPDATE_EVENT_TOPIC.name(), JsonUtil.getJsonBytesFromObject(choreographedEvent)); - pub.thenAcceptAsync(result -> log.debug("Event ID :: {} Published to JetStream :: {}", traxUpdatedPubEvent.getEventId(), result.getSeqno())); + pub.thenAcceptAsync(result -> log.debug("EventEntity ID :: {} Published to JetStream :: {}", traxUpdatedPubEvent.getEventId(), result.getSeqno())); } catch (IOException e) { log.error("exception while broadcasting message to JetStream", e); } diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/ChoreographedEvent.java b/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/ChoreographedEvent.java index eb28e448..f9077546 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/ChoreographedEvent.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/ChoreographedEvent.java @@ -18,11 +18,11 @@ public class ChoreographedEvent { UUID eventID; /** - * The Event type. + * The EventEntity type. */ EventType eventType; /** - * The Event outcome. + * The EventEntity outcome. */ EventOutcome eventOutcome; /** @@ -30,7 +30,7 @@ public class ChoreographedEvent { */ String activityCode; /** - * The Event payload. + * The EventEntity payload. */ String eventPayload; // json string /** diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/District.java b/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/District.java index d7700fa2..a7840b8f 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/District.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/District.java @@ -1,8 +1,10 @@ package ca.bc.gov.educ.api.trax.model.dto.institute; import ca.bc.gov.educ.api.trax.model.dto.BaseModel; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; import org.springframework.stereotype.Component; import java.util.List; @@ -10,6 +12,8 @@ @Data @EqualsAndHashCode(callSuper = true) @Component("InstituteDistrict") +@NoArgsConstructor +@AllArgsConstructor public class District extends BaseModel { private String districtId; @@ -18,6 +22,7 @@ public class District extends BaseModel { private String phoneNumber; private String email; private String website; + private String displayName; private String districtRegionCode; private String districtStatusCode; private List contacts; diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/MoveSchoolData.java b/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/MoveSchoolData.java new file mode 100644 index 00000000..ff837ea7 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/MoveSchoolData.java @@ -0,0 +1,33 @@ +package ca.bc.gov.educ.api.trax.model.dto.institute; + +import ca.bc.gov.educ.api.trax.model.dto.BaseModel; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import lombok.*; + +import java.io.Serializable; + +@EqualsAndHashCode(callSuper = true) +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor +@JsonIgnoreProperties(ignoreUnknown = true) +public class MoveSchoolData extends BaseModel implements Serializable { + + /** + * The constant serialVersionUID. + */ + private static final long serialVersionUID = 1L; + + @NotNull(message = "toSchool cannot be null.") + @Valid + private School toSchool; + + @NotNull(message = "fromSchoolId cannot be null.") + private String fromSchoolId; + + @NotNull(message = "moveDate cannot be null.") + private String moveDate; +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/School.java b/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/School.java index 55adc244..245d0eef 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/School.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/School.java @@ -1,6 +1,7 @@ package ca.bc.gov.educ.api.trax.model.dto.institute; import ca.bc.gov.educ.api.trax.model.dto.BaseModel; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import lombok.EqualsAndHashCode; import org.springframework.stereotype.Component; @@ -8,6 +9,7 @@ @Data @EqualsAndHashCode(callSuper = true) @Component("InstituteSchool") +@JsonIgnoreProperties(ignoreUnknown = true) public class School extends BaseModel { private String schoolId; diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/SchoolDetail.java b/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/SchoolDetail.java index 4212fc69..9047504e 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/SchoolDetail.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/model/dto/institute/SchoolDetail.java @@ -2,11 +2,8 @@ import ca.bc.gov.educ.api.trax.model.dto.BaseModel; import ca.bc.gov.educ.api.trax.model.dto.SchoolContact; -import ca.bc.gov.educ.api.trax.model.entity.institute.*; import lombok.Data; import lombok.EqualsAndHashCode; -import org.springframework.data.annotation.Id; -import org.springframework.data.redis.core.index.Indexed; import org.springframework.stereotype.Component; import java.util.List; @@ -35,10 +32,6 @@ public class SchoolDetail extends BaseModel { private String closedDate; private boolean canIssueTranscripts; private boolean canIssueCertificates; - private String createUser; - private String updateUser; - private String createDate; - private String updateDate; List contacts; List addresses; List notes; diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/Event.java b/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/EventEntity.java similarity index 88% rename from api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/Event.java rename to api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/EventEntity.java index b6a26ab1..b3fb0f53 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/Event.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/EventEntity.java @@ -21,7 +21,7 @@ @Table(name = "REPLICATION_EVENT") @Data @DynamicUpdate -public class Event { +public class EventEntity { /** * The Create user. */ @@ -51,12 +51,12 @@ public class Event { @Column(name = "REPLICATION_EVENT_ID", unique = true, updatable = false, columnDefinition = "BINARY(16)") private UUID replicationEventId; /** - * The Event id. + * The EventEntity id. */ @Column(name = "EVENT_ID", unique = true, updatable = false, columnDefinition = "BINARY(16)") private UUID eventId; /** - * The Event payload. + * The EventEntity payload. */ @NotNull(message = "eventPayload cannot be null") @Lob @@ -64,19 +64,19 @@ public class Event { @ToString.Exclude private byte[] eventPayloadBytes; /** - * The Event status. + * The EventEntity status. */ @NotNull(message = "eventStatus cannot be null") @Column(name = "EVENT_STATUS") private String eventStatus; /** - * The Event type. + * The EventEntity type. */ @NotNull(message = "eventType cannot be null") @Column(name = "EVENT_TYPE") private String eventType; /** - * The Event outcome. + * The EventEntity outcome. */ @NotNull(message = "eventOutcome cannot be null.") @Column(name = "EVENT_OUTCOME") @@ -109,19 +109,19 @@ public void setEventPayload(final String eventPayload) { /** * The type Student event builder. */ - public static class EventBuilder { + public static class EventEntityBuilder { /** - * The Event payload bytes. + * The EventEntity payload bytes. */ byte[] eventPayloadBytes; /** - * Event payload student event . student event builder. + * EventEntity payload student event . student event builder. * * @param eventPayload the event payload * @return the student event . student event builder */ - public EventBuilder eventPayload(final String eventPayload) { + public EventEntityBuilder eventPayload(final String eventPayload) { this.eventPayloadBytes = eventPayload.getBytes(StandardCharsets.UTF_8); return this; } diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/EventHistoryEntity.java b/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/EventHistoryEntity.java new file mode 100644 index 00000000..a02234c1 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/EventHistoryEntity.java @@ -0,0 +1,38 @@ +package ca.bc.gov.educ.api.trax.model.entity; + +import ca.bc.gov.educ.api.trax.model.entity.v2.BaseEntity; +import jakarta.persistence.*; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.DynamicUpdate; +import org.hibernate.annotations.GenericGenerator; + +import java.util.UUID; + +@Getter +@Setter +@Entity +@Table(name = "EVENT_HISTORY") +@DynamicUpdate +public class EventHistoryEntity extends BaseEntity { + + @Id + @GeneratedValue(generator = "UUID") + @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator", parameters = { + @org.hibernate.annotations.Parameter(name = "uuid_gen_strategy_class", value = "org.hibernate.id.uuid.CustomVersionOneStrategy")}) + @Column(name = "EVENT_HISTORY_ID", unique = true, updatable = false, columnDefinition = "BINARY(16)") + private UUID id; + + @NotNull + @OneToOne(fetch = FetchType.EAGER) + @JoinColumn(name = "EVENT_ID", referencedColumnName = "REPLICATION_EVENT_ID", nullable = false) + private EventEntity event; + + @Size(max = 1) + @NotNull + @Column(name = "ACKNOWLEDGE_FLAG", nullable = false, length = 1) + private String acknowledgeFlag = "N"; + +} \ No newline at end of file diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/TraxUpdatedPubEvent.java b/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/TraxUpdatedPubEvent.java index 10db141e..7da7bfac 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/TraxUpdatedPubEvent.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/TraxUpdatedPubEvent.java @@ -105,12 +105,12 @@ private static String nullSafeEventPayload(String eventPayload) { */ public static class TraxUpdatedPubEventBuilder { /** - * The Event payload bytes. + * The EventEntity payload bytes. */ byte[] eventPayloadBytes; /** - * Event payload grad status event . GradStatus event builder. + * EventEntity payload grad status event . GradStatus event builder. * * @param eventPayload the event payload * @return the student event . student event builder diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/institute/DistrictEntity.java b/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/institute/DistrictEntity.java index 482e9ce7..668c4236 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/institute/DistrictEntity.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/institute/DistrictEntity.java @@ -23,6 +23,7 @@ public class DistrictEntity { private String phoneNumber; private String email; private String website; + private String displayName; private String districtRegionCode; private String districtStatusCode; private List contacts; diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/institute/SchoolContactEntity.java b/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/institute/SchoolContactEntity.java index 48fd2720..3c2d0c8b 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/institute/SchoolContactEntity.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/institute/SchoolContactEntity.java @@ -14,7 +14,7 @@ public class SchoolContactEntity { @Id - private String schooldistrictContactId; + private String schoolContactId; @Indexed private String schoolId; @Indexed diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/v2/BaseEntity.java b/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/v2/BaseEntity.java new file mode 100644 index 00000000..0e3400ae --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/model/entity/v2/BaseEntity.java @@ -0,0 +1,49 @@ +package ca.bc.gov.educ.api.trax.model.entity.v2; + +import ca.bc.gov.educ.api.trax.util.EducGradTraxApiConstants; +import ca.bc.gov.educ.api.trax.util.ThreadLocalStateUtil; +import jakarta.persistence.*; +import jakarta.validation.constraints.PastOrPresent; +import lombok.Data; +import org.apache.commons.lang3.StringUtils; + +import java.time.LocalDateTime; + +@Data +@MappedSuperclass +public class BaseEntity { + + @Column(name = "CREATE_USER", updatable = false) + String createUser; + + @Column(name = "CREATE_DATE", updatable = false) + @PastOrPresent + LocalDateTime createDate; + + @Column(name = "UPDATE_USER") + String updateUser; + + @Column(name = "UPDATE_DATE") + @PastOrPresent + LocalDateTime updateDate; + + @PrePersist + protected void onCreate() { + initUserInfo(); + this.createDate = LocalDateTime.now(); + this.updateDate = LocalDateTime.now(); + } + + @PreUpdate + protected void onPersist() { + initUserInfo(); + this.createDate = (this.createDate == null) ? LocalDateTime.now() : this.createDate; + this.updateDate = LocalDateTime.now(); + } + + private void initUserInfo() { + String user = ThreadLocalStateUtil.getCurrentUser(); + this.updateUser = (StringUtils.isBlank(user)) ? EducGradTraxApiConstants.DEFAULT_UPDATED_BY : user; + this.createUser = (StringUtils.isBlank(createUser) && StringUtils.isBlank(user)) ? EducGradTraxApiConstants.DEFAULT_CREATED_BY : user; + } +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/repository/EventHistoryRepository.java b/api/src/main/java/ca/bc/gov/educ/api/trax/repository/EventHistoryRepository.java new file mode 100644 index 00000000..3dd616b2 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/repository/EventHistoryRepository.java @@ -0,0 +1,12 @@ +package ca.bc.gov.educ.api.trax.repository; + +import ca.bc.gov.educ.api.trax.model.entity.EventHistoryEntity; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import java.util.UUID; + +@Repository +public interface EventHistoryRepository extends JpaRepository { + + +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/repository/EventRepository.java b/api/src/main/java/ca/bc/gov/educ/api/trax/repository/EventRepository.java index 9c3a7c33..9508d861 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/repository/EventRepository.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/repository/EventRepository.java @@ -1,6 +1,6 @@ package ca.bc.gov.educ.api.trax.repository; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; @@ -13,15 +13,15 @@ import java.util.UUID; @Repository -public interface EventRepository extends JpaRepository { +public interface EventRepository extends JpaRepository { - Optional findByEventId(UUID eventId); + Optional findByEventId(UUID eventId); - List findAllByEventStatusOrderByCreateDate(String eventStatus); + List findAllByEventStatusOrderByCreateDate(String eventStatus); @Transactional @Modifying - @Query("delete from Event where createDate <= :createDate") + @Query("delete from EventEntity where createDate <= :createDate") void deleteByCreateDateBefore(LocalDateTime createDate); } diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/scheduler/JetStreamEventScheduler.java b/api/src/main/java/ca/bc/gov/educ/api/trax/scheduler/JetStreamEventScheduler.java index 89d7f90e..18454088 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/scheduler/JetStreamEventScheduler.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/scheduler/JetStreamEventScheduler.java @@ -2,7 +2,7 @@ import ca.bc.gov.educ.api.trax.choreographer.ChoreographEventHandler; import ca.bc.gov.educ.api.trax.messaging.jetstream.Publisher; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import ca.bc.gov.educ.api.trax.model.entity.TraxUpdatedPubEvent; import ca.bc.gov.educ.api.trax.repository.TraxUpdatedPubEventRepository; import ca.bc.gov.educ.api.trax.repository.EventRepository; @@ -22,7 +22,7 @@ public class JetStreamEventScheduler { /** - * The Event repository. + * The EventEntity repository. */ private final EventRepository eventRepository; /** @@ -62,7 +62,7 @@ public void findAndProcessEvents() { if (!results.isEmpty()) { var filteredList = results.stream().filter(el -> el.getUpdateDate().isBefore(LocalDateTime.now().minusMinutes(5))).toList(); int cnt = 0; - for (Event e : filteredList) { + for (EventEntity e : filteredList) { if (cnt++ >= constants.getGradToTraxProcessingThreshold()) { log.info(" ==> Reached the processing threshold of {}", constants.getGradToTraxProcessingThreshold()); break; diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/AuthorityContactCreatedService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/AuthorityContactCreatedService.java index 6786d604..cc2b68b8 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/AuthorityContactCreatedService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/AuthorityContactCreatedService.java @@ -2,7 +2,7 @@ import ca.bc.gov.educ.api.trax.constant.EventType; import ca.bc.gov.educ.api.trax.model.dto.AuthorityContact; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Service @@ -10,10 +10,10 @@ public class AuthorityContactCreatedService extends EventBaseService { @Override - public void processEvent(final AuthorityContact districtContact, Event event) { + public void processEvent(final AuthorityContact districtContact, EventEntity eventEntity) { log.debug("Processing Authority Contact Created"); - // process the event here as per https://eccbc.atlassian.net/browse/GRAD2-2648 - this.updateEvent(event); + // process the eventEntity here as per https://eccbc.atlassian.net/browse/GRAD2-2648 + this.updateEvent(eventEntity); } @Override diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/AuthorityContactDeletedService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/AuthorityContactDeletedService.java index 80e45c63..7c6eba40 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/AuthorityContactDeletedService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/AuthorityContactDeletedService.java @@ -2,7 +2,7 @@ import ca.bc.gov.educ.api.trax.constant.EventType; import ca.bc.gov.educ.api.trax.model.dto.AuthorityContact; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -11,10 +11,10 @@ public class AuthorityContactDeletedService extends EventBaseService { @Override - public void processEvent(final AuthorityContact districtContact, Event event) { + public void processEvent(final AuthorityContact districtContact, EventEntity eventEntity) { log.debug("Processing Authority Contact Deleted"); - // process the event here as per https://eccbc.atlassian.net/browse/GRAD2-2648 - this.updateEvent(event); + // process the eventEntity here as per https://eccbc.atlassian.net/browse/GRAD2-2648 + this.updateEvent(eventEntity); } @Override diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/AuthorityContactUpdatedService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/AuthorityContactUpdatedService.java index 5495e37b..f6c22034 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/AuthorityContactUpdatedService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/AuthorityContactUpdatedService.java @@ -2,7 +2,7 @@ import ca.bc.gov.educ.api.trax.constant.EventType; import ca.bc.gov.educ.api.trax.model.dto.AuthorityContact; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Service @@ -10,10 +10,10 @@ public class AuthorityContactUpdatedService extends EventBaseService { @Override - public void processEvent(final AuthorityContact districtContact, Event event) { + public void processEvent(final AuthorityContact districtContact, EventEntity eventEntity) { log.debug("Processing Authority Contact Updated"); - // process the event here as per https://eccbc.atlassian.net/browse/GRAD2-2648 - this.updateEvent(event); + // process the eventEntity here as per https://eccbc.atlassian.net/browse/GRAD2-2648 + this.updateEvent(eventEntity); } @Override diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/ChoreographedEventPersistenceService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/ChoreographedEventPersistenceService.java index 533bd86d..c0bebee2 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/ChoreographedEventPersistenceService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/ChoreographedEventPersistenceService.java @@ -3,7 +3,7 @@ import ca.bc.gov.educ.api.trax.exception.BusinessError; import ca.bc.gov.educ.api.trax.exception.BusinessException; import ca.bc.gov.educ.api.trax.model.dto.ChoreographedEvent; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import ca.bc.gov.educ.api.trax.repository.EventRepository; import ca.bc.gov.educ.api.trax.repository.TraxUpdatedPubEventRepository; import lombok.extern.slf4j.Slf4j; @@ -36,12 +36,12 @@ public ChoreographedEventPersistenceService( } @Transactional(propagation = Propagation.REQUIRES_NEW) - public Event persistEventToDB(final ChoreographedEvent choreographedEvent) throws BusinessException { + public EventEntity persistEventToDB(final ChoreographedEvent choreographedEvent) throws BusinessException { var eventOptional = eventRepository.findByEventId(choreographedEvent.getEventID()); if (eventOptional.isPresent()) { throw new BusinessException(BusinessError.EVENT_ALREADY_PERSISTED, choreographedEvent.getEventID().toString()); } - final Event event = Event.builder() + final EventEntity eventEntity = EventEntity.builder() .eventType(choreographedEvent.getEventType().toString()) .eventId(choreographedEvent.getEventID()) .eventOutcome(choreographedEvent.getEventOutcome().toString()) @@ -53,7 +53,7 @@ public Event persistEventToDB(final ChoreographedEvent choreographedEvent) throw .createDate(LocalDateTime.now()) .updateDate(LocalDateTime.now()) .build(); - return this.eventRepository.save(event); + return this.eventRepository.save(eventEntity); } /** diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictContactCreatedService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictContactCreatedService.java index ee41531f..02b7d9a8 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictContactCreatedService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictContactCreatedService.java @@ -2,7 +2,7 @@ import ca.bc.gov.educ.api.trax.constant.EventType; import ca.bc.gov.educ.api.trax.model.dto.DistrictContact; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -11,10 +11,10 @@ public class DistrictContactCreatedService extends EventBaseService { @Override - public void processEvent(final DistrictContact districtContact, Event event) { + public void processEvent(final DistrictContact districtContact, EventEntity eventEntity) { log.debug("Processing District Contact Created"); - // process the event here as per https://eccbc.atlassian.net/browse/GRAD2-2648 - this.updateEvent(event); + // process the eventEntity here as per https://eccbc.atlassian.net/browse/GRAD2-2648 + this.updateEvent(eventEntity); } @Override diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictContactDeletedService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictContactDeletedService.java index 04dfb5b2..7befed56 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictContactDeletedService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictContactDeletedService.java @@ -2,7 +2,7 @@ import ca.bc.gov.educ.api.trax.constant.EventType; import ca.bc.gov.educ.api.trax.model.dto.DistrictContact; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -11,10 +11,10 @@ public class DistrictContactDeletedService extends EventBaseService { @Override - public void processEvent(final DistrictContact districtContact, Event event) { + public void processEvent(final DistrictContact districtContact, EventEntity eventEntity) { log.debug("Processing District Contact Deleted"); - // process the event here as per https://eccbc.atlassian.net/browse/GRAD2-2648 - this.updateEvent(event); + // process the eventEntity here as per https://eccbc.atlassian.net/browse/GRAD2-2648 + this.updateEvent(eventEntity); } @Override diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictContactUpdatedService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictContactUpdatedService.java index bdb80990..31f99013 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictContactUpdatedService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictContactUpdatedService.java @@ -1,20 +1,37 @@ package ca.bc.gov.educ.api.trax.service; import ca.bc.gov.educ.api.trax.constant.EventType; +import ca.bc.gov.educ.api.trax.exception.ServiceException; import ca.bc.gov.educ.api.trax.model.dto.DistrictContact; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import ca.bc.gov.educ.api.trax.service.institute.DistrictService; @Service @Slf4j public class DistrictContactUpdatedService extends EventBaseService { + private final DistrictService districtService; + + @Autowired + public DistrictContactUpdatedService(DistrictService districtService) { + super(); + this.districtService = districtService; + } + @Override - public void processEvent(final DistrictContact districtContact, Event event) { + public void processEvent(final DistrictContact districtContact, EventEntity eventEntity) { log.debug("Processing District Contact Deleted"); - // process the event here as per https://eccbc.atlassian.net/browse/GRAD2-2648 - this.updateEvent(event); + try{ + districtService.updateDistrictCache(districtContact.getDistrictId()); + this.updateEventWithHistory(eventEntity); + } catch (ServiceException e) { + // do not mark eventEntity as processed + log.error(e.getMessage()); + } + } @Override diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictUpdatedService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictUpdatedService.java new file mode 100644 index 00000000..472cc5c7 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/DistrictUpdatedService.java @@ -0,0 +1,39 @@ +package ca.bc.gov.educ.api.trax.service; + +import ca.bc.gov.educ.api.trax.constant.EventType; +import ca.bc.gov.educ.api.trax.exception.ServiceException; +import ca.bc.gov.educ.api.trax.model.dto.institute.District; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; +import ca.bc.gov.educ.api.trax.service.institute.DistrictService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class DistrictUpdatedService extends EventBaseService { + + DistrictService districtService; + + @Autowired + public DistrictUpdatedService(DistrictService districtService) { + this.districtService = districtService; + } + + @Override + public void processEvent(final District district, EventEntity eventEntity) { + log.debug("Processing District Updated"); + try{ + districtService.updateDistrictCache(district.getDistrictId()); + this.updateEventWithHistory(eventEntity); + } catch (ServiceException e) { + log.error(e.getMessage()); + } + } + + @Override + public String getEventType() { + return EventType.UPDATE_DISTRICT.toString(); + } + +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventBaseService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventBaseService.java index aed619fa..dc56c98c 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventBaseService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventBaseService.java @@ -1,23 +1,44 @@ package ca.bc.gov.educ.api.trax.service; import ca.bc.gov.educ.api.trax.constant.EventStatus; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; +import ca.bc.gov.educ.api.trax.model.entity.EventHistoryEntity; +import ca.bc.gov.educ.api.trax.repository.EventHistoryRepository; import ca.bc.gov.educ.api.trax.repository.EventRepository; import org.springframework.beans.factory.annotation.Autowired; import java.time.LocalDateTime; +import java.util.Optional; public abstract class EventBaseService implements EventService { @Autowired protected EventRepository eventRepository; + @Autowired + protected EventHistoryRepository eventHistoryRepository; - protected void updateEvent(final Event event) { - this.eventRepository.findByEventId(event.getEventId()).ifPresent(existingEvent -> { + protected void updateEvent(final EventEntity eventEntity) { + this.eventRepository.findByEventId(eventEntity.getEventId()).ifPresent(existingEvent -> { existingEvent.setEventStatus(EventStatus.PROCESSED.toString()); existingEvent.setUpdateDate(LocalDateTime.now()); this.eventRepository.save(existingEvent); }); } + /** + * Adds the event to the EventHistory table. Implementing classes may want to + * use this method if they are interested in history tracking + * @param eventEntity the event entity + */ + protected void updateEventWithHistory(final EventEntity eventEntity) { + this.updateEvent(eventEntity); + Optional savedEvent = this.eventRepository.findByEventId(eventEntity.getEventId()); + if(savedEvent.isPresent()){ + EventHistoryEntity eventHistoryEntity = new EventHistoryEntity(); + eventHistoryEntity.setEvent(savedEvent.get()); + this.eventHistoryRepository.save(eventHistoryEntity); + } + + } + } diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventCommonService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventCommonService.java index bfb40695..60248e46 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventCommonService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventCommonService.java @@ -2,7 +2,7 @@ import ca.bc.gov.educ.api.trax.constant.FieldType; import ca.bc.gov.educ.api.trax.model.dto.GradStatusEventPayloadDTO; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import ca.bc.gov.educ.api.trax.model.entity.TraxStudentEntity; import ca.bc.gov.educ.api.trax.repository.TraxStudentRepository; import ca.bc.gov.educ.api.trax.util.EducGradTraxApiConstants; @@ -49,7 +49,7 @@ public abstract class EventCommonService extends EventBaseService { private EducGradTraxApiConstants constants; @Override - public void processEvent(T request, Event event) { + public void processEvent(T request, EventEntity eventEntity) { GradStatusEventPayloadDTO gradStatusUpdate = (GradStatusEventPayloadDTO) request; val em = this.getEntityManager(); @@ -58,7 +58,7 @@ public void processEvent(T request, Event event) { try { process(existingStudent, gradStatusUpdate, em, tx, constants.isTraxUpdateEnabled()); - var existingEvent = this.eventRepository.findByEventId(event.getEventId()); + var existingEvent = this.eventRepository.findByEventId(eventEntity.getEventId()); existingEvent.ifPresent(eventRecord -> { eventRecord.setEventStatus(PROCESSED.toString()); eventRecord.setUpdateDate(LocalDateTime.now()); @@ -95,7 +95,7 @@ private void process(Optional existingStudent, GradStatusEven } } - // UpdateFieldsMap to keep which TRAX fields need to be updated by Event Type + // UpdateFieldsMap to keep which TRAX fields need to be updated by EventEntity Type private Map> setupUpdateFieldsMap() { Map> updateFieldsMap = new HashMap<>(); if (StringUtils.equalsIgnoreCase(getEventType(), "GRAD_STUDENT_GRADUATED")) { diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventHandlerDelegatorService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventHandlerDelegatorService.java index 50ccb473..b2c9dea1 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventHandlerDelegatorService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventHandlerDelegatorService.java @@ -20,7 +20,7 @@ public class EventHandlerDelegatorService { private final ChoreographEventHandler choreographer; /** - * Instantiates a new Event handler delegator service. + * Instantiates a new EventEntity handler delegator service. * * @param choreographedEventPersistenceService the choreographed event persistence service * @param choreographer the choreographer diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventService.java index eed4fe00..02572460 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/EventService.java @@ -1,10 +1,10 @@ package ca.bc.gov.educ.api.trax.service; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; public interface EventService { - void processEvent(T request, Event event); + void processEvent(T request, EventEntity eventEntity); String getEventType(); } diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/RESTService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/RESTService.java index 36101de3..b7d8c23f 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/RESTService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/RESTService.java @@ -5,12 +5,12 @@ import ca.bc.gov.educ.api.trax.util.EducGradTraxApiConstants; import ca.bc.gov.educ.api.trax.util.ThreadLocalStateUtil; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; import org.springframework.stereotype.Service; import org.springframework.web.reactive.function.BodyInserters; import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.reactive.function.client.WebClientResponseException; import reactor.core.publisher.Mono; import reactor.util.retry.Retry; @@ -19,13 +19,15 @@ @Service public class RESTService { - @Autowired + private WebClient webClient; - private static final String ERROR_5xx = "5xx error."; + private static final String SERVER_ERROR = "5xx error."; private static final String SERVICE_FAILED_ERROR = "Service failed to process after max retries."; - public RESTService() { + @Autowired + public RESTService(WebClient webClient) { + this.webClient = webClient; } /** @@ -51,7 +53,7 @@ public T get(String url, Class clazz, String accessToken) { .retrieve() // if 5xx errors, throw Service error .onStatus(HttpStatusCode::is5xxServerError, - clientResponse -> Mono.error(new ServiceException(getErrorMessage(url, ERROR_5xx), clientResponse.statusCode().value()))) + clientResponse -> Mono.error(new ServiceException(getErrorMessage(url, SERVER_ERROR), clientResponse.statusCode().value()))) .bodyToMono(clazz) // only does retry if initial error was 5xx as service may be temporarily down // 4xx errors will always happen if 404, 401, 403 etc, so does not retry @@ -80,7 +82,7 @@ public T get(String url, Class clazz, WebClient webClient) { .retrieve() // if 5xx errors, throw Service error .onStatus(HttpStatusCode::is5xxServerError, - clientResponse -> Mono.error(new ServiceException(getErrorMessage(url, ERROR_5xx), clientResponse.statusCode().value()))) + clientResponse -> Mono.error(new ServiceException(getErrorMessage(url, SERVER_ERROR), clientResponse.statusCode().value()))) .bodyToMono(clazz) // only does retry if initial error was 5xx as service may be temporarily down // 4xx errors will always happen if 404, 401, 403 etc, so does not retry @@ -92,7 +94,10 @@ public T get(String url, Class clazz, WebClient webClient) { .block(); } catch (Exception e) { // catches IOExceptions and the like - throw new ServiceException(getErrorMessage(url, e.getLocalizedMessage()), HttpStatus.SERVICE_UNAVAILABLE.value(), e); + throw new ServiceException( + getErrorMessage(url, e.getLocalizedMessage()), + (e instanceof WebClientResponseException) ? ((WebClientResponseException) e).getStatusCode().value() : HttpStatus.SERVICE_UNAVAILABLE.value(), + e); } return obj; } @@ -115,7 +120,7 @@ public T post(String url, Object body, Class clazz, String accessToken) { .body(BodyInserters.fromValue(body)) .retrieve() .onStatus(HttpStatusCode::is5xxServerError, - clientResponse -> Mono.error(new ServiceException(getErrorMessage(url, ERROR_5xx), clientResponse.statusCode().value()))) + clientResponse -> Mono.error(new ServiceException(getErrorMessage(url, SERVER_ERROR), clientResponse.statusCode().value()))) .bodyToMono(clazz) .retryWhen(Retry.backoff(3, Duration.ofSeconds(2)) .filter(ServiceException.class::isInstance) @@ -140,7 +145,7 @@ public T post(String url, Object body, Class clazz, WebClient webClient) .body(BodyInserters.fromValue(body)) .retrieve() .onStatus(HttpStatusCode::is5xxServerError, - clientResponse -> Mono.error(new ServiceException(getErrorMessage(url, ERROR_5xx), clientResponse.statusCode().value()))) + clientResponse -> Mono.error(new ServiceException(getErrorMessage(url, SERVER_ERROR), clientResponse.statusCode().value()))) .bodyToMono(clazz) .retryWhen(Retry.backoff(3, Duration.ofSeconds(2)) .filter(ServiceException.class::isInstance) diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolContactCreatedService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolContactCreatedService.java index 2401fcb9..7a977360 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolContactCreatedService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolContactCreatedService.java @@ -2,7 +2,7 @@ import ca.bc.gov.educ.api.trax.constant.EventType; import ca.bc.gov.educ.api.trax.model.dto.SchoolContact; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -11,10 +11,10 @@ public class SchoolContactCreatedService extends EventBaseService { @Override - public void processEvent(final SchoolContact districtContact, Event event) { + public void processEvent(final SchoolContact schoolContact, EventEntity eventEntity) { log.debug("Processing School Contact Created"); - // process the event here as per https://eccbc.atlassian.net/browse/GRAD2-2648 - this.updateEvent(event); + // process the eventEntity here as per https://eccbc.atlassian.net/browse/GRAD2-2648 + this.updateEvent(eventEntity); } @Override diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolContactDeletedService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolContactDeletedService.java index 8ae2fbe4..3d5d8b07 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolContactDeletedService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolContactDeletedService.java @@ -2,7 +2,7 @@ import ca.bc.gov.educ.api.trax.constant.EventType; import ca.bc.gov.educ.api.trax.model.dto.SchoolContact; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @@ -11,10 +11,10 @@ public class SchoolContactDeletedService extends EventBaseService { @Override - public void processEvent(final SchoolContact districtContact, Event event) { + public void processEvent(final SchoolContact districtContact, EventEntity eventEntity) { log.debug("Processing School Contact Deleted"); - // process the event here as per https://eccbc.atlassian.net/browse/GRAD2-2648 - this.updateEvent(event); + // process the eventEntity here as per https://eccbc.atlassian.net/browse/GRAD2-2648 + this.updateEvent(eventEntity); } @Override diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolContactUpdatedService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolContactUpdatedService.java index 12befcab..cbfe134c 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolContactUpdatedService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolContactUpdatedService.java @@ -1,20 +1,35 @@ package ca.bc.gov.educ.api.trax.service; import ca.bc.gov.educ.api.trax.constant.EventType; +import ca.bc.gov.educ.api.trax.exception.ServiceException; import ca.bc.gov.educ.api.trax.model.dto.SchoolContact; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import ca.bc.gov.educ.api.trax.service.institute.SchoolService; @Service @Slf4j public class SchoolContactUpdatedService extends EventBaseService { + SchoolService schoolService; + + @Autowired + public SchoolContactUpdatedService(SchoolService schoolService) { + this.schoolService = schoolService; + } + @Override - public void processEvent(final SchoolContact districtContact, Event event) { + public void processEvent(final SchoolContact schoolContact, EventEntity eventEntity) { log.debug("Processing School Contact Updated"); - // process the event here as per https://eccbc.atlassian.net/browse/GRAD2-2648 - this.updateEvent(event); + try{ + schoolService.updateSchoolCache(schoolContact.getSchoolId()); + this.updateEventWithHistory(eventEntity); + } catch (ServiceException e) { + // do not mark eventEntity as processed + log.error(e.getMessage()); + } } @Override diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolCreatedService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolCreatedService.java new file mode 100644 index 00000000..a07e668a --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolCreatedService.java @@ -0,0 +1,39 @@ +package ca.bc.gov.educ.api.trax.service; + +import ca.bc.gov.educ.api.trax.constant.EventType; +import ca.bc.gov.educ.api.trax.exception.ServiceException; +import ca.bc.gov.educ.api.trax.model.dto.institute.School; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; +import ca.bc.gov.educ.api.trax.service.institute.SchoolService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class SchoolCreatedService extends EventBaseService { + + SchoolService schoolService; + + @Autowired + public SchoolCreatedService(SchoolService schoolService) { + this.schoolService = schoolService; + } + + @Override + public void processEvent(final School school, EventEntity eventEntity) { + log.debug("Processing School Created"); + try{ + schoolService.updateSchoolCache(school.getSchoolId()); + this.updateEventWithHistory(eventEntity); + } catch (ServiceException e) { + log.error(e.getMessage()); + } + } + + @Override + public String getEventType() { + return EventType.CREATE_SCHOOL.toString(); + } + +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolMovedService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolMovedService.java new file mode 100644 index 00000000..41094fc3 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolMovedService.java @@ -0,0 +1,41 @@ +package ca.bc.gov.educ.api.trax.service; + +import ca.bc.gov.educ.api.trax.constant.EventType; +import ca.bc.gov.educ.api.trax.exception.ServiceException; +import ca.bc.gov.educ.api.trax.model.dto.institute.MoveSchoolData; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; +import ca.bc.gov.educ.api.trax.service.institute.SchoolService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Arrays; + +@Service +@Slf4j +public class SchoolMovedService extends EventBaseService { + + SchoolService schoolService; + + @Autowired + public SchoolMovedService(SchoolService schoolService) { + this.schoolService = schoolService; + } + + @Override + public void processEvent(final MoveSchoolData moveSchoolData, EventEntity eventEntity) { + log.debug("Processing School Moved"); + try{ + schoolService.updateSchoolCache(Arrays.asList(moveSchoolData.getFromSchoolId(), moveSchoolData.getToSchool().getSchoolId())); + this.updateEventWithHistory(eventEntity); + } catch (ServiceException e) { + log.error(e.getMessage()); + } + } + + @Override + public String getEventType() { + return EventType.MOVE_SCHOOL.toString(); + } + +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolUpdatedService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolUpdatedService.java new file mode 100644 index 00000000..33097e64 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/SchoolUpdatedService.java @@ -0,0 +1,23 @@ +package ca.bc.gov.educ.api.trax.service; + +import ca.bc.gov.educ.api.trax.constant.EventType; +import ca.bc.gov.educ.api.trax.service.institute.SchoolService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class SchoolUpdatedService extends SchoolCreatedService { + + @Autowired + public SchoolUpdatedService(SchoolService schoolService) { + super(schoolService); + } + + @Override + public String getEventType() { + return EventType.UPDATE_SCHOOL.toString(); + } + +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/institute/DistrictService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/institute/DistrictService.java index 246b0200..1d528dcd 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/institute/DistrictService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/institute/DistrictService.java @@ -1,6 +1,7 @@ package ca.bc.gov.educ.api.trax.service.institute; import ca.bc.gov.educ.api.trax.constant.CacheKey; +import ca.bc.gov.educ.api.trax.exception.ServiceException; import ca.bc.gov.educ.api.trax.model.dto.institute.District; import ca.bc.gov.educ.api.trax.model.entity.institute.DistrictEntity; import ca.bc.gov.educ.api.trax.model.transformer.institute.DistrictTransformer; @@ -62,4 +63,16 @@ public List getDistrictsFromRedisCache() { public void initializeDistrictCache(boolean force) { serviceHelper.initializeCache(force, CacheKey.DISTRICT_CACHE, this); } + + /** + * Updates the district details in the cache + * based on schoolId + * @param districtId the district id guid + */ + public void updateDistrictCache(String districtId) throws ServiceException { + log.debug(String.format("Updating district %s in cache.", districtId)); + District district = this.restService.get(String.format(constants.getGetDistrictFromInstituteApiUrl(), districtId), + District.class, webClient); + districtRedisRepository.save(this.districtTransformer.transformToEntity(district)); + } } diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/service/institute/SchoolService.java b/api/src/main/java/ca/bc/gov/educ/api/trax/service/institute/SchoolService.java index 6d414f1c..e52b6cb3 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/service/institute/SchoolService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/service/institute/SchoolService.java @@ -1,6 +1,7 @@ package ca.bc.gov.educ.api.trax.service.institute; import ca.bc.gov.educ.api.trax.constant.CacheKey; +import ca.bc.gov.educ.api.trax.exception.ServiceException; import ca.bc.gov.educ.api.trax.model.dto.institute.School; import ca.bc.gov.educ.api.trax.model.dto.institute.SchoolDetail; import ca.bc.gov.educ.api.trax.model.entity.institute.SchoolEntity; @@ -115,4 +116,27 @@ public void initializeSchoolDetailCache(boolean force) { serviceHelper.initializeCache(force, CacheKey.SCHOOL_DETAIL_CACHE, this); } + /** + * Updates the school and school details in the cache + * based on schoolId + * @param schoolId the school id guid + */ + public void updateSchoolCache(String schoolId) throws ServiceException { + // get details from institute + log.debug("Updating school %s in cache.", schoolId); + SchoolDetail schoolDetail = this.restService.get(String.format(constants.getSchoolDetailsByIdFromInstituteApiUrl(), schoolId), + SchoolDetail.class, webClient); + schoolDetailRedisRepository.save(schoolDetailTransformer.transformToEntity(schoolDetail)); + } + + /** + * Updates the school and school details in the cache + * based on schoolId + * @param schoolIds the school id guids + */ + public void updateSchoolCache(List schoolIds) throws ServiceException { + for (String schoolId : schoolIds) { + updateSchoolCache(schoolId); + } + } } diff --git a/api/src/main/java/ca/bc/gov/educ/api/trax/util/EducGradTraxApiConstants.java b/api/src/main/java/ca/bc/gov/educ/api/trax/util/EducGradTraxApiConstants.java index 7b78fcee..36491b76 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/trax/util/EducGradTraxApiConstants.java +++ b/api/src/main/java/ca/bc/gov/educ/api/trax/util/EducGradTraxApiConstants.java @@ -166,6 +166,8 @@ public class EducGradTraxApiConstants { @Value("${endpoint.institute-api.get-all-districts.url}") private String allDistrictsFromInstituteApiUrl; + @Value("${endpoint.institute-api.get-district.url}") + private String getDistrictFromInstituteApiUrl; @Value("${endpoint.institute-api.get-all-school-category-codes.url}") private String allSchoolCategoryCodesFromInstituteApiUrl; diff --git a/api/src/main/resources/application.yaml b/api/src/main/resources/application.yaml index 0565b42f..3424dc39 100644 --- a/api/src/main/resources/application.yaml +++ b/api/src/main/resources/application.yaml @@ -188,6 +188,8 @@ endpoint: url: ${INSTITUTE_API_URL_ROOT}api/v1/institute/school/%s get-all-districts: url: ${INSTITUTE_API_URL_ROOT}api/v1/institute/district + get-district: + url: ${INSTITUTE_API_URL_ROOT}api/v1/institute/district/%s get-all-school-category-codes: url: ${INSTITUTE_API_URL_ROOT}api/v1/institute/category-codes get-all-school-funding-group-codes: diff --git a/api/src/test/java/ca/bc/gov/educ/api/trax/service/ChoreographedEventPersistenceServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/trax/service/ChoreographedEventPersistenceServiceTest.java index a64b83cc..7fc0a2af 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/trax/service/ChoreographedEventPersistenceServiceTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/trax/service/ChoreographedEventPersistenceServiceTest.java @@ -7,7 +7,7 @@ import ca.bc.gov.educ.api.trax.messaging.jetstream.Publisher; import ca.bc.gov.educ.api.trax.messaging.jetstream.Subscriber; import ca.bc.gov.educ.api.trax.model.dto.ChoreographedEvent; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import ca.bc.gov.educ.api.trax.model.entity.TraxUpdatedPubEvent; import ca.bc.gov.educ.api.trax.repository.EventRepository; import ca.bc.gov.educ.api.trax.repository.TraxUpdatedPubEventRepository; @@ -83,14 +83,14 @@ public void testPersistEventToDB_givenTheExistingEvent() throws BusinessExceptio choreographedEvent.setEventType(EventType.GRAD_STUDENT_UPDATED); choreographedEvent.setEventOutcome(EventOutcome.GRAD_STATUS_UPDATED); - Event event = new Event(); - event.setEventType(EventType.GRAD_STUDENT_UPDATED.toString()); - event.setEventStatus(DB_COMMITTED.toString()); - event.setEventId(eventId); - event.setEventOutcome(EventOutcome.GRAD_STATUS_UPDATED.toString()); - event.setReplicationEventId(UUID.randomUUID()); + EventEntity eventEntity = new EventEntity(); + eventEntity.setEventType(EventType.GRAD_STUDENT_UPDATED.toString()); + eventEntity.setEventStatus(DB_COMMITTED.toString()); + eventEntity.setEventId(eventId); + eventEntity.setEventOutcome(EventOutcome.GRAD_STATUS_UPDATED.toString()); + eventEntity.setReplicationEventId(UUID.randomUUID()); - Mockito.when(eventRepository.findByEventId(eventId)).thenReturn(Optional.of(event)); + Mockito.when(eventRepository.findByEventId(eventId)).thenReturn(Optional.of(eventEntity)); choreographedEventPersistenceService.persistEventToDB(choreographedEvent); @@ -105,17 +105,17 @@ public void testPersistEventToDB_givenTheNewEvent() throws BusinessException { choreographedEvent.setEventID(eventId); choreographedEvent.setEventType(EventType.GRAD_STUDENT_UPDATED); choreographedEvent.setEventOutcome(EventOutcome.GRAD_STATUS_UPDATED); - choreographedEvent.setEventPayload("{ test: 'event'}"); + choreographedEvent.setEventPayload("{ test: 'eventEntity'}"); - Event event = new Event(); - event.setEventType(EventType.GRAD_STUDENT_UPDATED.toString()); - event.setEventStatus(DB_COMMITTED.toString()); - event.setEventId(eventId); - event.setEventOutcome(EventOutcome.GRAD_STATUS_UPDATED.toString()); - event.setReplicationEventId(UUID.randomUUID()); + EventEntity eventEntity = new EventEntity(); + eventEntity.setEventType(EventType.GRAD_STUDENT_UPDATED.toString()); + eventEntity.setEventStatus(DB_COMMITTED.toString()); + eventEntity.setEventId(eventId); + eventEntity.setEventOutcome(EventOutcome.GRAD_STATUS_UPDATED.toString()); + eventEntity.setReplicationEventId(UUID.randomUUID()); Mockito.when(eventRepository.findByEventId(eventId)).thenReturn(Optional.empty()); - Mockito.when(eventRepository.save(event)).thenReturn(event); + Mockito.when(eventRepository.save(eventEntity)).thenReturn(eventEntity); choreographedEventPersistenceService.persistEventToDB(choreographedEvent); @@ -123,7 +123,7 @@ public void testPersistEventToDB_givenTheNewEvent() throws BusinessException { } @Test - public void testUpdateEventStatus_givenTraxUpdatedEvent() throws BusinessException { + public void testUpdateEventStatus_givenTraxUpdatedEvent() { UUID eventId = UUID.randomUUID(); ChoreographedEvent choreographedEvent = new ChoreographedEvent(); diff --git a/api/src/test/java/ca/bc/gov/educ/api/trax/service/DistrictContactUpdatedServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/trax/service/DistrictContactUpdatedServiceTest.java index d91b65aa..937e3ad0 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/trax/service/DistrictContactUpdatedServiceTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/trax/service/DistrictContactUpdatedServiceTest.java @@ -1,17 +1,26 @@ package ca.bc.gov.educ.api.trax.service; +import ca.bc.gov.educ.api.trax.exception.ServiceException; +import ca.bc.gov.educ.api.trax.service.institute.DistrictService; import ca.bc.gov.educ.api.trax.support.TestUtils; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; + import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doThrow; public class DistrictContactUpdatedServiceTest extends BaseReplicationServiceTest { @Autowired private DistrictContactUpdatedService districtContactUpdatedService; + @MockBean + private DistrictService districtServiceMock; + @Test public void testProcessEvent_givenUPDATE_DISTRICT_CONTACT_Event_shouldProcessEvent() throws JsonProcessingException { final var request = TestUtils.createDistrictContact(); @@ -25,4 +34,19 @@ public void testProcessEvent_givenUPDATE_DISTRICT_CONTACT_Event_shouldProcessEve } } + @Test + public void testProcessEvent_givenUPDATE_DISTRICT_CONTACT_Event_ServiceUnavailable_triggerError() throws JsonProcessingException { + final String ERROR_MSG = "Test Exception"; + doThrow(new ServiceException(ERROR_MSG)).when(districtServiceMock).updateDistrictCache(anyString()); + final var request = TestUtils.createDistrictContact(); + final var event = TestUtils.createEvent("UPDATE_SCHOOL_CONTACT", request, this.replicationTestUtils.getEventRepository()); + this.districtContactUpdatedService.processEvent(request, event); + var result = this.replicationTestUtils.getEventRepository().findById(event.getReplicationEventId()); + if(result.isPresent()){ + Assert.assertEquals("DB_COMMITTED", result.get().getEventStatus()); + } else { + fail("UPDATE_SCHOOL_CONTACT failed to process"); + } + } + } diff --git a/api/src/test/java/ca/bc/gov/educ/api/trax/service/DistrictUpdatedServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/trax/service/DistrictUpdatedServiceTest.java new file mode 100644 index 00000000..503d3af8 --- /dev/null +++ b/api/src/test/java/ca/bc/gov/educ/api/trax/service/DistrictUpdatedServiceTest.java @@ -0,0 +1,52 @@ +package ca.bc.gov.educ.api.trax.service; + +import ca.bc.gov.educ.api.trax.constant.EventType; +import ca.bc.gov.educ.api.trax.exception.ServiceException; +import ca.bc.gov.educ.api.trax.service.institute.DistrictService; +import ca.bc.gov.educ.api.trax.support.TestUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; + +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doThrow; + +public class DistrictUpdatedServiceTest extends BaseReplicationServiceTest { + + @Autowired + private DistrictUpdatedService districtUpdatedService; + + @MockBean + private DistrictService districtServiceMock; + + @Test + public void testProcessEvent_givenUPDATE_DISTRICT_Event_shouldProcessEvent() throws JsonProcessingException { + final var request = TestUtils.createDistrict(); + final var event = TestUtils.createEvent(EventType.UPDATE_DISTRICT.toString(), request, this.replicationTestUtils.getEventRepository()); + this.districtUpdatedService.processEvent(request, event); + var result = this.replicationTestUtils.getEventRepository().findById(event.getReplicationEventId()); + if(result.isPresent()){ + Assert.assertEquals("PROCESSED", result.get().getEventStatus()); + } else { + fail("UPDATE_DISTRICT failed to process"); + } + } + + @Test + public void testProcessEvent_givenUPDATE_DISTRICT_Event_ServiceUnavailable_triggerError() throws JsonProcessingException { + final String ERROR_MSG = "Test Exception"; + doThrow(new ServiceException(ERROR_MSG)).when(districtServiceMock).updateDistrictCache(anyString()); + final var request = TestUtils.createDistrict(); + final var event = TestUtils.createEvent(EventType.UPDATE_DISTRICT.toString(), request, this.replicationTestUtils.getEventRepository()); + this.districtUpdatedService.processEvent(request, event); + var result = this.replicationTestUtils.getEventRepository().findById(event.getReplicationEventId()); + if(result.isPresent()){ + Assert.assertEquals("DB_COMMITTED", result.get().getEventStatus()); + } else { + fail("UPDATE_DISTRICT failed to process"); + } + } +} diff --git a/api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolContactUpdatedServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolContactUpdatedServiceTest.java index 7d44daba..d89dac5e 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolContactUpdatedServiceTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolContactUpdatedServiceTest.java @@ -1,18 +1,26 @@ package ca.bc.gov.educ.api.trax.service; +import ca.bc.gov.educ.api.trax.exception.ServiceException; +import ca.bc.gov.educ.api.trax.service.institute.SchoolService; import ca.bc.gov.educ.api.trax.support.TestUtils; import com.fasterxml.jackson.core.JsonProcessingException; import org.junit.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doThrow; public class SchoolContactUpdatedServiceTest extends BaseReplicationServiceTest { @Autowired private SchoolContactUpdatedService schoolContactUpdatedService; + @MockBean + private SchoolService schoolServiceMock; + @Test public void testProcessEvent_givenUPDATE_SCHOOL_CONTACT_Event_shouldProcessEvent() throws JsonProcessingException { final var request = TestUtils.createSchoolContact(); @@ -25,4 +33,19 @@ public void testProcessEvent_givenUPDATE_SCHOOL_CONTACT_Event_shouldProcessEvent fail("UPDATE_SCHOOL_CONTACT failed to process"); } } + + @Test + public void testProcessEvent_givenUPDATE_SCHOOL_CONTACT_Event_ServiceUnavailable_triggerError() throws JsonProcessingException { + final String ERROR_MSG = "Test Exception"; + doThrow(new ServiceException(ERROR_MSG)).when(schoolServiceMock).updateSchoolCache(anyString()); + final var request = TestUtils.createSchoolContact(); + final var event = TestUtils.createEvent("UPDATE_SCHOOL_CONTACT", request, this.replicationTestUtils.getEventRepository()); + this.schoolContactUpdatedService.processEvent(request, event); + var result = this.replicationTestUtils.getEventRepository().findById(event.getReplicationEventId()); + if(result.isPresent()){ + Assert.assertEquals("DB_COMMITTED", result.get().getEventStatus()); + } else { + fail("UPDATE_SCHOOL_CONTACT failed to process"); + } + } } diff --git a/api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolCreatedServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolCreatedServiceTest.java new file mode 100644 index 00000000..d8979264 --- /dev/null +++ b/api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolCreatedServiceTest.java @@ -0,0 +1,52 @@ +package ca.bc.gov.educ.api.trax.service; + +import ca.bc.gov.educ.api.trax.constant.EventType; +import ca.bc.gov.educ.api.trax.exception.ServiceException; +import ca.bc.gov.educ.api.trax.service.institute.SchoolService; +import ca.bc.gov.educ.api.trax.support.TestUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; + +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doThrow; + +public class SchoolCreatedServiceTest extends BaseReplicationServiceTest { + + @Autowired + private SchoolCreatedService schoolCreatedService; + + @MockBean + private SchoolService schoolServiceMock; + + @Test + public void testProcessEvent_givenCREATE_SCHOOL_Event_shouldProcessEvent() throws JsonProcessingException { + final var request = TestUtils.createSchool(); + final var event = TestUtils.createEvent(EventType.CREATE_SCHOOL.toString(), request, this.replicationTestUtils.getEventRepository()); + this.schoolCreatedService.processEvent(request, event); + var result = this.replicationTestUtils.getEventRepository().findById(event.getReplicationEventId()); + if(result.isPresent()){ + Assert.assertEquals("PROCESSED", result.get().getEventStatus()); + } else { + fail("CREATE_SCHOOL failed to process"); + } + } + + @Test + public void testProcessEvent_givenCREATE_SCHOOL_Event_ServiceUnavailable_triggerError() throws JsonProcessingException { + final String ERROR_MSG = "Test Exception"; + doThrow(new ServiceException(ERROR_MSG)).when(schoolServiceMock).updateSchoolCache(anyString()); + final var request = TestUtils.createSchool(); + final var event = TestUtils.createEvent(EventType.CREATE_SCHOOL.toString(), request, this.replicationTestUtils.getEventRepository()); + this.schoolCreatedService.processEvent(request, event); + var result = this.replicationTestUtils.getEventRepository().findById(event.getReplicationEventId()); + if(result.isPresent()){ + Assert.assertEquals("DB_COMMITTED", result.get().getEventStatus()); + } else { + fail("CREATE_SCHOOL failed to process"); + } + } +} diff --git a/api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolMovedServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolMovedServiceTest.java new file mode 100644 index 00000000..bf76d956 --- /dev/null +++ b/api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolMovedServiceTest.java @@ -0,0 +1,52 @@ +package ca.bc.gov.educ.api.trax.service; + +import ca.bc.gov.educ.api.trax.constant.EventType; +import ca.bc.gov.educ.api.trax.exception.ServiceException; +import ca.bc.gov.educ.api.trax.service.institute.SchoolService; +import ca.bc.gov.educ.api.trax.support.TestUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; + +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.anyList; +import static org.mockito.Mockito.doThrow; + +public class SchoolMovedServiceTest extends BaseReplicationServiceTest { + + @Autowired + private SchoolMovedService schoolMovedService; + + @MockBean + private SchoolService schoolServiceMock; + + @Test + public void testProcessEvent_givenMOVE_SCHOOL_Event_shouldProcessEvent() throws JsonProcessingException { + final var request = TestUtils.createMoveSchoolData(); + final var event = TestUtils.createEvent(EventType.MOVE_SCHOOL.toString(), request, this.replicationTestUtils.getEventRepository()); + this.schoolMovedService.processEvent(request, event); + var result = this.replicationTestUtils.getEventRepository().findById(event.getReplicationEventId()); + if(result.isPresent()){ + Assert.assertEquals("PROCESSED", result.get().getEventStatus()); + } else { + fail("MOVE_SCHOOL failed to process"); + } + } + + @Test + public void testProcessEvent_givenMOVE_SCHOOL_Event_ServiceUnavailable_triggerError() throws JsonProcessingException { + final String ERROR_MSG = "Test Exception"; + doThrow(new ServiceException(ERROR_MSG)).when(schoolServiceMock).updateSchoolCache(anyList()); + final var request = TestUtils.createMoveSchoolData(); + final var event = TestUtils.createEvent(EventType.MOVE_SCHOOL.toString(), request, this.replicationTestUtils.getEventRepository()); + this.schoolMovedService.processEvent(request, event); + var result = this.replicationTestUtils.getEventRepository().findById(event.getReplicationEventId()); + if(result.isPresent()){ + Assert.assertEquals("DB_COMMITTED", result.get().getEventStatus()); + } else { + fail("MOVE_SCHOOL failed to process"); + } + } +} diff --git a/api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolUpdatedServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolUpdatedServiceTest.java new file mode 100644 index 00000000..b6e76f90 --- /dev/null +++ b/api/src/test/java/ca/bc/gov/educ/api/trax/service/SchoolUpdatedServiceTest.java @@ -0,0 +1,52 @@ +package ca.bc.gov.educ.api.trax.service; + +import ca.bc.gov.educ.api.trax.constant.EventType; +import ca.bc.gov.educ.api.trax.exception.ServiceException; +import ca.bc.gov.educ.api.trax.service.institute.SchoolService; +import ca.bc.gov.educ.api.trax.support.TestUtils; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.Assert; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; + +import static org.assertj.core.api.Assertions.fail; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.doThrow; + +public class SchoolUpdatedServiceTest extends BaseReplicationServiceTest { + + @Autowired + private SchoolUpdatedService schoolUpdatedService; + + @MockBean + private SchoolService schoolServiceMock; + + @Test + public void testProcessEvent_givenUPDATE_SCHOOL_Event_shouldProcessEvent() throws JsonProcessingException { + final var request = TestUtils.createSchool(); + final var event = TestUtils.createEvent(EventType.UPDATE_SCHOOL.toString(), request, this.replicationTestUtils.getEventRepository()); + this.schoolUpdatedService.processEvent(request, event); + var result = this.replicationTestUtils.getEventRepository().findById(event.getReplicationEventId()); + if(result.isPresent()){ + Assert.assertEquals("PROCESSED", result.get().getEventStatus()); + } else { + fail("UPDATE_SCHOOL failed to process"); + } + } + + @Test + public void testProcessEvent_givenUPDATE_SCHOOL_Event_ServiceUnavailable_triggerError() throws JsonProcessingException { + final String ERROR_MSG = "Test Exception"; + doThrow(new ServiceException(ERROR_MSG)).when(schoolServiceMock).updateSchoolCache(anyString()); + final var request = TestUtils.createSchool(); + final var event = TestUtils.createEvent(EventType.UPDATE_SCHOOL.toString(), request, this.replicationTestUtils.getEventRepository()); + this.schoolUpdatedService.processEvent(request, event); + var result = this.replicationTestUtils.getEventRepository().findById(event.getReplicationEventId()); + if(result.isPresent()){ + Assert.assertEquals("DB_COMMITTED", result.get().getEventStatus()); + } else { + fail("UPDATE_SCHOOL failed to process"); + } + } +} diff --git a/api/src/test/java/ca/bc/gov/educ/api/trax/service/institute/InstituteDistrictServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/trax/service/institute/InstituteDistrictServiceTest.java index 6ad328f1..0f4afd93 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/trax/service/institute/InstituteDistrictServiceTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/trax/service/institute/InstituteDistrictServiceTest.java @@ -11,13 +11,15 @@ import ca.bc.gov.educ.api.trax.model.entity.institute.DistrictEntity; import ca.bc.gov.educ.api.trax.model.transformer.institute.DistrictTransformer; import ca.bc.gov.educ.api.trax.repository.redis.DistrictRedisRepository; +import ca.bc.gov.educ.api.trax.service.RESTService; +import ca.bc.gov.educ.api.trax.support.TestUtils; import ca.bc.gov.educ.api.trax.util.EducGradTraxApiConstants; import ca.bc.gov.educ.api.trax.util.RestUtils; -import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.junit.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.runner.RunWith; import org.mockito.Mock; +import org.mockito.Mockito; import org.mockito.exceptions.base.MockitoException; import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.beans.factory.annotation.Autowired; @@ -27,9 +29,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Bean; import org.springframework.core.ParameterizedTypeReference; -import org.springframework.data.redis.connection.RedisClusterConfiguration; import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; -import org.springframework.data.redis.core.ValueOperations; import org.springframework.http.HttpHeaders; import org.springframework.security.oauth2.client.registration.ClientRegistration; import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; @@ -39,12 +39,13 @@ import reactor.core.publisher.Mono; import redis.clients.jedis.JedisCluster; -import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.function.Consumer; +import static org.junit.Assert.assertTrue; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; @@ -62,6 +63,8 @@ public class InstituteDistrictServiceTest { @Autowired private EducGradTraxApiConstants constants; @Autowired + private DistrictTransformer districtTransformer; + @Autowired private DistrictService districtService; @MockBean private DistrictRedisRepository districtRedisRepository; @@ -73,6 +76,8 @@ public class InstituteDistrictServiceTest { @MockBean @Qualifier("default") WebClient webClientMock; + + @Mock private WebClient.RequestHeadersSpec requestHeadersSpecMock; @Mock @@ -87,8 +92,7 @@ public class InstituteDistrictServiceTest { private Mono> districtEntitiesMock; @Mock private List districtsMock; - @MockBean - private DistrictTransformer districtTransformerMock; + // NATS @MockBean @@ -101,6 +105,8 @@ public class InstituteDistrictServiceTest { private Subscriber subscriber; @MockBean private RestUtils restUtils; + @MockBean + private RESTService restServiceMock; @TestConfiguration static class TestConfigInstitute { @@ -249,4 +255,28 @@ public void whenInitializeDistrictCache_WithReadyAndTrue_ThenForceLoad() { .thenReturn(anyString()); districtService.initializeDistrictCache(true); } + + @Test + public void updateDistrictCache_givenValidDistrictId_shouldUpdateCache() { + // given + // set up initial district in redis mock + DistrictEntity districtEntity = createDistrictEntity(); + District district = districtTransformer.transformToDTO(districtEntity); + // when + // call updateDistrictCache with district id and mock a return from webclient + Mockito.when(this.restServiceMock.get(anyString(), + any(), any(WebClient.class))).thenReturn(district); + // then + Mockito.when(this.districtRedisRepository.save(districtEntity)).thenReturn(districtEntity); + Mockito.when(this.districtRedisRepository.findById(districtEntity.getDistrictId())).thenReturn(Optional.of(districtEntity)); + // mock return of district from redis cache and compare + districtService.updateDistrictCache(districtEntity.getDistrictId()); + assertTrue(this.districtRedisRepository.findById(districtEntity.getDistrictId()).isPresent()); + } + + private DistrictEntity createDistrictEntity() { + District d = TestUtils.createDistrict(); + DistrictEntity de = this.districtTransformer.transformToEntity(d); + return de; + } } diff --git a/api/src/test/java/ca/bc/gov/educ/api/trax/service/institute/intstituteSchoolServiceTestToo.java b/api/src/test/java/ca/bc/gov/educ/api/trax/service/institute/intstituteSchoolServiceTestToo.java new file mode 100644 index 00000000..56f437a5 --- /dev/null +++ b/api/src/test/java/ca/bc/gov/educ/api/trax/service/institute/intstituteSchoolServiceTestToo.java @@ -0,0 +1,163 @@ +package ca.bc.gov.educ.api.trax.service.institute; + +import ca.bc.gov.educ.api.trax.messaging.NatsConnection; +import ca.bc.gov.educ.api.trax.messaging.jetstream.Publisher; +import ca.bc.gov.educ.api.trax.messaging.jetstream.Subscriber; +import ca.bc.gov.educ.api.trax.model.dto.ResponseObj; +import ca.bc.gov.educ.api.trax.model.dto.institute.School; +import ca.bc.gov.educ.api.trax.model.dto.institute.SchoolDetail; +import ca.bc.gov.educ.api.trax.model.entity.institute.SchoolDetailEntity; +import ca.bc.gov.educ.api.trax.model.entity.institute.SchoolEntity; +import ca.bc.gov.educ.api.trax.model.transformer.institute.SchoolDetailTransformer; +import ca.bc.gov.educ.api.trax.model.transformer.institute.SchoolTransformer; +import ca.bc.gov.educ.api.trax.repository.redis.SchoolDetailRedisRepository; +import ca.bc.gov.educ.api.trax.repository.redis.SchoolRedisRepository; +import ca.bc.gov.educ.api.trax.service.RESTService; +import ca.bc.gov.educ.api.trax.support.TestUtils; +import ca.bc.gov.educ.api.trax.util.EducGradTraxApiConstants; +import ca.bc.gov.educ.api.trax.util.RestUtils; +import org.junit.Assert; +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; +import redis.clients.jedis.JedisCluster; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.when; + +@RunWith(SpringRunner.class) +@SpringBootTest +@ActiveProfiles("test") +@ExtendWith(MockitoExtension.class) +public class intstituteSchoolServiceTestToo { + + @Autowired + private EducGradTraxApiConstants constants; + @MockBean + private SchoolService schoolService; + @MockBean + private SchoolRedisRepository schoolRedisRepository; + @MockBean + private JedisConnectionFactory jedisConnectionFactoryMock; + @MockBean + private JedisCluster jedisClusterMock; + @MockBean + @Qualifier("default") + WebClient webClientMock; + @Mock + private WebClient.RequestHeadersSpec requestHeadersSpecMock; + @Mock + private WebClient.RequestHeadersUriSpec requestHeadersUriSpecMock; + @Mock + private WebClient.ResponseSpec responseSpecMock; + @Mock + private HttpHeaders httpHeadersMock; + @Mock + private ResponseObj responseObjectMock; + @Mock + private Mono> schoolEntitiesMock; + @Mock + private Mono> schoolDetailEntitiesMock; + @Mock + private List schoolsMock; + @Mock + private List schoolDetailsMock; + @MockBean + private RestUtils restUtils; + @MockBean + private SchoolTransformer schoolTransformerMock; + @Autowired + private SchoolDetailTransformer schoolDetailTransformer; + @MockBean + private RESTService restServiceMock; + @MockBean + private SchoolDetailRedisRepository schoolDetailRedisRepository; + // NATS + @MockBean + private NatsConnection natsConnection; + + @MockBean + private Publisher publisher; + + @MockBean + private Subscriber subscriber; + + @TestConfiguration + static class TestConfigInstitute { + @Bean + public ClientRegistrationRepository clientRegistrationRepository() { + return new ClientRegistrationRepository() { + @Override + public ClientRegistration findByRegistrationId(String registrationId) { + return null; + } + }; + } + } + + @Test + public void updateSchoolCache_givenValidShcoolId_shouldUpdate() { + SchoolDetail schoolDetail = TestUtils.createSchoolDetail(); + SchoolDetailEntity schoolDetailEntity = schoolDetailTransformer.transformToEntity(schoolDetail); + Optional schoolDetailEntityOptional = Optional.of(schoolDetailEntity); + when(this.restServiceMock.get(String.format(constants.getSchoolDetailsByIdFromInstituteApiUrl(), schoolDetail.getSchoolId()), + SchoolDetail.class, this.webClientMock)).thenReturn(schoolDetail); + when(this.schoolDetailRedisRepository.findById(schoolDetail.getSchoolId())).thenReturn(schoolDetailEntityOptional); + doNothing().when(this.schoolService).updateSchoolCache(schoolDetail.getSchoolId()); + this.schoolService.updateSchoolCache(schoolDetail.getSchoolId()); + Optional response = this.schoolDetailRedisRepository.findById(schoolDetail.getSchoolId()); + if (response.isPresent()) { + Assert.assertEquals(response.get().getSchoolId(), schoolDetail.getSchoolId()); + } else { + Assert.fail(); + } + } + + @Test + public void updateSchoolCache_givenValidShcoolIdList_shouldUpdate() { + SchoolDetail schoolDetail1 = TestUtils.createSchoolDetail(); + SchoolDetail schoolDetail2 = TestUtils.createSchoolDetail(); + SchoolDetailEntity schoolDetailEntity1 = schoolDetailTransformer.transformToEntity(schoolDetail1); + SchoolDetailEntity schoolDetailEntity2 = schoolDetailTransformer.transformToEntity(schoolDetail2); + Optional schoolDetailEntityOptional1 = Optional.of(schoolDetailEntity1); + Optional schoolDetailEntityOptional2 = Optional.of(schoolDetailEntity2); + when(this.restServiceMock.get(String.format(constants.getSchoolDetailsByIdFromInstituteApiUrl(), schoolDetail1.getSchoolId()), + SchoolDetail.class, this.webClientMock)).thenReturn(schoolDetail1); + when(this.restServiceMock.get(String.format(constants.getSchoolDetailsByIdFromInstituteApiUrl(), schoolDetail2.getSchoolId()), + SchoolDetail.class, this.webClientMock)).thenReturn(schoolDetail2); + when(this.schoolDetailRedisRepository.findById(schoolDetail1.getSchoolId())).thenReturn(schoolDetailEntityOptional1); + when(this.schoolDetailRedisRepository.findById(schoolDetail2.getSchoolId())).thenReturn(schoolDetailEntityOptional2); + List schoolIds = Arrays.asList(schoolDetail1.getSchoolId(), schoolDetail2.getSchoolId()); + doNothing().when(this.schoolService).updateSchoolCache(schoolIds); + this.schoolService.updateSchoolCache(schoolIds); + Optional response1 = this.schoolDetailRedisRepository.findById(schoolDetail1.getSchoolId()); + Optional response2 = this.schoolDetailRedisRepository.findById(schoolDetail2.getSchoolId()); + if (response1.isPresent() && response2.isPresent()) { + Assert.assertTrue(response1.get().getSchoolId() == schoolDetail1.getSchoolId() && response2.get().getSchoolId() == schoolDetail2.getSchoolId()); + } else { + Assert.fail(); + } + + } + +} diff --git a/api/src/test/java/ca/bc/gov/educ/api/trax/support/TestUtils.java b/api/src/test/java/ca/bc/gov/educ/api/trax/support/TestUtils.java index e2a7b93e..cde06d6d 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/trax/support/TestUtils.java +++ b/api/src/test/java/ca/bc/gov/educ/api/trax/support/TestUtils.java @@ -4,7 +4,8 @@ import ca.bc.gov.educ.api.trax.model.dto.DistrictContact; import ca.bc.gov.educ.api.trax.model.dto.GradStatusEventPayloadDTO; import ca.bc.gov.educ.api.trax.model.dto.SchoolContact; -import ca.bc.gov.educ.api.trax.model.entity.Event; +import ca.bc.gov.educ.api.trax.model.dto.institute.*; +import ca.bc.gov.educ.api.trax.model.entity.EventEntity; import ca.bc.gov.educ.api.trax.model.entity.TraxStudentEntity; import ca.bc.gov.educ.api.trax.repository.EventRepository; import ca.bc.gov.educ.api.trax.util.JsonUtil; @@ -14,6 +15,7 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; +import java.util.Arrays; import java.util.UUID; import static ca.bc.gov.educ.api.trax.util.EducGradTraxApiConstants.DEFAULT_CREATED_BY; @@ -36,8 +38,8 @@ public static GradStatusEventPayloadDTO createGraduationStatus(boolean isGraduat return graduationStatus; } - public static Event createEvent(String eventType, Object payload, EventRepository eventRepository) throws JsonProcessingException { - var event = Event.builder() + public static EventEntity createEvent(String eventType, Object payload, EventRepository eventRepository) throws JsonProcessingException { + var event = EventEntity.builder() .eventType(eventType) .eventId(UUID.randomUUID()) .eventOutcome("DB_COMMITTED") @@ -93,6 +95,37 @@ public static SchoolContact createSchoolContact() { return contact; } + public static School createSchool() { + var school = new School(); + school.setSchoolId(UUID.randomUUID().toString()); + school.setDistrictId(UUID.randomUUID().toString()); + school.setMincode("07996006"); + school.setIndependentAuthorityId(UUID.randomUUID().toString()); + school.setSchoolNumber("96006"); + school.setFaxNumber("2507436200"); + school.setPhoneNumber("2507435516"); + school.setEmail("executiveoffice@shawnigan.ca"); + school.setWebsite(null); + school.setDisplayName("Shawnigan Lake"); + school.setDisplayNameNoSpecialChars("Shawnigan Lake"); + school.setSchoolReportingRequirementCode("REGULAR"); + school.setSchoolOrganizationCode("QUARTER"); + school.setSchoolCategoryCode("INDEPEND"); + school.setFacilityTypeCode("STANDARD"); + school.setOpenedDate("1989-09-01T00:00:00"); + school.setCanIssueCertificates(true); + school.setCanIssueTranscripts(true); + return school; + } + + public static MoveSchoolData createMoveSchoolData() { + var move = new MoveSchoolData(); + move.setToSchool(createSchool()); + move.setMoveDate(LocalDateTime.now().toString()); + move.setFromSchoolId(UUID.randomUUID().toString()); + return move; + } + public static DistrictContact createDistrictContact() { var contact = new DistrictContact(); contact.setDistrictId(UUID.randomUUID().toString()); @@ -182,4 +215,43 @@ public static TraxStudentEntity createTraxStudent(String program, String student } return traxStudent; } + + public static District createDistrict() { + District district = new District(); + district.setDistrictId(UUID.randomUUID().toString()); + district.setDistrictNumber("002"); + district.setFaxNumber("1233216547"); + district.setPhoneNumber("3216549874"); + district.setEmail("district@district.ca"); + district.setWebsite("www.district.ca"); + district.setDisplayName("Test Display Name"); + district.setDistrictRegionCode("NOT_APPLIC"); + district.setDistrictStatusCode("INACTIVE"); + return district; + } + + public static SchoolDetail createSchoolDetail(){ + String schoolId = UUID.randomUUID().toString(); + SchoolAddress schoolAddress = new SchoolAddress(); + schoolAddress.setSchoolId(schoolId); + schoolAddress.setAddressLine1("123 Fake St"); + schoolAddress.setCity("Vancouverland"); + schoolAddress.setCountryCode("CAN"); + schoolAddress.setPostal("VQV2L2"); + SchoolDetail schoolDetail = new SchoolDetail(); + schoolDetail.setSchoolId(schoolId); + schoolDetail.setSchoolNumber("96006"); + schoolDetail.setDistrictId(UUID.randomUUID().toString()); + schoolDetail.setAddresses(Arrays.asList(schoolAddress)); + schoolDetail.setCreateDate(LocalDateTime.now().toString()); + schoolDetail.setCanIssueCertificates(true); + schoolDetail.setDisplayName("Blah"); + schoolDetail.setCreateUser("Test"); + schoolDetail.setUpdateDate(LocalDateTime.now().toString()); + schoolDetail.setUpdateUser("Test"); + schoolDetail.setCreateDate(LocalDateTime.now().toString()); + schoolDetail.setCanIssueTranscripts(true); + schoolDetail.setDisplayNameNoSpecialChars("blah blah"); + return schoolDetail; + } } diff --git a/api/src/test/java/ca/bc/gov/educ/api/trax/util/ReplicationTestUtils.java b/api/src/test/java/ca/bc/gov/educ/api/trax/util/ReplicationTestUtils.java index a6cd346f..dac2cecb 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/trax/util/ReplicationTestUtils.java +++ b/api/src/test/java/ca/bc/gov/educ/api/trax/util/ReplicationTestUtils.java @@ -1,5 +1,6 @@ package ca.bc.gov.educ.api.trax.util; +import ca.bc.gov.educ.api.trax.repository.EventHistoryRepository; import ca.bc.gov.educ.api.trax.repository.EventRepository; import lombok.Getter; import org.springframework.beans.factory.annotation.Autowired; @@ -14,14 +15,17 @@ public class ReplicationTestUtils { private final EventRepository eventRepository; + private final EventHistoryRepository eventHistoryRepository; @Autowired - public ReplicationTestUtils(EventRepository eventRepository) { + public ReplicationTestUtils(EventRepository eventRepository, EventHistoryRepository eventHistoryRepository) { this.eventRepository = eventRepository; + this.eventHistoryRepository = eventHistoryRepository; } @Transactional(propagation = Propagation.REQUIRES_NEW) public void cleanDB() { + this.eventHistoryRepository.deleteAll(); this.eventRepository.deleteAll(); } } diff --git a/api/src/test/resources/application.yaml b/api/src/test/resources/application.yaml index 1e0bdd5e..9bffa622 100644 --- a/api/src/test/resources/application.yaml +++ b/api/src/test/resources/application.yaml @@ -130,6 +130,8 @@ endpoint: url: https://school-api-75e61b-dev.apps.silver.devops.gov.bc.ca/api/v1/institute/school/%s get-all-districts: url: https://school-api-75e61b-dev.apps.silver.devops.gov.bc.ca/api/v1/institute/district + get-district: + url: https://school-api-75e61b-dev.apps.silver.devops.gov.bc.ca/api/v1/institute/district/%s get-all-school-category-codes: url: https://school-api-75e61b-dev.apps.silver.devops.gov.bc.ca/api/v1/institute/category-codes get-all-school-funding-group-codes: