diff --git a/.github/workflows/build.from.main.branch.deploy.to.dev.yml b/.github/workflows/build.from.main.branch.deploy.to.dev.yml index 33c1e04f..897ccd88 100644 --- a/.github/workflows/build.from.main.branch.deploy.to.dev.yml +++ b/.github/workflows/build.from.main.branch.deploy.to.dev.yml @@ -27,7 +27,7 @@ env: MAX_CPU: "500m" MIN_MEM: "1Gi" MAX_MEM: "2Gi" - MIN_REPLICAS: "3" + MIN_REPLICAS: "2" MAX_REPLICAS: "3" on: diff --git a/.github/workflows/build.from.release.branch.deploy.to.dev.yml b/.github/workflows/build.from.release.branch.deploy.to.dev.yml index d2e4cfd0..44ffffec 100644 --- a/.github/workflows/build.from.release.branch.deploy.to.dev.yml +++ b/.github/workflows/build.from.release.branch.deploy.to.dev.yml @@ -27,7 +27,7 @@ env: MAX_CPU: "500m" MIN_MEM: "1Gi" MAX_MEM: "2Gi" - MIN_REPLICAS: "3" + MIN_REPLICAS: "2" MAX_REPLICAS: "3" on: diff --git a/.github/workflows/deploy.to.dev.jinil.yaml b/.github/workflows/deploy.to.dev.jinil.yaml index 75ae69fe..76aa55c7 100644 --- a/.github/workflows/deploy.to.dev.jinil.yaml +++ b/.github/workflows/deploy.to.dev.jinil.yaml @@ -27,7 +27,7 @@ env: MAX_CPU: "500m" MIN_MEM: "1Gi" MAX_MEM: "2Gi" - MIN_REPLICAS: "3" + MIN_REPLICAS: "2" MAX_REPLICAS: "3" on: diff --git a/.github/workflows/deploy_prod.yml b/.github/workflows/deploy_prod.yml index 43d95365..55fc6cb5 100644 --- a/.github/workflows/deploy_prod.yml +++ b/.github/workflows/deploy_prod.yml @@ -23,7 +23,7 @@ env: MIN_MEM: "1Gi" MAX_MEM: "2Gi" MIN_REPLICAS: "3" - MAX_REPLICAS: "3" + MAX_REPLICAS: "4" on: # https://docs.github.com/en/actions/reference/events-that-trigger-workflows diff --git a/.github/workflows/deploy_test.yml b/.github/workflows/deploy_test.yml index bd12442e..37f1265d 100644 --- a/.github/workflows/deploy_test.yml +++ b/.github/workflows/deploy_test.yml @@ -23,7 +23,7 @@ env: MIN_MEM: "1Gi" MAX_MEM: "2Gi" MIN_REPLICAS: "3" - MAX_REPLICAS: "3" + MAX_REPLICAS: "4" on: # https://docs.github.com/en/actions/reference/events-that-trigger-workflows diff --git a/api/pom.xml b/api/pom.xml index 3c214f40..fa02a6b5 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -6,7 +6,7 @@ ca.bc.gov.educ educ-grad-data-conversion-api - 1.8.54 + 1.8.55 educ-grad-data-conversion-api Ministry of Education GRAD DATA CONVERSION API diff --git a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/config/WebSecurityConfiguration.java b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/config/WebSecurityConfiguration.java index e89cd61d..283e9824 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/config/WebSecurityConfiguration.java +++ b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/config/WebSecurityConfiguration.java @@ -16,10 +16,6 @@ @EnableWebSecurity public class WebSecurityConfiguration { - public WebSecurityConfiguration() { - SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); - } - @Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http diff --git a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/controller/JobLauncherController.java b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/controller/JobLauncherController.java index b12c8120..eb825922 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/controller/JobLauncherController.java +++ b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/controller/JobLauncherController.java @@ -26,7 +26,7 @@ import java.util.Date; @RestController -@RequestMapping(EducGradDataConversionApiConstants.GRAD_BATCH_API_ROOT_MAPPING) +@RequestMapping(EducGradDataConversionApiConstants.GRAD_DATA_CONVERSION_API_ROOT_MAPPING) @CrossOrigin @OpenAPIDefinition(info = @Info(title = "API for Data Conversion & Ongoing Updates.", description = "This API is for Reading TRAX data and Persisting GRAD data.", version = "1")) diff --git a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/controller/StudentController.java b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/controller/StudentController.java new file mode 100644 index 00000000..4a1343a4 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/controller/StudentController.java @@ -0,0 +1,59 @@ +package ca.bc.gov.educ.api.dataconversion.controller; + +import ca.bc.gov.educ.api.dataconversion.model.Student; +import ca.bc.gov.educ.api.dataconversion.service.student.StudentService; +import ca.bc.gov.educ.api.dataconversion.util.EducGradDataConversionApiConstants; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.info.Info; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping(EducGradDataConversionApiConstants.GRAD_DATA_CONVERSION_API_ROOT_MAPPING) +@CrossOrigin +@OpenAPIDefinition(info = @Info(title = "API for Adhoc Student Operations", + description = "This API is for running adhoc student operations invoking the endpoints manually.", version = "1")) +public class StudentController { + + private static final Logger logger = LoggerFactory.getLogger(StudentController.class); + + @Autowired + StudentService studentService; + + @GetMapping(EducGradDataConversionApiConstants.GRAD_STUDENT_BY_PEN_STUDENT_API) + @PreAuthorize("hasAuthority('SCOPE_READ_GRAD_STUDENT_DATA')") + @Operation(summary = "Search For Student by PEN", description = "Search for Student Demographics by PEN", tags = { "Students" }) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")}) + public Student getGradStudentByPenFromStudentAPI(@PathVariable String pen, @RequestHeader(name="Authorization") String accessToken) { + logger.debug("Get Student by PEN [Controller]"); + return studentService.getStudentByPen(pen, accessToken.replaceAll("Bearer ", "")); + } + + @DeleteMapping(EducGradDataConversionApiConstants.GRAD_STUDENT_BY_PEN_STUDENT_API) + @PreAuthorize("hasAuthority('SCOPE_DELETE_GRAD_STUDENT_DATA')") + @Operation(summary = "Delete a Student by PEN", description = "Delete a Student and all related data by PEN", tags = { "Students" }) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")}) + public void cascadeDeleteStudent(@PathVariable String pen, @RequestHeader(name="Authorization") String accessToken) { + logger.debug("Cascade Delete a Student [Controller]"); + studentService.cascadeDeleteStudent(pen, accessToken.replaceAll("Bearer ", "")); + } + + @DeleteMapping(EducGradDataConversionApiConstants.GRAD_STUDENTS_BY_PENLIST_STUDENT_API) + @PreAuthorize("hasAuthority('SCOPE_DELETE_GRAD_STUDENT_DATA')") + @Operation(summary = "Delete multiple Students by PEN", description = "Delete a list of Students and all related data by PEN", tags = { "Students" }) + @ApiResponses(value = {@ApiResponse(responseCode = "200", description = "OK")}) + public void cascadeDeleteStudents(@RequestBody List penList, @RequestHeader(name="Authorization") String accessToken) { + logger.debug("Cascade Delete a Student [Controller]"); + penList.forEach(pen -> { + studentService.cascadeDeleteStudent(pen, accessToken.replaceAll("Bearer ", "")); + }); + } +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/model/GradSearchStudent.java b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/model/GradSearchStudent.java new file mode 100644 index 00000000..0c855c9c --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/model/GradSearchStudent.java @@ -0,0 +1,52 @@ +package ca.bc.gov.educ.api.dataconversion.model; + +import com.fasterxml.jackson.annotation.JsonFormat; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +import java.sql.Date; + +@Data +@SuperBuilder +@AllArgsConstructor +@NoArgsConstructor +public class GradSearchStudent { + + private String studentID; + private String pen; + private String legalFirstName; + private String legalMiddleNames; + private String legalLastName; + private String dob; + private String sexCode; + private String genderCode; + private String studentCitizenship; + private String usualFirstName; + private String usualMiddleNames; + private String usualLastName; + private String email; + private String emailVerified; + private String deceasedDate; + private String postalCode; + private String mincode; + private String localID; + private String gradeCode; + private String gradeYear; + private String demogCode; + private String statusCode; + private String memo; + private String trueStudentID; + private String program; + private String schoolOfRecord; + private String schoolOfRecordName; + private String schoolOfRecordindependentAffiliation; + private String studentGrade; + private String studentStatus; + private String transcriptEligibility; + private String certificateEligibility; + @JsonFormat(pattern="yyyy-MM-dd") + private Date adultStartDate; + +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/model/Student.java b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/model/Student.java index 547c7e66..db8405bd 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/model/Student.java +++ b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/model/Student.java @@ -13,35 +13,35 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class Student { - String studentID; - String pen; - String legalFirstName; - String legalMiddleNames; - String legalLastName; - String dob; - String sexCode; - String genderCode; - String usualFirstName; - String usualMiddleNames; - String usualLastName; - String email; - String emailVerified; - String deceasedDate; - String postalCode; - String mincode; - String localID; - String gradeCode; - String gradeYear; - String demogCode; - String statusCode; - String memo; - String trueStudentID; - String historyActivityCode; + String studentID; + String pen; + String legalFirstName; + String legalMiddleNames; + String legalLastName; + String dob; + String sexCode; + String genderCode; + String usualFirstName; + String usualMiddleNames; + String usualLastName; + String email; + String emailVerified; + String deceasedDate; + String postalCode; + String mincode; + String localID; + String gradeCode; + String gradeYear; + String demogCode; + String statusCode; + String memo; + String trueStudentID; + String historyActivityCode; - public String createUser; - public String updateUser; - public String createDate; - public String updateDate; + public String createUser; + public String updateUser; + public String createDate; + public String updateDate; - String truePen; + String truePen; } diff --git a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/model/StudentNote.java b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/model/StudentNote.java new file mode 100644 index 00000000..c814be1b --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/model/StudentNote.java @@ -0,0 +1,17 @@ +package ca.bc.gov.educ.api.dataconversion.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.UUID; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class StudentNote extends BaseModel { + + private UUID id; + private String note; + private String studentID; +} \ No newline at end of file diff --git a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/service/student/StudentService.java b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/service/student/StudentService.java new file mode 100644 index 00000000..fe82db82 --- /dev/null +++ b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/service/student/StudentService.java @@ -0,0 +1,100 @@ +package ca.bc.gov.educ.api.dataconversion.service.student; + +import ca.bc.gov.educ.api.dataconversion.model.*; +import ca.bc.gov.educ.api.dataconversion.util.RestUtils; +import io.github.resilience4j.retry.annotation.Retry; +import jakarta.transaction.Transactional; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.reactive.function.client.WebClient; + +import java.util.List; +import java.util.UUID; + +@Service +@Slf4j +public class StudentService { + private static final Logger logger = LoggerFactory.getLogger(StudentService.class); + + private final RestUtils restUtils; + final WebClient webClient; + + @Autowired + public StudentService(RestUtils restUtils, WebClient webClient) { + this.restUtils = restUtils; + this.webClient = webClient; + } + + @Transactional + @Retry(name = "searchbypen") + public Student getStudentByPen(String pen, String accessToken) { + logger.debug("Get Student by PEN [Service]"); + + Student student; + List gradStudentList; + + try { + gradStudentList = restUtils.getStudentsByPen(pen, accessToken); + student = gradStudentList.stream().filter(s -> s.getPen().compareTo(pen) == 0).findAny().orElse(null); + } catch (Exception e) { + log.error("Failed to retrieve PEN [{}] : {} ", pen, e.getLocalizedMessage()); + return null; + } + + if (student == null) { + log.error("PEN NOT FOUND [{}]", pen); + return null; + } + return student; + } + + @Transactional + public String cascadeDeleteStudent(String pen, String accessToken) { + logger.debug("Cascade Delete a Student [Service]"); + + //GET Student by PEN + Student student = getStudentByPen(pen, accessToken); + logger.debug("After GET student"); + String studentID; + + if (student != null) { + studentID = student.getStudentID(); + logger.debug("Student ID: [{}]", studentID); + + /* + Delete All student related data ({STUDENT_API}/api/v1/student/conv/studentid/{studentID}) + This will delete student data from the following tables: + STUDENT_RECORD_NOTE, STUDENT_CAREER_PROGRAMS, STUDENT_OPTIONAL_PROGRAM_HISTORY, + STUDENT_OPTIONAL_PROGRAM, GRADUATION_STUDENT_RECORD_HISTORY, GRADUATION_STUDENT_RECORD + */ + try { + restUtils.removeAllStudentRelatedData(UUID.fromString(studentID), accessToken); + } catch (Exception e) { + logger.info("Exception thrown when trying to delete student related data from grad student API."); + } + + /* + Delete all Student certificates, transcripts and reports from API_GRAD_REPORT schema + Tables: STUDENT_CERTIFICATE, STUDENT_TRANSCRIPT and STUDENT_REPORT + */ + try { + restUtils.removeAllStudentAchievements(UUID.fromString(studentID), accessToken); + } catch (Exception e) { + logger.info("Exception thrown when trying to delete student achievements."); + } + + /* + Update TRAX_STUDENT_NO status to NULL + */ + try { + restUtils.updateTraxStudentNo(new TraxStudentNo(pen, null, null), accessToken); + } catch (Exception e) { + logger.info("Exception thrown when trying to update TraxStudentNo."); + } + } + return pen; + } +} diff --git a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/util/EducGradDataConversionApiConstants.java b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/util/EducGradDataConversionApiConstants.java index 28e9c623..76ed6d79 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/util/EducGradDataConversionApiConstants.java +++ b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/util/EducGradDataConversionApiConstants.java @@ -15,12 +15,14 @@ public class EducGradDataConversionApiConstants { public static final String CORRELATION_ID = "correlationID"; public static final String API_ROOT_MAPPING = ""; public static final String API_VERSION = "v1"; - public static final String GRAD_BATCH_API_ROOT_MAPPING = "/api/" + API_VERSION + "/data-conversion"; + public static final String GRAD_DATA_CONVERSION_API_ROOT_MAPPING = "/api/" + API_VERSION + "/data-conversion"; // Data Conversion public static final String GRAD_STUDENT_PARALLEL_DATA_CONVERSION_BATCH_JOB = "/student/parallel"; public static final String GRAD_COURSE_RESTRICTION_DATA_CONVERSION_BATCH_JOB = "/courseRestriction"; public static final String GRAD_COURSE_REQUIREMENT_DATA_CONVERSION_BATCH_JOB = "/courseRequirement"; + public static final String GRAD_STUDENT_BY_PEN_STUDENT_API = "/student/pen/{pen}"; + public static final String GRAD_STUDENTS_BY_PENLIST_STUDENT_API = "/student/penlist"; // Util public static final String PEN_UPDATES_PARALLEL_BATCH_JOB = "/penUpdates/parallel"; @@ -33,8 +35,6 @@ public class EducGradDataConversionApiConstants { public static final String DEFAULT_CREATED_BY = "DATA_CONV"; public static final String DEFAULT_UPDATED_BY = "DATA_CONV"; - public static final String DEFAULT_END_DATE_FORMAT = "20991231"; // yyyyMMdd - @Value("${authorization.user}") private String userName; @@ -101,6 +101,12 @@ public class EducGradDataConversionApiConstants { @Value("${endpoint.grad-student-api.remove-student-career-program}") private String removeStudentCareerProgram; + @Value("${endpoint.grad-student-api.get-student-by-pen}") + private String gradStudentByPenUrl; + + @Value("${endpoint.grad-student-api.get-student-notes-by-studentID}") + private String gradStudentNotesByStudentID; + @Value("${endpoint.grad-assessment-api.assessment-requirement.url}") private String addAssessmentRequirementApiUrl; @@ -155,6 +161,9 @@ public class EducGradDataConversionApiConstants { @Value("${endpoint.grad-trax-api.student.save-trax-student-no.url}") private String saveTraxStudentNoUrl; + @Value("${endpoint.grad-graduation-report-api.delete-student-achievements.url}") + private String deleteStudentAchievementsUrl; + // Incremental Grad Update @Value("${grad.update.enabled}") private boolean gradUpdateEnabled; diff --git a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/util/RestUtils.java b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/util/RestUtils.java index c3e5f840..7c2b8dc3 100644 --- a/api/src/main/java/ca/bc/gov/educ/api/dataconversion/util/RestUtils.java +++ b/api/src/main/java/ca/bc/gov/educ/api/dataconversion/util/RestUtils.java @@ -26,7 +26,7 @@ public class RestUtils { private final EducGradDataConversionApiConstants constants; - private ResponseObjCache responseObjCache; + private final ResponseObjCache responseObjCache; private final WebClient webClient; @@ -76,6 +76,20 @@ public List getStudentsByPen(String pen, String accessToken) { .retrieve().bodyToMono(responseType).block(); } + public List getStudentNotesByStudentId(String studentID, String accessToken) { + + log.debug("GET student Notes: {}", String.format(constants.getGradStudentNotesByStudentID(), studentID)); + final ParameterizedTypeReference> responseType = new ParameterizedTypeReference<>() { + }; + return this.webClient.get() + .uri(String.format(constants.getGradStudentNotesByStudentID(), studentID)) + .headers(h -> { + h.setBearerAuth(accessToken); + h.set(EducGradDataConversionApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()); + }) + .retrieve().bodyToMono(responseType).block(); + } + public OptionalProgram getOptionalProgram(String programCode, String specialProgramCode, String accessToken) { return this.webClient.get() .uri(constants.getGradOptionalProgramUrl(), uri -> uri.path("/{programCode}/{specialProgramCode}").build(programCode, specialProgramCode)) @@ -346,6 +360,17 @@ public TraxStudentNo saveTraxStudentNo(TraxStudentNo traxStudentNo, String acces .retrieve().bodyToMono(TraxStudentNo.class).block(); } + public TraxStudentNo updateTraxStudentNo(TraxStudentNo traxStudentNo, String accessToken) { + return webClient.put() + .uri(constants.getSaveTraxStudentNoUrl()) + .headers(h -> { + h.setBearerAuth(accessToken); + h.set(EducGradDataConversionApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()); + }) + .body(BodyInserters.fromValue(traxStudentNo)) + .retrieve().bodyToMono(TraxStudentNo.class).block(); + } + // Read GraduationStudentRecord - GET /student/studentid/{id}/algorithm @Retry(name = "rt-getStudentGradStatus", fallbackMethod = "rtGetStudentGradStatusFallback") public GraduationStudentRecord getStudentGradStatus(String studentID, String accessToken) { @@ -380,13 +405,21 @@ public GraduationStudentRecord updateStudentGradStatusByFields(OngoingUpdateRequ }).body(BodyInserters.fromValue(requestDTO)).retrieve().bodyToMono(GraduationStudentRecord.class).block(); } + public void removeAllStudentAchievements(UUID studentID, String accessToken) { + webClient.delete().uri(String.format(constants.getDeleteStudentAchievementsUrl(),studentID)) + .headers(h -> { + h.setBearerAuth(accessToken); + h.set(EducGradDataConversionApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()); + }).retrieve().bodyToMono(Void.class).block(); + } + // Remove All Student Related Data - DELETE /student/conv/studentid/{id} public void removeAllStudentRelatedData(UUID studentID, String accessToken) { webClient.delete().uri(String.format(constants.getSaveGraduationStudentRecord(),studentID)) .headers(h -> { h.setBearerAuth(accessToken); h.set(EducGradDataConversionApiConstants.CORRELATION_ID, ThreadLocalStateUtil.getCorrelationID()); - }).retrieve().onStatus(p -> p.value() == 404, error -> Mono.error(new Exception("Student Data Not Found"))).bodyToMono(Void.class).block(); + }).retrieve().bodyToMono(Void.class).block(); } // READ StudentOptionalProgram - GET /student/optionalprogram/studentid/{id} diff --git a/api/src/main/resources/application.yaml b/api/src/main/resources/application.yaml index 2d03219e..ff295f55 100644 --- a/api/src/main/resources/application.yaml +++ b/api/src/main/resources/application.yaml @@ -210,6 +210,8 @@ endpoint: remove-student-career-program: ${GRAD_STUDENT_API}api/v1/student/conv/studentcareerprogram/%s/%s read-student-career-programs: ${GRAD_STUDENT_API}api/v1/student/studentcareerprogram/studentid/%s save-grad-student-record-for-ongoing-updates: ${GRAD_STUDENT_API}api/v1/student/conv/ongoingupdate/gradstatus + get-student-by-pen: ${GRAD_STUDENT_API}api/v1/student/pen/%s + get-student-notes-by-studentID: ${GRAD_STUDENT_API}api/v1/student/studentnotes/studentid/%s grad-assessment-api: assessment-requirement: url: ${GRAD_ASSESSMENT_API}api/v1/assessment/requirement @@ -233,6 +235,10 @@ endpoint: url: ${GRAD_TRAX_API}api/v1/trax/common/course-restrictions get-course-requirements: url: ${GRAD_TRAX_API}api/v1/trax/common/course-requirements + grad-graduation-report-api: + delete-student-achievements: + url: ${GRAD_GRADUATION_REPORT_API}api/v1/graduationreports/studentachievement/%s + #Incremental Grad Update grad: diff --git a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/controller/StudentControllerTest.java b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/controller/StudentControllerTest.java new file mode 100644 index 00000000..8d8358d7 --- /dev/null +++ b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/controller/StudentControllerTest.java @@ -0,0 +1,44 @@ +package ca.bc.gov.educ.api.dataconversion.controller; + +import ca.bc.gov.educ.api.dataconversion.model.Student; +import ca.bc.gov.educ.api.dataconversion.service.student.StudentService; +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.junit.MockitoJUnitRunner; +import org.mockito.junit.jupiter.MockitoExtension; + +@RunWith(MockitoJUnitRunner.class) +@ExtendWith(MockitoExtension.class) +public class StudentControllerTest { + + @Mock + private StudentService studentService; + + @Test + public void testGetGradStudentByPenFromStudentAPI() { + + String pen = "123456789"; + String accessToken = "Bearer accesstoken"; + Student student = new Student(); + + Mockito.when(studentService.getStudentByPen(pen,accessToken.replaceAll("Bearer ", accessToken))).thenReturn(student); + studentService.getStudentByPen(pen,accessToken.replaceAll("Bearer ", accessToken)); + Mockito.verify(studentService).getStudentByPen(pen, accessToken.replaceAll("Bearer ", accessToken)); + } + + @Test + public void testCascadeDeleteStudent() { + + String pen = "123456789"; + String accessToken = "Bearer accesstoken"; + Student student = new Student(); + + Mockito.when(studentService.cascadeDeleteStudent(pen, accessToken.replaceAll("Bearer ", accessToken))).thenReturn(pen); + studentService.cascadeDeleteStudent(pen,accessToken.replaceAll("Bearer ", accessToken)); + Mockito.verify(studentService).cascadeDeleteStudent(pen, accessToken.replaceAll("Bearer ", accessToken)); + + } +} diff --git a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/NewStudentEventServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/NewStudentEventServiceTest.java index 55d0e4a5..08363b3f 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/NewStudentEventServiceTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/NewStudentEventServiceTest.java @@ -18,6 +18,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.WebClient; import java.util.Arrays; import java.util.Optional; @@ -45,6 +46,9 @@ public class NewStudentEventServiceTest { @MockBean RestUtils restUtils; + @MockBean + WebClient webClient; + // NATS @MockBean private NatsConnection natsConnection; diff --git a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentAssessmentUpdateEventServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentAssessmentUpdateEventServiceTest.java index 9b8c48d3..58fe1e1c 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentAssessmentUpdateEventServiceTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentAssessmentUpdateEventServiceTest.java @@ -18,6 +18,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.WebClient; import java.util.Optional; import java.util.UUID; @@ -44,6 +45,9 @@ public class StudentAssessmentUpdateEventServiceTest { @MockBean RestUtils restUtils; + @MockBean + WebClient webClient; + // NATS @MockBean private NatsConnection natsConnection; diff --git a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentCourseUpdateEventServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentCourseUpdateEventServiceTest.java index d65d1258..9f9319f5 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentCourseUpdateEventServiceTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentCourseUpdateEventServiceTest.java @@ -18,6 +18,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.WebClient; import java.util.Optional; import java.util.UUID; @@ -44,6 +45,9 @@ public class StudentCourseUpdateEventServiceTest { @MockBean RestUtils restUtils; + @MockBean + WebClient webClient; + // NATS @MockBean private NatsConnection natsConnection; diff --git a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentDemographicsUpdateEventServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentDemographicsUpdateEventServiceTest.java index 8c904691..9a858067 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentDemographicsUpdateEventServiceTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentDemographicsUpdateEventServiceTest.java @@ -18,6 +18,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.WebClient; import java.util.Optional; import java.util.UUID; @@ -44,6 +45,9 @@ public class StudentDemographicsUpdateEventServiceTest { @MockBean RestUtils restUtils; + @MockBean + WebClient webClient; + // NATS @MockBean private NatsConnection natsConnection; diff --git a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentFrenchImmersionEventServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentFrenchImmersionEventServiceTest.java index 699a86d8..db7962ac 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentFrenchImmersionEventServiceTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentFrenchImmersionEventServiceTest.java @@ -18,6 +18,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.WebClient; import java.util.Optional; import java.util.UUID; @@ -44,6 +45,9 @@ public class StudentFrenchImmersionEventServiceTest { @MockBean RestUtils restUtils; + @MockBean + WebClient webClient; + // NATS @MockBean private NatsConnection natsConnection; diff --git a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentGraduationUpdateEventServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentGraduationUpdateEventServiceTest.java index b99513c2..20a38f27 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentGraduationUpdateEventServiceTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentGraduationUpdateEventServiceTest.java @@ -18,6 +18,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.WebClient; import java.util.Optional; import java.util.UUID; @@ -44,6 +45,9 @@ public class StudentGraduationUpdateEventServiceTest { @MockBean RestUtils restUtils; + @MockBean + WebClient webClient; + // NATS @MockBean private NatsConnection natsConnection; diff --git a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentServiceTest.java new file mode 100644 index 00000000..bb47f062 --- /dev/null +++ b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentServiceTest.java @@ -0,0 +1,113 @@ +package ca.bc.gov.educ.api.dataconversion.service; + +import ca.bc.gov.educ.api.dataconversion.constant.EventStatus; +import ca.bc.gov.educ.api.dataconversion.constant.EventType; +import ca.bc.gov.educ.api.dataconversion.entity.Event; +import ca.bc.gov.educ.api.dataconversion.messaging.NatsConnection; +import ca.bc.gov.educ.api.dataconversion.messaging.jetstream.Subscriber; +import ca.bc.gov.educ.api.dataconversion.model.ConvGradStudent; +import ca.bc.gov.educ.api.dataconversion.model.ConversionStudentSummaryDTO; +import ca.bc.gov.educ.api.dataconversion.model.Student; +import ca.bc.gov.educ.api.dataconversion.process.StudentProcess; +import ca.bc.gov.educ.api.dataconversion.repository.EventRepository; +import ca.bc.gov.educ.api.dataconversion.service.student.NewStudentEventService; +import ca.bc.gov.educ.api.dataconversion.service.student.StudentService; +import ca.bc.gov.educ.api.dataconversion.util.EducGradDataConversionApiConstants; +import ca.bc.gov.educ.api.dataconversion.util.RestUtils; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.WebClient; + +import java.util.*; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatException; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.when; + +@RunWith(SpringRunner.class) +@SpringBootTest +@ActiveProfiles("test") +public class StudentServiceTest { + + @Autowired + StudentService studentService; + + @MockBean + RestUtils restUtils; + + @MockBean + WebClient webClient; + + // NATS + @MockBean + private NatsConnection natsConnection; + @MockBean + private Subscriber subscriber; + + @Test + public void testGetStudentByPen_given_PEN_returnsAPICallSuccess() { + String pen = "123456789"; + String accessToken = "Bearer accesstoken"; + + Student student = new Student(); + student.setPen(pen); + List penList = List.of(student); + + when(this.restUtils.getStudentsByPen(pen, accessToken)).thenReturn(penList); + studentService.getStudentByPen(pen, accessToken); + + assertThat(penList).isNotNull(); + } + + @Test + public void testGetStudentByPen_given_PEN_returnsNULL() { + String pen = "123456789"; + String accessToken = "Bearer accesstoken"; + + Student student = new Student(); + student.setPen(pen); + List penList = List.of(student); + + when(this.restUtils.getStudentsByPen(pen, accessToken)).thenReturn(new ArrayList()); + Student s = studentService.getStudentByPen(pen, accessToken); + + assertThat(s).isNull(); + } + + @Test(expected = Exception.class) + public void testGetStudentByPen_given_PEN_throws_exception() { + String pen = "123456789"; + String accessToken = "Bearer accesstoken"; + + Student student = new Student(); + student.setPen(pen); + List penList = List.of(student); + + when(this.restUtils.getStudentsByPen(pen, accessToken)).thenThrow(Exception.class); + Student s = studentService.getStudentByPen(pen, accessToken); + } + + @Test + public void testCascadeDeleteStudentBy_given_PEN_returnsAPICallSuccess() { + String pen = "123456789"; + String studentID = "0a614e84-7e27-1815-817e-fad384090090"; + String accessToken = "Bearer accesstoken"; + + Student student = new Student(); + student.setPen(pen); + student.setStudentID(studentID); + List penList = List.of(student); + + when(this.restUtils.getStudentsByPen(pen, accessToken)).thenReturn(penList); + studentService.cascadeDeleteStudent(pen, accessToken); + Mockito.verify(restUtils).removeAllStudentRelatedData(UUID.fromString(studentID), accessToken); + } +} diff --git a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentStatusUpdateEventServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentStatusUpdateEventServiceTest.java index d92072a1..8c944ab5 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentStatusUpdateEventServiceTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentStatusUpdateEventServiceTest.java @@ -18,6 +18,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.WebClient; import java.util.Arrays; import java.util.Optional; @@ -45,6 +46,9 @@ public class StudentStatusUpdateEventServiceTest { @MockBean RestUtils restUtils; + @MockBean + WebClient webClient; + // NATS @MockBean private NatsConnection natsConnection; diff --git a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentXProgramEventServiceTest.java b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentXProgramEventServiceTest.java index 7a3aa9b2..ce36a69f 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentXProgramEventServiceTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/service/StudentXProgramEventServiceTest.java @@ -18,6 +18,7 @@ import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.reactive.function.client.WebClient; import java.util.ArrayList; import java.util.Arrays; @@ -46,6 +47,9 @@ public class StudentXProgramEventServiceTest { @MockBean RestUtils restUtils; + @MockBean + WebClient webClient; + // NATS @MockBean private NatsConnection natsConnection; diff --git a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/util/RestUtilsTest.java b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/util/RestUtilsTest.java index dbd49fbe..310c84ed 100644 --- a/api/src/test/java/ca/bc/gov/educ/api/dataconversion/util/RestUtilsTest.java +++ b/api/src/test/java/ca/bc/gov/educ/api/dataconversion/util/RestUtilsTest.java @@ -368,4 +368,31 @@ public void testUpdateStudentFallbackMethod_givenException_shouldReturnNull(){ val result = this.restUtils.rtUpdateStudentGradStatusFallback(new HttpServerErrorException(HttpStatus.I_AM_A_TEAPOT)); assertThat(result).isNull(); } + + @Test + public void testGetStudentNotesByStudentId_returnsTrue_with_APICallSuccess() { + + final String studentID = "0a614e84-7e27-1815-817e-fad384090090"; + final String noteID = "0a614e84-7e27-1815-817e-fad384090090"; + String accessToken = "Bearer accesstoken"; + StudentNote note = new StudentNote(); + note.setStudentID(studentID); + note.setNote("Sample Note"); + note.setId(UUID.fromString(noteID)); + + when(this.webClient.get()).thenReturn(this.requestHeadersUriMock); + when(this.requestHeadersUriMock.uri(String.format(constants.getGradStudentNotesByStudentID(), studentID))) + .thenReturn(this.requestHeadersMock); + when(this.requestHeadersMock.headers(any(Consumer.class))).thenReturn(this.requestHeadersMock); + when(this.requestHeadersMock.retrieve()).thenReturn(this.responseMock); + + final ParameterizedTypeReference> responseType = new ParameterizedTypeReference<>() { + }; + when(this.responseMock.bodyToMono(responseType)).thenReturn(Mono.just(Arrays.asList(note))); + + val result = this.restUtils.getStudentNotesByStudentId(studentID, accessToken); + assertThat(result).isNotNull(); + assertThat(!result.isEmpty()).isTrue(); + assertThat(result.get(0).getStudentID()).isEqualTo(studentID); + } } diff --git a/api/src/test/resources/application.yaml b/api/src/test/resources/application.yaml index fee5931c..d36d6f55 100644 --- a/api/src/test/resources/application.yaml +++ b/api/src/test/resources/application.yaml @@ -137,6 +137,8 @@ endpoint: remove-student-career-program: https://educ-grad-student-api-77c02f-dev.apps.silver.devops.gov.bc.ca/api/v1/student/conv/studentcareerprogram/%s/%s read-student-career-programs: https://educ-grad-student-api-77c02f-dev.apps.silver.devops.gov.bc.ca/api/v1/student/studentcareerprogram/studentid/%s save-grad-student-record-for-ongoing-updates: https://educ-grad-student-api-77c02f-dev.apps.silver.devops.gov.bc.ca/api/v1/student/conv/ongoingupdate/gradstatus + get-student-by-pen: https://educ-grad-student-api-77c02f-dev.apps.silver.devops.gov.bc.ca/api/v1/student/pen/%s + get-student-notes-by-studentID: https://educ-grad-student-api-77c02f-dev.apps.silver.devops.gov.bc.ca/api/v1/student/studentnotes/studentid/%s grad-assessment-api: assessment-requirement: url: https://educ-grad-assessment-api-77c02f-dev.apps.silver.devops.gov.bc.ca/api/v1/assessment/requirement @@ -160,6 +162,9 @@ endpoint: url: https://educ-grad-trax-api-77c02f-dev.apps.silver.devops.gov.bc.ca/api/v1/trax/common/course-restrictions get-course-requirements: url: https://educ-grad-trax-api-77c02f-dev.apps.silver.devops.gov.bc.ca/api/v1/trax/common/course-requirements + grad-graduation-report-api: + delete-student-achievements: + url: https://educ-grad-graduation-report-api-77c02f-dev.apps.silver.devops.gov.bc.ca/api/v1/graduationreports/studentachievement/%s #Incremental Grad Update grad: diff --git a/tools/config/update-configmap.sh b/tools/config/update-configmap.sh index ca30ca8c..baa14221 100644 --- a/tools/config/update-configmap.sh +++ b/tools/config/update-configmap.sh @@ -62,6 +62,7 @@ oc create -n "$BUSINESS_NAMESPACE"-"$envValue" configmap "$APP_NAME"-config-map --from-literal=GRAD_PROGRAM_API="http://educ-grad-program-api.$GRAD_NAMESPACE-$envValue.svc.cluster.local:8080/" \ --from-literal=GRAD_STUDENT_API="http://educ-grad-student-api.$GRAD_NAMESPACE-$envValue.svc.cluster.local:8080/" \ --from-literal=GRAD_TRAX_API="http://educ-grad-trax-api.$GRAD_NAMESPACE-$envValue.svc.cluster.local:8080/" \ + --from-literal=GRAD_GRADUATION_REPORT_API="http://educ-grad-graduation-report-api.$GRAD_NAMESPACE-$envValue.svc.cluster.local:8080/" \ --from-literal=IDLE_TIMEOUT="400000" \ --from-literal=KEYCLOAK_TOKEN_URL="https://soam-$envValue.apps.silver.devops.gov.bc.ca/" \ --from-literal=MAXIMUM_POOL_SIZE="40" \