From 39f8337f4445b452632fb74cacca14d1a1d154bc Mon Sep 17 00:00:00 2001 From: Marco Villeneuve Date: Tue, 24 Oct 2023 17:06:21 -0700 Subject: [PATCH 1/3] First style of change --- .../CreateSchoolOrchestrator.java | 90 ++++++++++++------- .../SchoolUserActivationBaseOrchestrator.java | 1 + .../v1/CreateSchoolOrchestratorService.java | 41 --------- ...erActivationInviteOrchestratorService.java | 50 ++++++++--- .../edx/struct/v1/CreateSchoolSagaData.java | 1 + 5 files changed, 98 insertions(+), 85 deletions(-) diff --git a/api/src/main/java/ca/bc/gov/educ/api/edx/orchestrator/CreateSchoolOrchestrator.java b/api/src/main/java/ca/bc/gov/educ/api/edx/orchestrator/CreateSchoolOrchestrator.java index 94202915..39815619 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/edx/orchestrator/CreateSchoolOrchestrator.java +++ b/api/src/main/java/ca/bc/gov/educ/api/edx/orchestrator/CreateSchoolOrchestrator.java @@ -1,22 +1,20 @@ package ca.bc.gov.educ.api.edx.orchestrator; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.INITIAL_USER_FOUND; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.INITIAL_USER_INVITED; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.NO_INITIAL_USER_FOUND; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.PRIMARY_ACTIVATION_CODE_SENT; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.SCHOOL_CREATED; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.SCHOOL_PRIMARY_CODE_CREATED; -import static ca.bc.gov.educ.api.edx.constants.EventType.CREATE_SCHOOL; -import static ca.bc.gov.educ.api.edx.constants.EventType.CREATE_SCHOOL_PRIMARY_CODE; -import static ca.bc.gov.educ.api.edx.constants.EventType.INVITE_INITIAL_USER; -import static ca.bc.gov.educ.api.edx.constants.EventType.ONBOARD_INITIAL_USER; -import static ca.bc.gov.educ.api.edx.constants.EventType.SEND_PRIMARY_ACTIVATION_CODE; +import static ca.bc.gov.educ.api.edx.constants.EventOutcome.*; +import static ca.bc.gov.educ.api.edx.constants.EventType.*; +import static ca.bc.gov.educ.api.edx.constants.SagaEnum.EDX_SCHOOL_USER_ACTIVATION_INVITE_SAGA; import static ca.bc.gov.educ.api.edx.constants.SagaStatusEnum.IN_PROGRESS; import static ca.bc.gov.educ.api.edx.constants.SagaEnum.CREATE_NEW_SCHOOL_SAGA; import static ca.bc.gov.educ.api.edx.constants.TopicsEnum.EDX_API_TOPIC; import static ca.bc.gov.educ.api.edx.constants.TopicsEnum.INSTITUTE_API_TOPIC; import static lombok.AccessLevel.PRIVATE; +import ca.bc.gov.educ.api.edx.constants.SagaStatusEnum; +import ca.bc.gov.educ.api.edx.model.v1.EdxActivationCodeEntity; +import ca.bc.gov.educ.api.edx.orchestrator.base.BaseOrchestrator; +import ca.bc.gov.educ.api.edx.service.v1.EdxSchoolUserActivationInviteOrchestratorService; +import ca.bc.gov.educ.api.edx.struct.v1.*; +import ca.bc.gov.educ.api.edx.utils.RequestUtil; import org.springframework.stereotype.Component; import com.fasterxml.jackson.core.JsonProcessingException; @@ -25,17 +23,16 @@ import ca.bc.gov.educ.api.edx.messaging.jetstream.Publisher; import ca.bc.gov.educ.api.edx.model.v1.SagaEntity; import ca.bc.gov.educ.api.edx.model.v1.SagaEventStatesEntity; -import ca.bc.gov.educ.api.edx.orchestrator.base.BaseOrchestrator; import ca.bc.gov.educ.api.edx.service.v1.CreateSchoolOrchestratorService; import ca.bc.gov.educ.api.edx.service.v1.EdxUsersService; import ca.bc.gov.educ.api.edx.service.v1.SagaService; -import ca.bc.gov.educ.api.edx.struct.v1.CreateSchoolSagaData; -import ca.bc.gov.educ.api.edx.struct.v1.Event; -import ca.bc.gov.educ.api.edx.struct.v1.School; import ca.bc.gov.educ.api.edx.utils.JsonUtil; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import java.util.List; +import java.util.UUID; + @Component @Slf4j public class CreateSchoolOrchestrator extends BaseOrchestrator { @@ -48,13 +45,15 @@ public class CreateSchoolOrchestrator extends BaseOrchestrator roles = List.of("EDX_SCHOOL_ADMIN"); - List statusFilters = List.of(SagaStatusEnum.IN_PROGRESS.toString(), SagaStatusEnum.STARTED.toString()); - String sagaName = EDX_SCHOOL_USER_ACTIVATION_INVITE_SAGA.toString(); - - inviteSagaData.setSchoolID(UUID.fromString(school.getSchoolId())); - inviteSagaData.setSchoolName(school.getDisplayName()); - inviteSagaData.setFirstName(user.getFirstName()); - inviteSagaData.setLastName(user.getLastName()); - inviteSagaData.setEmail(user.getEmail()); - inviteSagaData.setEdxActivationRoleCodes(roles); - RequestUtil.setAuditColumnsForCreate(inviteSagaData); - - final Optional sagaInProgress = sagaService. - findAllActiveUserActivationInviteSagasBySchoolIDAndEmailId( - inviteSagaData.getSchoolID(), - inviteSagaData.getEmail(), - sagaName, - statusFilters); - - if (sagaInProgress.isEmpty()) { - try { - SagaEntity sagaEntity = SAGA_DATA_MAPPER.toModel(String.valueOf(sagaName), inviteSagaData); - final SagaEntity saga = this.activationInviteOrchestrator.createSaga(sagaEntity); - this.activationInviteOrchestrator.startSaga(saga); - } catch (JsonProcessingException e) { - throw new SagaRuntimeException(e); - } - } - } - public EdxActivationCodeEntity findPrimaryCode(String schoolId) { return this.service.findPrimaryEdxActivationCode(SCHOOL, schoolId); } diff --git a/api/src/main/java/ca/bc/gov/educ/api/edx/service/v1/EdxSchoolUserActivationInviteOrchestratorService.java b/api/src/main/java/ca/bc/gov/educ/api/edx/service/v1/EdxSchoolUserActivationInviteOrchestratorService.java index bd713187..cdf3e300 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/edx/service/v1/EdxSchoolUserActivationInviteOrchestratorService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/edx/service/v1/EdxSchoolUserActivationInviteOrchestratorService.java @@ -9,6 +9,7 @@ import ca.bc.gov.educ.api.edx.props.EmailProperties; import ca.bc.gov.educ.api.edx.repository.EdxActivationCodeRepository; import ca.bc.gov.educ.api.edx.repository.EdxUserRepository; +import ca.bc.gov.educ.api.edx.struct.v1.CreateSchoolSagaData; import ca.bc.gov.educ.api.edx.struct.v1.EdxActivationCode; import ca.bc.gov.educ.api.edx.struct.v1.EdxUserSchoolActivationInviteSagaData; import ca.bc.gov.educ.api.edx.struct.v1.EmailNotification; @@ -63,33 +64,63 @@ public EdxSchoolUserActivationInviteOrchestratorService(EdxActivationCodeReposit this.sagaService = sagaService; } - @Transactional(propagation = Propagation.REQUIRES_NEW) - public void createPersonalActivationCodeAndUpdateSagaData(EdxUserSchoolActivationInviteSagaData edxUserActivationInviteSagaData, SagaEntity sagaEntity) { + private EdxActivationCodeEntity createActivationCode(EdxUserSchoolActivationInviteSagaData edxUserActivationInviteSagaData){ EdxActivationCode edxActivationCode = EDX_USER_ACTIVATION_INVITE_SAGA_DATA_MAPPER.toEdxActivationCode(edxUserActivationInviteSagaData); RequestUtil.setAuditColumnsForCreate(edxActivationCode); if (!CollectionUtils.isEmpty(edxActivationCode.getEdxActivationRoles())) { edxActivationCode.getEdxActivationRoles().forEach(RequestUtil::setAuditColumnsForCreate); } try { - EdxActivationCodeEntity personalActivationCodeEntity = getEdxUsersService().createPersonalEdxActivationCode(EDX_ACTIVATION_CODE_MAPPER.toModel(edxActivationCode)); - updateSagaDataInternal(edxUserActivationInviteSagaData, personalActivationCodeEntity, sagaEntity); - } catch (NoSuchAlgorithmException | JsonProcessingException e) { + return getEdxUsersService().createPersonalEdxActivationCode(EDX_ACTIVATION_CODE_MAPPER.toModel(edxActivationCode)); + } catch (NoSuchAlgorithmException e) { + throw new SagaRuntimeException(e); + } + } + + private void saveSagaPayload(SagaEntity sagaEntity, Object object) throws JsonProcessingException { + sagaEntity.setPayload(JsonUtil.getJsonStringFromObject(object)); // update the payload which will be updated in DB. + this.sagaService.updateSagaRecord(sagaEntity); + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void createPersonalActivationCodeAndUpdateSagaData(EdxUserSchoolActivationInviteSagaData edxUserActivationInviteSagaData, SagaEntity sagaEntity) { + try { + EdxActivationCodeEntity personalActivationCodeEntity = createActivationCode(edxUserActivationInviteSagaData); + updateSagaDataInternal(edxUserActivationInviteSagaData, personalActivationCodeEntity); + saveSagaPayload(sagaEntity, edxUserActivationInviteSagaData); + } catch (JsonProcessingException e) { + throw new SagaRuntimeException(e); + } + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) + public void createPersonalActivationCodeAndUpdateSagaData(CreateSchoolSagaData createSchoolSagaData, SagaEntity sagaEntity) { + try { + EdxActivationCodeEntity personalActivationCodeEntity = createActivationCode(createSchoolSagaData.getInviteSagaData()); + updateSagaDataInternal(createSchoolSagaData.getInviteSagaData(), personalActivationCodeEntity); + saveSagaPayload(sagaEntity, createSchoolSagaData); + } catch (JsonProcessingException e) { throw new SagaRuntimeException(e); } } + @Transactional(propagation = Propagation.REQUIRES_NEW) // this makes sure it is done in a new transaction when used through proxy, so call on line#84 won't have a new transaction. + public void updateSagaData(CreateSchoolSagaData createSchoolSagaData, EdxActivationCodeEntity personalActivationCodeEntity, SagaEntity sagaEntity) throws JsonProcessingException { + updateSagaDataInternal(createSchoolSagaData.getInviteSagaData(), personalActivationCodeEntity); + saveSagaPayload(sagaEntity, createSchoolSagaData); + } + @Transactional(propagation = Propagation.REQUIRES_NEW) // this makes sure it is done in a new transaction when used through proxy, so call on line#84 won't have a new transaction. public void updateSagaData(EdxUserSchoolActivationInviteSagaData edxUserActivationInviteSagaData, EdxActivationCodeEntity personalActivationCodeEntity, SagaEntity sagaEntity) throws JsonProcessingException { - updateSagaDataInternal(edxUserActivationInviteSagaData, personalActivationCodeEntity, sagaEntity); + updateSagaDataInternal(edxUserActivationInviteSagaData, personalActivationCodeEntity); + saveSagaPayload(sagaEntity, edxUserActivationInviteSagaData); } - private void updateSagaDataInternal(EdxUserSchoolActivationInviteSagaData edxUserActivationInviteSagaData, EdxActivationCodeEntity personalActivationCodeEntity, SagaEntity sagaEntity) throws JsonProcessingException { + private void updateSagaDataInternal(EdxUserSchoolActivationInviteSagaData edxUserActivationInviteSagaData, EdxActivationCodeEntity personalActivationCodeEntity) { edxUserActivationInviteSagaData.setEdxActivationCodeId(personalActivationCodeEntity.getEdxActivationCodeId().toString()); edxUserActivationInviteSagaData.setValidationCode(personalActivationCodeEntity.getValidationCode().toString()); edxUserActivationInviteSagaData.setExpiryDate(personalActivationCodeEntity.getExpiryDate()); edxUserActivationInviteSagaData.setPersonalActivationCode(personalActivationCodeEntity.getActivationCode()); - sagaEntity.setPayload(JsonUtil.getJsonStringFromObject(edxUserActivationInviteSagaData)); // update the payload which will be updated in DB. - this.sagaService.updateSagaRecord(sagaEntity); // save updated payload to DB again. } @Transactional(propagation = Propagation.REQUIRES_NEW) @@ -106,7 +137,6 @@ public void sendEmail(EdxUserSchoolActivationInviteSagaData edxUserActivationInv .build(); this.getEmailNotificationService().sendEmail(emailNotification); - } private String createUserActivationLink(EdxUserSchoolActivationInviteSagaData edxUserActivationInviteSagaData) { diff --git a/api/src/main/java/ca/bc/gov/educ/api/edx/struct/v1/CreateSchoolSagaData.java b/api/src/main/java/ca/bc/gov/educ/api/edx/struct/v1/CreateSchoolSagaData.java index 9fd7c922..6c040f96 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/edx/struct/v1/CreateSchoolSagaData.java +++ b/api/src/main/java/ca/bc/gov/educ/api/edx/struct/v1/CreateSchoolSagaData.java @@ -15,4 +15,5 @@ public class CreateSchoolSagaData extends BaseRequest implements Serializable { private School school; private Optional initialEdxUser; + private EdxUserSchoolActivationInviteSagaData inviteSagaData; } From 57ecb09df2aaaabae0077bc194671d719322a2ab Mon Sep 17 00:00:00 2001 From: Marco Villeneuve Date: Tue, 24 Oct 2023 17:48:26 -0700 Subject: [PATCH 2/3] Extended the object --- .../CreateSchoolOrchestrator.java | 83 +++++-------------- .../v1/CreateSchoolOrchestratorService.java | 58 ++++++------- ...erActivationInviteOrchestratorService.java | 41 ++------- .../edx/struct/v1/CreateSchoolSagaData.java | 13 ++- 4 files changed, 56 insertions(+), 139 deletions(-) diff --git a/api/src/main/java/ca/bc/gov/educ/api/edx/orchestrator/CreateSchoolOrchestrator.java b/api/src/main/java/ca/bc/gov/educ/api/edx/orchestrator/CreateSchoolOrchestrator.java index 39815619..d6db79d6 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/edx/orchestrator/CreateSchoolOrchestrator.java +++ b/api/src/main/java/ca/bc/gov/educ/api/edx/orchestrator/CreateSchoolOrchestrator.java @@ -1,41 +1,33 @@ package ca.bc.gov.educ.api.edx.orchestrator; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.*; -import static ca.bc.gov.educ.api.edx.constants.EventType.*; -import static ca.bc.gov.educ.api.edx.constants.SagaEnum.EDX_SCHOOL_USER_ACTIVATION_INVITE_SAGA; -import static ca.bc.gov.educ.api.edx.constants.SagaStatusEnum.IN_PROGRESS; -import static ca.bc.gov.educ.api.edx.constants.SagaEnum.CREATE_NEW_SCHOOL_SAGA; -import static ca.bc.gov.educ.api.edx.constants.TopicsEnum.EDX_API_TOPIC; -import static ca.bc.gov.educ.api.edx.constants.TopicsEnum.INSTITUTE_API_TOPIC; -import static lombok.AccessLevel.PRIVATE; - -import ca.bc.gov.educ.api.edx.constants.SagaStatusEnum; -import ca.bc.gov.educ.api.edx.model.v1.EdxActivationCodeEntity; -import ca.bc.gov.educ.api.edx.orchestrator.base.BaseOrchestrator; -import ca.bc.gov.educ.api.edx.service.v1.EdxSchoolUserActivationInviteOrchestratorService; -import ca.bc.gov.educ.api.edx.struct.v1.*; -import ca.bc.gov.educ.api.edx.utils.RequestUtil; -import org.springframework.stereotype.Component; - -import com.fasterxml.jackson.core.JsonProcessingException; - import ca.bc.gov.educ.api.edx.messaging.MessagePublisher; import ca.bc.gov.educ.api.edx.messaging.jetstream.Publisher; import ca.bc.gov.educ.api.edx.model.v1.SagaEntity; import ca.bc.gov.educ.api.edx.model.v1.SagaEventStatesEntity; import ca.bc.gov.educ.api.edx.service.v1.CreateSchoolOrchestratorService; +import ca.bc.gov.educ.api.edx.service.v1.EdxSchoolUserActivationInviteOrchestratorService; import ca.bc.gov.educ.api.edx.service.v1.EdxUsersService; import ca.bc.gov.educ.api.edx.service.v1.SagaService; +import ca.bc.gov.educ.api.edx.struct.v1.CreateSchoolSagaData; +import ca.bc.gov.educ.api.edx.struct.v1.Event; +import ca.bc.gov.educ.api.edx.struct.v1.School; import ca.bc.gov.educ.api.edx.utils.JsonUtil; +import com.fasterxml.jackson.core.JsonProcessingException; import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; -import java.util.List; -import java.util.UUID; +import static ca.bc.gov.educ.api.edx.constants.EventOutcome.*; +import static ca.bc.gov.educ.api.edx.constants.EventType.*; +import static ca.bc.gov.educ.api.edx.constants.SagaEnum.CREATE_NEW_SCHOOL_SAGA; +import static ca.bc.gov.educ.api.edx.constants.SagaStatusEnum.IN_PROGRESS; +import static ca.bc.gov.educ.api.edx.constants.TopicsEnum.EDX_API_TOPIC; +import static ca.bc.gov.educ.api.edx.constants.TopicsEnum.INSTITUTE_API_TOPIC; +import static lombok.AccessLevel.PRIVATE; @Component @Slf4j -public class CreateSchoolOrchestrator extends BaseOrchestrator { +public class CreateSchoolOrchestrator extends SchoolUserActivationBaseOrchestrator { @Getter(PRIVATE) private final Publisher publisher; @@ -49,7 +41,7 @@ public class CreateSchoolOrchestrator extends BaseOrchestrator result = this.restUtils.getSchoolById(saga.getSagaId(), schoolId); @@ -76,9 +64,17 @@ public void attachInstituteSchoolToSaga(String schoolId, SagaEntity saga) School school = result.get(0); saga.setSchoolID(UUID.fromString(school.getSchoolId())); - CreateSchoolSagaData payload = JsonUtil.getJsonObjectFromString(CreateSchoolSagaData.class, saga.getPayload()); - payload.setSchool(school); - saga.setPayload(JsonUtil.getJsonStringFromObject(payload)); + createSchoolSagaData.setSchool(school); + EdxUser user = createSchoolSagaData.getInitialEdxUser(); + List roles = List.of("EDX_SCHOOL_ADMIN"); + createSchoolSagaData.setSchoolID(UUID.fromString(school.getSchoolId())); + createSchoolSagaData.setSchoolName(school.getDisplayName()); + createSchoolSagaData.setFirstName(user.getFirstName()); + createSchoolSagaData.setLastName(user.getLastName()); + createSchoolSagaData.setEmail(user.getEmail()); + createSchoolSagaData.setEdxActivationRoleCodes(roles); + + saga.setPayload(JsonUtil.getJsonStringFromObject(createSchoolSagaData)); this.sagaService.updateAttachedEntityDuringSagaProcess(saga); } @@ -94,7 +90,7 @@ public void createPrimaryActivationCode(CreateSchoolSagaData sagaData) { @Transactional(propagation = Propagation.REQUIRES_NEW) public void sendPrimaryActivationCodeNotification(CreateSchoolSagaData sagaData) { - EdxUser user = sagaData.getInitialEdxUser().orElseThrow(); + EdxUser user = sagaData.getInitialEdxUser(); School school = sagaData.getSchool(); UUID schoolId = UUID.fromString(school.getSchoolId()); @@ -118,8 +114,4 @@ public void sendPrimaryActivationCodeNotification(CreateSchoolSagaData sagaData) this.emailNotificationService.sendEmail(emailNotification); } - public EdxActivationCodeEntity findPrimaryCode(String schoolId) { - return this.service.findPrimaryEdxActivationCode(SCHOOL, schoolId); - } - } diff --git a/api/src/main/java/ca/bc/gov/educ/api/edx/service/v1/EdxSchoolUserActivationInviteOrchestratorService.java b/api/src/main/java/ca/bc/gov/educ/api/edx/service/v1/EdxSchoolUserActivationInviteOrchestratorService.java index cdf3e300..1150d020 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/edx/service/v1/EdxSchoolUserActivationInviteOrchestratorService.java +++ b/api/src/main/java/ca/bc/gov/educ/api/edx/service/v1/EdxSchoolUserActivationInviteOrchestratorService.java @@ -9,7 +9,6 @@ import ca.bc.gov.educ.api.edx.props.EmailProperties; import ca.bc.gov.educ.api.edx.repository.EdxActivationCodeRepository; import ca.bc.gov.educ.api.edx.repository.EdxUserRepository; -import ca.bc.gov.educ.api.edx.struct.v1.CreateSchoolSagaData; import ca.bc.gov.educ.api.edx.struct.v1.EdxActivationCode; import ca.bc.gov.educ.api.edx.struct.v1.EdxUserSchoolActivationInviteSagaData; import ca.bc.gov.educ.api.edx.struct.v1.EmailNotification; @@ -77,50 +76,24 @@ private EdxActivationCodeEntity createActivationCode(EdxUserSchoolActivationInvi } } - private void saveSagaPayload(SagaEntity sagaEntity, Object object) throws JsonProcessingException { - sagaEntity.setPayload(JsonUtil.getJsonStringFromObject(object)); // update the payload which will be updated in DB. - this.sagaService.updateSagaRecord(sagaEntity); - } - - @Transactional(propagation = Propagation.REQUIRES_NEW) - public void createPersonalActivationCodeAndUpdateSagaData(EdxUserSchoolActivationInviteSagaData edxUserActivationInviteSagaData, SagaEntity sagaEntity) { - try { - EdxActivationCodeEntity personalActivationCodeEntity = createActivationCode(edxUserActivationInviteSagaData); - updateSagaDataInternal(edxUserActivationInviteSagaData, personalActivationCodeEntity); - saveSagaPayload(sagaEntity, edxUserActivationInviteSagaData); - } catch (JsonProcessingException e) { - throw new SagaRuntimeException(e); - } - } - @Transactional(propagation = Propagation.REQUIRES_NEW) - public void createPersonalActivationCodeAndUpdateSagaData(CreateSchoolSagaData createSchoolSagaData, SagaEntity sagaEntity) { - try { - EdxActivationCodeEntity personalActivationCodeEntity = createActivationCode(createSchoolSagaData.getInviteSagaData()); - updateSagaDataInternal(createSchoolSagaData.getInviteSagaData(), personalActivationCodeEntity); - saveSagaPayload(sagaEntity, createSchoolSagaData); - } catch (JsonProcessingException e) { - throw new SagaRuntimeException(e); - } - } - - @Transactional(propagation = Propagation.REQUIRES_NEW) // this makes sure it is done in a new transaction when used through proxy, so call on line#84 won't have a new transaction. - public void updateSagaData(CreateSchoolSagaData createSchoolSagaData, EdxActivationCodeEntity personalActivationCodeEntity, SagaEntity sagaEntity) throws JsonProcessingException { - updateSagaDataInternal(createSchoolSagaData.getInviteSagaData(), personalActivationCodeEntity); - saveSagaPayload(sagaEntity, createSchoolSagaData); + public void createPersonalActivationCodeAndUpdateSagaData(EdxUserSchoolActivationInviteSagaData edxUserActivationInviteSagaData, SagaEntity sagaEntity) throws JsonProcessingException { + EdxActivationCodeEntity personalActivationCodeEntity = createActivationCode(edxUserActivationInviteSagaData); + updateSagaDataInternal(edxUserActivationInviteSagaData, personalActivationCodeEntity, sagaEntity); } @Transactional(propagation = Propagation.REQUIRES_NEW) // this makes sure it is done in a new transaction when used through proxy, so call on line#84 won't have a new transaction. public void updateSagaData(EdxUserSchoolActivationInviteSagaData edxUserActivationInviteSagaData, EdxActivationCodeEntity personalActivationCodeEntity, SagaEntity sagaEntity) throws JsonProcessingException { - updateSagaDataInternal(edxUserActivationInviteSagaData, personalActivationCodeEntity); - saveSagaPayload(sagaEntity, edxUserActivationInviteSagaData); + updateSagaDataInternal(edxUserActivationInviteSagaData, personalActivationCodeEntity, sagaEntity); } - private void updateSagaDataInternal(EdxUserSchoolActivationInviteSagaData edxUserActivationInviteSagaData, EdxActivationCodeEntity personalActivationCodeEntity) { + private void updateSagaDataInternal(EdxUserSchoolActivationInviteSagaData edxUserActivationInviteSagaData, EdxActivationCodeEntity personalActivationCodeEntity, SagaEntity sagaEntity) throws JsonProcessingException { edxUserActivationInviteSagaData.setEdxActivationCodeId(personalActivationCodeEntity.getEdxActivationCodeId().toString()); edxUserActivationInviteSagaData.setValidationCode(personalActivationCodeEntity.getValidationCode().toString()); edxUserActivationInviteSagaData.setExpiryDate(personalActivationCodeEntity.getExpiryDate()); edxUserActivationInviteSagaData.setPersonalActivationCode(personalActivationCodeEntity.getActivationCode()); + sagaEntity.setPayload(JsonUtil.getJsonStringFromObject(edxUserActivationInviteSagaData)); // update the payload which will be updated in DB. + this.sagaService.updateSagaRecord(sagaEntity); } @Transactional(propagation = Propagation.REQUIRES_NEW) diff --git a/api/src/main/java/ca/bc/gov/educ/api/edx/struct/v1/CreateSchoolSagaData.java b/api/src/main/java/ca/bc/gov/educ/api/edx/struct/v1/CreateSchoolSagaData.java index 6c040f96..5a27bdec 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/edx/struct/v1/CreateSchoolSagaData.java +++ b/api/src/main/java/ca/bc/gov/educ/api/edx/struct/v1/CreateSchoolSagaData.java @@ -1,19 +1,16 @@ package ca.bc.gov.educ.api.edx.struct.v1; -import java.io.Serializable; -import java.util.Optional; - import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -import ca.bc.gov.educ.api.edx.struct.BaseRequest; import lombok.Data; import lombok.EqualsAndHashCode; +import java.io.Serializable; +import java.util.Optional; + @Data @EqualsAndHashCode(callSuper = true) @JsonIgnoreProperties(ignoreUnknown = true) -public class CreateSchoolSagaData extends BaseRequest implements Serializable { +public class CreateSchoolSagaData extends EdxUserSchoolActivationInviteSagaData implements Serializable { private School school; - private Optional initialEdxUser; - private EdxUserSchoolActivationInviteSagaData inviteSagaData; + private EdxUser initialEdxUser; } From 0f4bd230c6592591a3d14a01f2039243ebf517e2 Mon Sep 17 00:00:00 2001 From: Marco Villeneuve Date: Wed, 25 Oct 2023 11:27:02 -0700 Subject: [PATCH 3/3] Added more UT. --- .../edx/controller/v1/EdxSagaController.java | 13 +- .../api/edx/mappers/v1/SagaDataMapper.java | 2 +- .../CreateSchoolOrchestratorTest.java | 138 +++++++++--------- 3 files changed, 73 insertions(+), 80 deletions(-) diff --git a/api/src/main/java/ca/bc/gov/educ/api/edx/controller/v1/EdxSagaController.java b/api/src/main/java/ca/bc/gov/educ/api/edx/controller/v1/EdxSagaController.java index 3183c7af..a12c2737 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/edx/controller/v1/EdxSagaController.java +++ b/api/src/main/java/ca/bc/gov/educ/api/edx/controller/v1/EdxSagaController.java @@ -12,11 +12,7 @@ import ca.bc.gov.educ.api.edx.service.v1.SagaService; import ca.bc.gov.educ.api.edx.struct.v1.*; import ca.bc.gov.educ.api.edx.utils.RequestUtil; -import ca.bc.gov.educ.api.edx.validator.CreateSecureExchangeSagaPayloadValidator; -import ca.bc.gov.educ.api.edx.validator.EdxActivationCodeSagaDataPayloadValidator; -import ca.bc.gov.educ.api.edx.validator.EdxUserPayloadValidator; -import ca.bc.gov.educ.api.edx.validator.SecureExchangeCommentSagaValidator; -import ca.bc.gov.educ.api.edx.validator.SecureExchangePayloadValidator; +import ca.bc.gov.educ.api.edx.validator.*; import com.fasterxml.jackson.core.JsonProcessingException; import lombok.AccessLevel; import lombok.Getter; @@ -32,7 +28,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Optional; import java.util.function.Supplier; import static ca.bc.gov.educ.api.edx.constants.SagaEnum.*; @@ -121,9 +116,9 @@ public ResponseEntity edxDistrictUserActivationRelink(EdxUserDistrictAct @Override public ResponseEntity createSchool(CreateSchoolSagaData edxSchoolCreationSagaData) { - Optional userOptional = edxSchoolCreationSagaData.getInitialEdxUser(); - if (userOptional.isPresent()) { - validatePayload(() -> getEdxUserPayLoadValidator().validateEdxUserPayload(userOptional.get(), true)); + EdxUser edxUser = edxSchoolCreationSagaData.getInitialEdxUser(); + if (edxUser != null) { + validatePayload(() -> getEdxUserPayLoadValidator().validateEdxUserPayload(edxUser, true)); } return this.processNewSchoolSaga(CREATE_NEW_SCHOOL_SAGA, edxSchoolCreationSagaData); } diff --git a/api/src/main/java/ca/bc/gov/educ/api/edx/mappers/v1/SagaDataMapper.java b/api/src/main/java/ca/bc/gov/educ/api/edx/mappers/v1/SagaDataMapper.java index 13259d0f..01530c09 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/edx/mappers/v1/SagaDataMapper.java +++ b/api/src/main/java/ca/bc/gov/educ/api/edx/mappers/v1/SagaDataMapper.java @@ -38,7 +38,7 @@ public interface SagaDataMapper { @Mapping(target = "sagaCompensated", ignore = true) @Mapping(target = "retryCount", ignore = true) @Mapping(target = "payload", expression = "java(ca.bc.gov.educ.api.edx.utils.JsonUtil.getJsonStringFromObject(sagaData))") - @Mapping(target = "emailId", expression = "java(sagaData.getInitialEdxUser().isPresent() ? sagaData.getInitialEdxUser().get().getEmail() : \"\")") + @Mapping(target = "emailId", expression = "java(sagaData.getInitialEdxUser() != null ? sagaData.getInitialEdxUser().getEmail() : \"\")") @Mapping(target = "edxUserId", ignore = true) @Mapping(target = "schoolID", source = "sagaData.school.schoolId") @Mapping(target = "districtID", source = "sagaData.school.districtId") diff --git a/api/src/test/java/ca/bc/gov/educ/api/edx/orchestrator/CreateSchoolOrchestratorTest.java b/api/src/test/java/ca/bc/gov/educ/api/edx/orchestrator/CreateSchoolOrchestratorTest.java index e31b7efb..a66607db 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/edx/orchestrator/CreateSchoolOrchestratorTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/edx/orchestrator/CreateSchoolOrchestratorTest.java @@ -1,48 +1,6 @@ package ca.bc.gov.educ.api.edx.orchestrator; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.INITIAL_USER_FOUND; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.INITIAL_USER_INVITED; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.INITIATE_SUCCESS; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.PRIMARY_ACTIVATION_CODE_SENT; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.SAGA_COMPLETED; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.SCHOOL_CREATED; -import static ca.bc.gov.educ.api.edx.constants.EventOutcome.SCHOOL_PRIMARY_CODE_CREATED; -import static ca.bc.gov.educ.api.edx.constants.EventType.CREATE_SCHOOL; -import static ca.bc.gov.educ.api.edx.constants.EventType.CREATE_SCHOOL_PRIMARY_CODE; -import static ca.bc.gov.educ.api.edx.constants.EventType.INITIATED; -import static ca.bc.gov.educ.api.edx.constants.EventType.INVITE_INITIAL_USER; -import static ca.bc.gov.educ.api.edx.constants.EventType.MARK_SAGA_COMPLETE; -import static ca.bc.gov.educ.api.edx.constants.EventType.ONBOARD_INITIAL_USER; -import static ca.bc.gov.educ.api.edx.constants.EventType.SEND_PRIMARY_ACTIVATION_CODE; -import static ca.bc.gov.educ.api.edx.constants.SagaEnum.EDX_SCHOOL_USER_ACTIVATION_INVITE_SAGA; -import static ca.bc.gov.educ.api.edx.constants.TopicsEnum.INSTITUTE_API_TOPIC; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.atMost; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mockingDetails; -import static org.mockito.Mockito.verify; -import static org.mockito.ArgumentMatchers.any; - -import java.io.IOException; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; -import java.util.UUID; -import java.util.concurrent.TimeoutException; - -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.MockitoAnnotations; -import org.springframework.beans.factory.annotation.Autowired; - -import com.fasterxml.jackson.core.JsonProcessingException; - import ca.bc.gov.educ.api.edx.constants.SagaEnum; -import ca.bc.gov.educ.api.edx.constants.SagaStatusEnum; import ca.bc.gov.educ.api.edx.controller.BaseSagaControllerTest; import ca.bc.gov.educ.api.edx.exception.SagaRuntimeException; import ca.bc.gov.educ.api.edx.mappers.v1.SagaDataMapper; @@ -50,12 +8,7 @@ import ca.bc.gov.educ.api.edx.model.v1.EdxActivationCodeEntity; import ca.bc.gov.educ.api.edx.model.v1.SagaEntity; import ca.bc.gov.educ.api.edx.model.v1.SagaEventStatesEntity; -import ca.bc.gov.educ.api.edx.repository.EdxActivationCodeRepository; -import ca.bc.gov.educ.api.edx.repository.EdxPermissionRepository; -import ca.bc.gov.educ.api.edx.repository.EdxRoleRepository; -import ca.bc.gov.educ.api.edx.repository.EdxUserSchoolRepository; -import ca.bc.gov.educ.api.edx.repository.SagaEventStateRepository; -import ca.bc.gov.educ.api.edx.repository.SagaRepository; +import ca.bc.gov.educ.api.edx.repository.*; import ca.bc.gov.educ.api.edx.rest.RestUtils; import ca.bc.gov.educ.api.edx.service.v1.SagaService; import ca.bc.gov.educ.api.edx.struct.v1.CreateSchoolSagaData; @@ -64,6 +17,29 @@ import ca.bc.gov.educ.api.edx.struct.v1.School; import ca.bc.gov.educ.api.edx.utils.JsonUtil; import ca.bc.gov.educ.api.edx.utils.RequestUtil; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.MockitoAnnotations; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.IOException; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.TimeoutException; + +import static ca.bc.gov.educ.api.edx.constants.EventOutcome.*; +import static ca.bc.gov.educ.api.edx.constants.EventType.*; +import static ca.bc.gov.educ.api.edx.constants.TopicsEnum.INSTITUTE_API_TOPIC; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.*; class CreateSchoolOrchestratorTest extends BaseSagaControllerTest { @@ -174,14 +150,13 @@ void testCheckForIntitialUser_GivenAnInitialUser_sagaShouldOnboardUser() CreateSchoolSagaData newData = JsonUtil.getJsonObjectFromString(CreateSchoolSagaData.class, nextEvent.getEventPayload()); - assertThat(newData.getInitialEdxUser()).isPresent(); + assertThat(newData.getInitialEdxUser()).isNotNull(); assertThat(nextEvent.getEventType()).isEqualTo(ONBOARD_INITIAL_USER); assertThat(nextEvent.getEventOutcome()).isEqualTo(INITIAL_USER_FOUND); } @Test - void testCheckForIntitialUser_GivenNoIntialUser_sagaShouldBeCompleted() - throws JsonProcessingException, IOException, TimeoutException, InterruptedException { + void testCheckForInitialUser_GivenNoInitialUser_sagaShouldBeCompleted() throws IOException, TimeoutException, InterruptedException { final CreateSchoolSagaData mockData = createMockCreateSchoolSagaData(this.mockSchool); SagaEntity saga = this.saveMockSaga(mockData); @@ -201,7 +176,7 @@ void testCheckForIntitialUser_GivenNoIntialUser_sagaShouldBeCompleted() CreateSchoolSagaData onboardingSagaData = JsonUtil.getJsonObjectFromString(CreateSchoolSagaData.class, onboardingEvent.getEventPayload()); - assertThat(onboardingSagaData.getInitialEdxUser()).isNotPresent(); + assertThat(onboardingSagaData.getInitialEdxUser()).isNull(); this.orchestrator.handleEvent(onboardingEvent); @@ -215,8 +190,7 @@ void testCheckForIntitialUser_GivenNoIntialUser_sagaShouldBeCompleted() } @Test - void testCreatePrimaryCode_GivenAnInitialUserAndSchool_sagaShouldCreatePrimarySchoolCode() - throws TimeoutException, IOException, InterruptedException { + void testCreatePrimaryCode_GivenAnInitialUserAndSchool_sagaShouldCreatePrimarySchoolCode() throws TimeoutException, IOException, InterruptedException { final CreateSchoolSagaData mockData = createMockCreateSchoolSagaData(this.mockInstituteSchool); mockData.setInitialEdxUser(createMockInitialUser()); SagaEntity saga = this.saveMockSaga(mockData); @@ -242,7 +216,7 @@ void testCreatePrimaryCode_GivenAnInitialUserAndSchool_sagaShouldCreatePrimarySc edxActivationCodeRepository.findEdxActivationCodeEntitiesBySchoolIDAndIsPrimaryTrueAndDistrictIDIsNull(schooId); assertThat(codeOptional).isPresent(); - assertThat(newData.getInitialEdxUser()).isPresent(); + assertThat(newData.getInitialEdxUser()).isNotNull(); assertThat(newEvent.getEventType()).isEqualTo(CREATE_SCHOOL_PRIMARY_CODE); assertThat(newEvent.getEventOutcome()).isEqualTo(SCHOOL_PRIMARY_CODE_CREATED); } @@ -279,7 +253,7 @@ void testSendPrimaryCode_GivenAnInitialUser_School_AndPrimaryCode_sagaShouldSend } @Test - void testInviteInitialUser_GivenEventAndSaga_sagaShouldStartInviteSaga() throws IOException, InterruptedException, TimeoutException { + void testCreatePersonalCodeForUser_GivenEventAndSaga_sagaShouldGenerateCode() throws IOException, InterruptedException, TimeoutException { final CreateSchoolSagaData mockData = createMockCreateSchoolSagaData(this.mockInstituteSchool); mockData.setInitialEdxUser(createMockInitialUser()); SagaEntity saga = this.saveMockSaga(mockData); @@ -298,20 +272,44 @@ void testInviteInitialUser_GivenEventAndSaga_sagaShouldStartInviteSaga() throws .dispatchMessage(eq(this.orchestrator.getTopicToSubscribe()), this.eventCaptor.capture()); Event currentEventState = JsonUtil.getJsonObjectFromBytes(Event.class, this.eventCaptor.getValue()); - assertThat(currentEventState.getEventType()).isEqualTo(INVITE_INITIAL_USER); - assertThat(currentEventState.getEventOutcome()).isEqualTo(INITIAL_USER_INVITED); + assertThat(currentEventState.getEventType()).isEqualTo(CREATE_PERSONAL_ACTIVATION_CODE); + assertThat(currentEventState.getEventOutcome()).isEqualTo(PERSONAL_ACTIVATION_CODE_CREATED); + } + + @Test + void testSendEdxUserActivationEmail_GivenEventAndSaga_sagaShouldSendEmail() throws IOException, InterruptedException, TimeoutException { + final CreateSchoolSagaData mockData = createMockCreateSchoolSagaData(this.mockInstituteSchool); + mockData.setInitialEdxUser(createMockInitialUser()); + EdxUser user = mockData.getInitialEdxUser(); + List roles = List.of("EDX_SCHOOL_ADMIN"); + mockData.setSchoolID(UUID.fromString(mockData.getSchool().getSchoolId())); + mockData.setSchoolName(mockData.getSchool().getDisplayName()); + mockData.setFirstName(user.getFirstName()); + mockData.setLastName(user.getLastName()); + mockData.setEmail(user.getEmail()); + mockData.setEdxActivationRoleCodes(roles); + mockData.setEdxActivationCodeId(UUID.randomUUID().toString()); + mockData.setValidationCode("FEDCBA"); + mockData.setExpiryDate(LocalDateTime.now().plusDays(2)); + mockData.setPersonalActivationCode("ABCDEF"); + SagaEntity saga = this.saveMockSaga(mockData); + createRoleAndPermissionData(this.edxPermissionRepository, this.edxRoleRepository); - School school = mockData.getSchool(); - EdxUser user = mockData.getInitialEdxUser().orElseThrow(); + final int invocations = mockingDetails(this.messagePublisher).getInvocations().size(); + final Event event = Event.builder() + .eventType(CREATE_PERSONAL_ACTIVATION_CODE) + .eventOutcome(PERSONAL_ACTIVATION_CODE_CREATED) + .sagaId(saga.getSagaId()) + .eventPayload(getJsonString(mockData)) + .build(); + this.orchestrator.handleEvent(event); - final Optional sagaInProgress = this.sagaService - .findAllActiveUserActivationInviteSagasBySchoolIDAndEmailId( - UUID.fromString(school.getSchoolId()), - user.getEmail(), - EDX_SCHOOL_USER_ACTIVATION_INVITE_SAGA.toString(), - List.of(SagaStatusEnum.IN_PROGRESS.toString(), SagaStatusEnum.STARTED.toString())); + verify(this.messagePublisher, atMost(invocations + 2)) + .dispatchMessage(eq(this.orchestrator.getTopicToSubscribe()), this.eventCaptor.capture()); - assertThat(sagaInProgress).isPresent(); + Event currentEventState = JsonUtil.getJsonObjectFromBytes(Event.class, this.eventCaptor.getValue()); + assertThat(currentEventState.getEventType()).isEqualTo(SEND_EDX_SCHOOL_USER_ACTIVATION_EMAIL); + assertThat(currentEventState.getEventOutcome()).isEqualTo(EDX_SCHOOL_USER_ACTIVATION_EMAIL_SENT); } private CreateSchoolSagaData createMockCreateSchoolSagaData(School school) { @@ -319,7 +317,7 @@ private CreateSchoolSagaData createMockCreateSchoolSagaData(School school) { RequestUtil.setAuditColumnsForCreate(school); RequestUtil.setAuditColumnsForCreate(sagaData); sagaData.setSchool(school); - sagaData.setInitialEdxUser(Optional.empty()); + sagaData.setInitialEdxUser(null); return sagaData; } @@ -348,13 +346,13 @@ private School createMockSchoolFromInstitute(School school) { return updatedSchool; } - private Optional createMockInitialUser() { + private EdxUser createMockInitialUser() { EdxUser mockUser = new EdxUser(); mockUser.setEmail("test@gov.bc.ca"); mockUser.setFirstName("TestFirst"); mockUser.setLastName("TestLast"); mockUser.setDigitalIdentityID(UUID.randomUUID().toString()); - return Optional.of(mockUser); + return mockUser; } private void tearDown() {